]> git.pld-linux.org Git - packages/exim.git/commitdiff
- rel 3; use exim-4_86+fixes branch instead of master auto/th/exim-4.86-3
authorArkadiusz Miśkiewicz <arekm@maven.pl>
Mon, 5 Oct 2015 20:01:14 +0000 (22:01 +0200)
committerArkadiusz Miśkiewicz <arekm@maven.pl>
Mon, 5 Oct 2015 20:01:14 +0000 (22:01 +0200)
exim-git.patch
exim.spec

index 7b70b89a298ea51658c625b1b4740076e5905ea9..327fff54b16a598030fab282d05c4b48ccf20842 100644 (file)
-commit aa7751be078fe9a20efa2cf2a8856fadb98f4178
-Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date:   Sun Jun 28 15:14:02 2015 +0100
-
-    Compiler quietening
-
-diff --git a/src/src/dns.c b/src/src/dns.c
-index 64958d9..a239bec 100644
---- a/src/src/dns.c
-+++ b/src/src/dns.c
-@@ -40,7 +40,6 @@ fakens_search(const uschar *domain, int type, uschar *answerptr, int size)
- {
- int len = Ustrlen(domain);
- int asize = size;                  /* Locally modified */
--uschar *endname;
- uschar name[256];
- uschar utilname[256];
- uschar *aptr = answerptr;          /* Locally modified */
-@@ -51,7 +50,6 @@ struct stat statbuf;
- if (domain[len - 1] == '.') len--;
- Ustrncpy(name, domain, len);
- name[len] = 0;
--endname = name + len;
- /* Look for the fakens utility, and if it exists, call it. */
-@@ -86,7 +84,7 @@ if (stat(CS utilname, &statbuf) >= 0)
-     asize -= rc;      /* may need to be passed on to res_search(). */
-     }
--  /* If we ran out of output buffer before exhasting the return,
-+  /* If we ran out of output buffer before exhausting the return,
-   carry on reading and counting it. */
-   if (asize == 0)
-diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
-index cf0a5d6..711ee86 100644
---- a/src/src/smtp_in.c
-+++ b/src/src/smtp_in.c
-@@ -3293,7 +3293,7 @@ while (done <= 0)
-   pid_t pid;
-   int start, end, sender_domain, recipient_domain;
-   int ptr, size, rc;
--  int c, i;
-+  int c;
-   auth_instance *au;
-   uschar *orcpt = NULL;
-   int flags;
-diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
-index e1dcd77..d8377fd 100644
---- a/src/src/tls-openssl.c
-+++ b/src/src/tls-openssl.c
-@@ -708,7 +708,7 @@ if (  (nid = OBJ_sn2nid       (CCS exp_curve)) == NID_undef
- if (!(ecdh = EC_KEY_new_by_curve_name(nid)))
-   {
--  tls_error("Unable to create ec curve", host, NULL);
-+  tls_error(US"Unable to create ec curve", host, NULL);
-   return FALSE;
-   }
-diff --git a/src/src/utf8.c b/src/src/utf8.c
-index a0ec003..8a7cf38 100644
---- a/src/src/utf8.c
-+++ b/src/src/utf8.c
-@@ -127,7 +127,7 @@ if ((rc = punycode_decode(p_len, CCS alabel+4, &p_len, p, NULL)) != PUNYCODE_SUC
-   return NULL;
-   }
--s = stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len);
-+s = US stringprep_ucs4_to_utf8(p, p_len, NULL, &p_len);
- res = string_copyn(s, p_len);
- free(s);
- return res;
-
-commit 5b881b5a8e0d7bc540f4b63cc9559d2cb1775965
+commit da1aecfce80fba32b9abb328cf78088d95b7700a
 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
-Date:   Thu Jul 30 09:43:51 2015 +0200
-
-    Docs: Add a note about the maximum spam bar length
-
-diff --git a/src/src/spam.h b/src/src/spam.h
-index 05ab655..2fe7380 100644
---- a/src/src/spam.h
-+++ b/src/src/spam.h
-@@ -12,7 +12,8 @@
- /* timeout for reading and writing spamd */
- #define SPAMD_TIMEOUT 120
--/* maximum length of the spam bar */
-+/* maximum length of the spam bar, please update the
-+ * spec, the max length is mentioned there */
- #define MAX_SPAM_BAR_CHARS 50
- /* SHUT_WR seems to be undefined on Unixware ? */
-
-commit 98716abe2b636d275e866f3ad6374cb70bf6e504
-Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date:   Sun Aug 2 13:44:31 2015 +0100
+Date:   Wed Aug 19 15:22:41 2015 +0200
 
-    Testsuite: Add testcase for OCSP-nonaware client, to supporting server.  Bug 1664
+    Fix post-transport-crash.
+    
+    The crash probably was introduced in a39bd74d3e94 and
+    needs 'split_spool_directory=yes' to expose.
     
-    The logfile here is for (I hope) the passing case, though the fixed GnuTLS library
-    is not yet available.  Also due to the bug, client-gnutls is not usable for the
-    test; client-openssl must be used - meaning that a GnuTLS-only system cannot run
-    the testcase:
+    Thanks to Wolfgang Breyha, who found the same fix.
     
-      OCSP-GnuTLS/5650 OCSP stapling, server
-      ** Command 15 ("client-ssl", starting at line 98)
-      ** Return code 127 (expected 0)
+    (cherry picked from commit 6b51df8340eacc95e3def9a4376506610e91996c)
 
-diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
-index 61ed0e8..e2ac17c 100644
---- a/src/src/tls-gnu.c
-+++ b/src/src/tls-gnu.c
-@@ -842,7 +842,7 @@ if (  !host        /* server */
-   gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
-     server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
--  DEBUG(D_tls) debug_printf("Set OCSP response file %s\n", &state->exp_tls_ocsp_file);
-+  DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file);
-   }
- #endif
+diff --git a/src/src/transport.c b/src/src/transport.c
+index fa6f869..a6ad3ed 100644
+--- a/src/src/transport.c
++++ b/src/src/transport.c
+@@ -1752,7 +1752,7 @@ while (1)
+     {
+     if (split_spool_directory)
+       sprintf(CS spool_file, "%s%c/%s-D",
+-                    spool_dir, new_message_id[5], msgq[i].message_id);
++                    spool_dir, msgq[i].message_id[5], msgq[i].message_id);
+     else
+       sprintf(CS spool_file, "%s%s-D", spool_dir, msgq[i].message_id);
  
 
-commit 9196d5bf543d75a81ae0825a352920d27241c325
-Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date:   Sun Aug 2 13:53:15 2015 +0100
+commit d7168d8b112d3ba642a25ed04de36fb76d4a847d
+Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
+Date:   Thu Aug 20 13:58:06 2015 +0200
 
-    GnuTLS: avoid using OCSP on buggy library versions.  Bug 1664
+    Fix post-transport-crash: safeguard for missing spool BUG 1671
+    
+    Based on a proposal from Wolfgang Breyha.
+    
+    (cherry picked from commit dadff1d47e54962b0fdf98e8ce5cef42b6cb7fb5)
 
-diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
-index e2ac17c..8aabc5c 100644
---- a/src/src/tls-gnu.c
-+++ b/src/src/tls-gnu.c
-@@ -176,6 +176,8 @@ static const char * const exim_default_gnutls_priority = "NORMAL";
- static BOOL exim_gnutls_base_init_done = FALSE;
+diff --git a/src/src/deliver.c b/src/src/deliver.c
+index 78f8f4b..4154ff7 100644
+--- a/src/src/deliver.c
++++ b/src/src/deliver.c
+@@ -9,6 +9,7 @@
  
-+static BOOL gnutls_buggy_ocsp = FALSE;
-+
  
- /* ------------------------------------------------------------------------ */
- /* macros */
-@@ -831,18 +833,25 @@ if (  !host      /* server */
-    && tls_ocsp_file
-    )
-   {
--  if (!expand_check(tls_ocsp_file, US"tls_ocsp_file",
--      &state->exp_tls_ocsp_file))
--    return DEFER;
-+  if (gnutls_buggy_ocsp)
-+    {
-+    DEBUG(D_tls) debug_printf("GnuTLS library is buggy for OCSP; avoiding\n");
-+    }
-+  else
-+    {
-+    if (!expand_check(tls_ocsp_file, US"tls_ocsp_file",
-+        &state->exp_tls_ocsp_file))
-+      return DEFER;
--  /* Use the full callback method for stapling just to get observability.
--  More efficient would be to read the file once only, if it never changed
--  (due to SNI). Would need restart on file update, or watch datestamp.  */
-+    /* Use the full callback method for stapling just to get observability.
-+    More efficient would be to read the file once only, if it never changed
-+    (due to SNI). Would need restart on file update, or watch datestamp.  */
--  gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
--    server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
-+    gnutls_certificate_set_ocsp_status_request_function(state->x509_cred,
-+      server_ocsp_stapling_cb, state->exp_tls_ocsp_file);
--  DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file);
-+    DEBUG(D_tls) debug_printf("OCSP response file = %s\n", state->exp_tls_ocsp_file);
-+    }
-   }
- #endif
+ #include "exim.h"
++#include <assert.h>
  
-@@ -1011,6 +1020,35 @@ return OK;
- *            Initialize for GnuTLS               *
- *************************************************/
  
+ /* Data block for keeping track of subprocesses for parallel remote
+@@ -7904,17 +7905,36 @@ if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
+ uschar *
+ deliver_get_sender_address (uschar * id)
+ {
++int rc;
++uschar * new_sender_address,
++       * save_sender_address;
 +
-+static BOOL
-+tls_is_buggy_ocsp(void)
-+{
-+const uschar * s;
-+uschar maj, mid, mic;
+ if (!spool_open_datafile(id))
+   return NULL;
++/* Save and restore the global sender_address.  I'm not sure if we should
++not save/restore all the other global variables too, because
++spool_read_header() may change all of them. But OTOH, when this
++deliver_get_sender_address() gets called, the current message is done
++already and nobody needs the globals anymore. (HS12, 2015-08-21) */
 +
-+s = CUS gnutls_check_version(NULL);
-+maj = atoi(CCS s);
-+if (maj == 3)
-+  {
-+  while (*s && *s != '.') s++;
-+  mid = atoi(CCS ++s);
-+  if (mid <= 2)
-+    return TRUE;
-+  else if (mid >= 5)
-+    return FALSE;
-+  else
-+    {
-+    while (*s && *s != '.') s++;
-+    mic = atoi(CCS ++s);
-+    return mic <= (mid == 3 ? 16 : 3);
-+    }
-+  }
-+return FALSE;
-+}
+ sprintf(CS spoolname, "%s-H", id);
+-if (spool_read_header(spoolname, TRUE, TRUE) != spool_read_OK)
++save_sender_address = sender_address;
 +
++rc = spool_read_header(spoolname, TRUE, TRUE);
 +
++new_sender_address = sender_address;
++sender_address = save_sender_address;
 +
- /* Called from both server and client code. In the case of a server, errors
- before actual TLS negotiation return DEFER.
-@@ -1074,6 +1112,9 @@ if (!exim_gnutls_base_init_done)
-     }
- #endif
++if (rc != spool_read_OK)
+   return NULL;
  
-+  if ((gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
-+    log_write(0, LOG_MAIN, "OCSP unusable with this GnuTLS library version");
++assert(new_sender_address);
 +
  exim_gnutls_base_init_done = TRUE;
-   }
(void)close(deliver_datafile);
+ deliver_datafile = -1;
  
-
-commit 63f0dbe0ca0aba7be3bc8807f45c8703a1cfafe1
-Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date:   Thu Aug 6 21:38:33 2015 +0100
-
-    OpenSSL: fix complile on pre-EC-capable library versions
-
-diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
-index d8377fd..b1dccb8 100644
---- a/src/src/tls-openssl.c
-+++ b/src/src/tls-openssl.c
-@@ -659,15 +659,15 @@ Returns:    TRUE if OK (nothing to set up, or setup worked)
+-return sender_address;
++return new_sender_address;
+ }
+ /* vi: aw ai sw=2
+diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
+index a952413..5ac5533 100644
+--- a/src/src/transports/smtp.c
++++ b/src/src/transports/smtp.c
+@@ -1274,14 +1274,19 @@ we will veto this new message.  */
  static BOOL
init_ecdh(SSL_CTX * sctx, host_item * host)
smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare)
  {
-+#ifdef OPENSSL_NO_ECDH
-+return TRUE;
-+#else
+-uschar * save_sender_address = sender_address;
+-uschar * current_local_identity =
 +
- EC_KEY * ecdh;
- uschar * exp_curve;
- int nid;
- BOOL rv;
--#ifdef OPENSSL_NO_ECDH
--return TRUE;
--#else
--
- if (host)     /* No ECDH setup for clients, only for servers */
-   return TRUE;
-
-commit 755762fd4c420cabbcba4a9c79947e926fa82219
-Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
-Date:   Sun Aug 9 23:29:44 2015 +0200
-
-    Compiler quietening
-
-diff --git a/src/src/malware.c b/src/src/malware.c
-index 141c6ea..96af1e8 100644
---- a/src/src/malware.c
-+++ b/src/src/malware.c
-@@ -886,7 +886,7 @@ if (!malware_ok)
-             string_sprintf("unable to read result (%s)", strerror(errno)),
-             sock);
--        for (p[bread] = '\0'; q = Ustrchr(p, '\n'); p = q+1)
-+        for (p[bread] = '\0'; (q = Ustrchr(p, '\n')); p = q+1)
-           {
-           *q = '\0';
-@@ -1880,6 +1880,9 @@ if (!malware_ok)
-             /* here for any unexpected response from the scanner */
-             goto endloop;
++uschar * message_local_identity,
++       * current_local_identity,
++       * new_sender_address;
 +
-+          case AVA_DONE:      log_write(0, LOG_PANIC, "%s:%d:%s: should not happen",
-+                          __FILE__, __LINE__, __FUNCTION__);
-           }
-         }
-       }
-diff --git a/src/src/spam.c b/src/src/spam.c
-index ca8d207..457aa3a 100644
---- a/src/src/spam.c
-+++ b/src/src/spam.c
-@@ -297,7 +297,7 @@ start = time(NULL);
-     sd = (spamd_address_container *)store_get(sizeof(spamd_address_container));
-     for (sublist = address, args = 0, spamd_param_init(sd);
--       s = string_nextinlist(&sublist, &sublist_sep, NULL, 0);
-+       (s = string_nextinlist(&sublist, &sublist_sep, NULL, 0));
-        args++
-        )
-       {
-diff --git a/src/src/tlscert-openssl.c b/src/src/tlscert-openssl.c
-index 72808a7..19db040 100644
---- a/src/src/tlscert-openssl.c
-+++ b/src/src/tlscert-openssl.c
-@@ -127,7 +127,7 @@ else
-   {
-   struct tm tm;
-   struct tm * tm_p = &tm;
--  BOOL mod_tz;
-+  BOOL mod_tz = TRUE;
-   uschar * tz = to_tz(US"GMT0");    /* need to call strptime with baseline TZ */
-   /* Parse OpenSSL ASN1_TIME_print output.  A shame there seems to
-@@ -164,7 +164,7 @@ else
-       }
-     }
++current_local_identity =
+   smtp_local_identity(s_compare->current_sender_address, s_compare->tblock);
+-uschar * new_sender_address = deliver_get_sender_address(message_id);
+-uschar * message_local_identity =
+-  smtp_local_identity(new_sender_address, s_compare->tblock);
  
--  if (mod_tz);
-+  if (mod_tz)
-     restore_tz(tz);
-   }
- BIO_free(bp);
-
-commit 4bd6107db73131e1b48f1902833fd7c637c08bda
-Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
-Date:   Mon Aug 10 00:39:36 2015 +0200
-
-    Really re-select() when interrupted.
-
-diff --git a/src/src/ip.c b/src/src/ip.c
-index ead7299..cb54f16 100644
---- a/src/src/ip.c
-+++ b/src/src/ip.c
-@@ -499,7 +499,7 @@ do
+-sender_address = save_sender_address;
++if (!(new_sender_address = deliver_get_sender_address(message_id)))
++    return 0;
++
++message_local_identity =
++  smtp_local_identity(new_sender_address, s_compare->tblock);
  
-   /* If the socket is ready, break out of the loop. */
-   }
--while (!FD_ISSET(fd, &select_inset));
-+while (rc < 0 || !FD_ISSET(fd, &select_inset));
- return TRUE;
+ return Ustrcmp(current_local_identity, message_local_identity) == 0;
  }
-
-commit 505d976aa23de4294751162dee6466e335c96fbf
-Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
-Date:   Tue Aug 11 09:13:11 2015 +0200
-
-    Build: Make test_{os,parse,dbfn,string} work
 
-diff --git a/src/OS/Makefile-Base b/src/OS/Makefile-Base
-index 1d5a5f6..95110e6 100644
---- a/src/OS/Makefile-Base
-+++ b/src/OS/Makefile-Base
-@@ -743,11 +743,11 @@ sa-os.o:         $(HDRS) os.c
- # These are the test targets themselves
- test_dbfn:   config.h dbfn.c dummies.o sa-globals.o sa-os.o store.o \
--             string.o tod.o version.o
-+             string.o tod.o version.o utf8.o
-       $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE dbfn.c
-       $(LNCC) -o test_dbfn $(LFLAGS) dbfn.o \
-         dummies.o sa-globals.o sa-os.o store.o string.o \
--        tod.o version.o $(LIBS) $(DBMLIB)
-+        tod.o version.o utf8.o $(LIBS) $(DBMLIB) $(LDFLAGS)
-       rm -f dbfn.o
- test_host:   config.h child.c host.c dns.c dummies.c sa-globals.o os.o \
-@@ -761,23 +761,24 @@ test_host:   config.h child.c host.c dns.c dummies.c sa-globals.o os.o \
-         tod.o tree.o $(LIBS) $(LIBRESOLV)
-       rm -f child.o dummies.o host.o dns.o
--test_os:     os.h os.c dummies.o sa-globals.o store.o string.o tod.o
-+test_os:     os.h os.c dummies.o sa-globals.o store.o string.o tod.o utf8.o
-       $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE os.c
-       $(LNCC) -o test_os $(LFLAGS) os.o dummies.o \
--        sa-globals.o store.o string.o tod.o $(LIBS)
-+        sa-globals.o store.o string.o tod.o utf8.o $(LIBS) $(LDFLAGS)
-       rm -f os.o
- test_parse:  config.h parse.c dummies.o sa-globals.o \
--           store.o string.o tod.o version.o
-+           store.o string.o tod.o version.o utf8.o
-       $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE parse.c
-       $(LNCC) -o test_parse $(LFLAGS) parse.o \
--        dummies.o sa-globals.o store.o string.o tod.o version.o
-+        dummies.o sa-globals.o store.o string.o tod.o version.o \
-+        utf8.o $(LDFLAGS)
-       rm -f parse.o
--test_string: config.h string.c dummies.o sa-globals.o store.o tod.o
-+test_string: config.h string.c dummies.o sa-globals.o store.o tod.o utf8.o
-       $(CC) -c $(CFLAGS) $(INCLUDE) -DSTAND_ALONE string.c
-       $(LNCC) -o test_string $(LFLAGS) -DSTAND_ALONE string.o \
--        dummies.o sa-globals.o store.o tod.o $(LIBS)
-+        dummies.o sa-globals.o store.o tod.o utf8.o $(LIBS) $(LDFLAGS)
-       rm -f string.o
- # End
-diff --git a/src/src/host.c b/src/src/host.c
-index 94126d4..31c2bbf 100644
---- a/src/src/host.c
-+++ b/src/src/host.c
-@@ -3212,7 +3212,7 @@ while (Ufgets(buffer, 256, stdin) != NULL)
-   else
-     {
-     int flags = whichrrs;
--    dnssec d;
-+    dnssec_domains d;
-     h.name = buffer;
-     h.next = NULL;
-
-commit 2ef7ed082481b2dccd3c2e0eae849b24bf0b172a
+commit 9e1acebc3fbb288da804ab9031c7c448dffd841a
 Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
 Date:   Tue Aug 11 17:36:29 2015 +0200
 
@@ -417,9 +125,11 @@ Date:   Tue Aug 11 17:36:29 2015 +0200
     
     If the address containes spaces, the option processing
     was confused.
+    
+    (cherry picked from commit 2ef7ed082481b2dccd3c2e0eae849b24bf0b172a)
 
 diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
-index 711ee86..effc636 100644
+index cf0a5d6..980d54b 100644
 --- a/src/src/smtp_in.c
 +++ b/src/src/smtp_in.c
 @@ -9,6 +9,7 @@
@@ -488,3210 +198,954 @@ index 711ee86..effc636 100644
        /* Break out of for loop if switch() had bad argument or
           when start of the email address is reached */
 
-commit 4fb7df6d044a39151e72346ac0d67ac09686f704
+commit 22c82c48aa5d23c49d38e3581810ea54587df61b
 Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date:   Tue Aug 11 22:54:53 2015 +0100
+Date:   Tue Aug 25 10:36:27 2015 +0100
 
-    GnuTLS: avoid whining about OCSP when not requested by config
+    Close logs after daemon-process exceptional write.  Bug 728
+    
+    (cherry picked from commit c8899c20aa08c9ae6a4c291aad23ba90512bebe4)
 
-diff --git a/src/src/tls-gnu.c b/src/src/tls-gnu.c
-index 8aabc5c..fe18094 100644
---- a/src/src/tls-gnu.c
-+++ b/src/src/tls-gnu.c
-@@ -176,7 +176,9 @@ static const char * const exim_default_gnutls_priority = "NORMAL";
- static BOOL exim_gnutls_base_init_done = FALSE;
-+#ifndef DISABLE_OCSP
- static BOOL gnutls_buggy_ocsp = FALSE;
-+#endif
- /* ------------------------------------------------------------------------ */
-@@ -1021,6 +1023,8 @@ return OK;
- *************************************************/
+diff --git a/src/src/daemon.c b/src/src/daemon.c
+index 894a96f..a7a49f0 100644
+--- a/src/src/daemon.c
++++ b/src/src/daemon.c
+@@ -735,6 +735,7 @@ else (void)close(dup_accept_socket);
+ /* Release any store used in this process, including the store used for holding
+ the incoming host address and an expanded active_hostname. */
  
++log_close_all();
+ store_reset(reset_point);
+ sender_host_address = NULL;
+ }
+
+commit 4d3b8805796ffa39889060d9f216bfaf7391c542
+Author: Jeremy Harris <jgh146exb@wizmail.org>
+Date:   Thu Sep 17 13:35:16 2015 +0100
+
+    DNS: time-limit cached returns, using TTL.  Bug 1395
+    
+    This can matter for fast-changing data such as DNSBLs.
+    
+    (cherry picked from commit 14b3c5bc64a16df07583fe4b5ef2e0129d063893)
+    
+    DNS: avoid overflow in cache TTL for negative entries.  Bug 1395
+    
+    (cherry picked from commit e162fc9757d4b8cb41aca74214e968622d6c3dee)
+
+diff --git a/src/src/dns.c b/src/src/dns.c
+index 64958d9..abed126 100644
+--- a/src/src/dns.c
++++ b/src/src/dns.c
+@@ -390,7 +390,8 @@ from the following bytes. */
+ dnss->aptr += namelen;
+ GETSHORT(dnss->srr.type, dnss->aptr); /* Record type */
+-dnss->aptr += 6;                      /* Don't want class or TTL */
++dnss->aptr += 2;                      /* Don't want class */
++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 */
+ dnss->aptr += dnss->srr.size;         /* Advance to next RR */
+diff --git a/src/src/lookupapi.h b/src/src/lookupapi.h
+index cdd1c85..03de8f6 100644
+--- a/src/src/lookupapi.h
++++ b/src/src/lookupapi.h
+@@ -34,7 +34,7 @@ typedef struct lookup_info {
+     int,                          /* length of key or query */
+     uschar **,                    /* for returning answer */
+     uschar **,                    /* for error message */
+-    BOOL *);                      /* to request cache cleanup */
++    uint *);                      /* cache TTL, sconds */
+   void (*close)(                  /* close function */
+     void *);                      /* handle */
+   void (*tidy)(void);             /* tidy function */
+@@ -46,9 +46,10 @@ typedef struct lookup_info {
+ } lookup_info;
+ /* This magic number is used by the following lookup_module_info structure
+-   for checking API compatibility. It's equivalent to the string"LMM2" */
+-#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4d32
++   for checking API compatibility. It used to be equivalent to the string"LMM3" */
++#define LOOKUP_MODULE_INFO_MAGIC 0x4c4d4933
+ /* Version 2 adds: version_report */
++/* Version 3 change: non/cache becomes TTL in seconds */
+ typedef struct lookup_module_info {
+   uint magic;
+diff --git a/src/src/lookups/README b/src/src/lookups/README
+index 98905dc..31fea64 100644
+--- a/src/src/lookups/README
++++ b/src/src/lookups/README
+@@ -122,12 +122,15 @@ DEFER. The arguments are:
+   uschar **errmsg     where to put an error message on failure;
+                       this is initially set to "", and should be left
+                       as that for a standard "entry not found" error
+-  BOOL *do_cache      the lookup should set this to FALSE when it changes data.
+-                      This is TRUE by default. When set to FALSE the cache tree
++  uint *do_cache      the lookup should set this to 0 when it changes data.
++                      This is MAXINT by default. When set to 0 the cache tree
+                       of the current search handle will be cleaned and the
+                       current result will NOT be cached. Currently the mysql
+                       and pgsql lookups use this when UPDATE/INSERT queries are
+                       executed.
++                      If set to a nonzero number of seconds, the cached value
++                      becomes unusable after this time. Currently the dnsdb
++                      lookup uses this to support the TTL value.
+ Even though the key is zero-terminated, the length is passed because in the
+ common case it has been computed already and is often needed.
+diff --git a/src/src/lookups/cdb.c b/src/src/lookups/cdb.c
+index ea017de..ba925dc 100644
+--- a/src/src/lookups/cdb.c
++++ b/src/src/lookups/cdb.c
+@@ -279,7 +279,7 @@ cdb_find(void *handle,
+         int  key_len,
+         uschar **result,
+         uschar **errmsg,
+-        BOOL *do_cache)
++        uint *do_cache)
+ {
+   struct cdb_state * cdbp = handle;
+   uint32 item_key_len,
+diff --git a/src/src/lookups/dbmdb.c b/src/src/lookups/dbmdb.c
+index 03248e4..b8c42d5 100644
+--- a/src/src/lookups/dbmdb.c
++++ b/src/src/lookups/dbmdb.c
+@@ -87,7 +87,7 @@ the keylength in order to include the terminating zero. */
+ static int
+ dbmdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ EXIM_DB *d = (EXIM_DB *)handle;
+ EXIM_DATUM key, data;
+@@ -120,7 +120,7 @@ return FAIL;
  
-+#ifndef DISABLE_OCSP
-+
- static BOOL
- tls_is_buggy_ocsp(void)
+ int
+ static dbmnz_find(void *handle, uschar *filename, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
  {
-@@ -1047,6 +1051,7 @@ if (maj == 3)
- return FALSE;
- }
+ return dbmdb_find(handle, filename, keystring, length-1, result, errmsg,
+   do_cache);
+@@ -140,7 +140,7 @@ return dbmdb_find(handle, filename, keystring, length-1, result, errmsg,
+ static int
+ dbmjz_find(void *handle, uschar *filename, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ uschar *key_item, *key_buffer, *key_p;
+ const uschar *key_elems = keystring;
+diff --git a/src/src/lookups/dnsdb.c b/src/src/lookups/dnsdb.c
+index e3de279..70e6c8c 100644
+--- a/src/src/lookups/dnsdb.c
++++ b/src/src/lookups/dnsdb.c
+@@ -131,7 +131,7 @@ separator, as always, is colon. */
+ static int
+ dnsdb_find(void *handle, uschar *filename, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ int rc;
+ int size = 256;
+@@ -388,6 +388,9 @@ while ((domain = string_nextinlist(&keystring, &sep, NULL, 0)))
+       {
+       if (rr->type != searchtype) continue;
  
-+#endif
++      if (*do_cache > rr->ttl)
++      *do_cache = rr->ttl;
++
+       if (type == T_A || type == T_AAAA || type == T_ADDRESSES)
+         {
+         dns_address *da;
+diff --git a/src/src/lookups/dsearch.c b/src/src/lookups/dsearch.c
+index f8c592a..9f7dd8d 100644
+--- a/src/src/lookups/dsearch.c
++++ b/src/src/lookups/dsearch.c
+@@ -67,7 +67,7 @@ for us. */
  
+ int
+ static dsearch_find(void *handle, uschar *dirname, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ struct stat statbuf;
+ int save_errno;
+diff --git a/src/src/lookups/ibase.c b/src/src/lookups/ibase.c
+index 23e1dea..7fd53d0 100644
+--- a/src/src/lookups/ibase.c
++++ b/src/src/lookups/ibase.c
+@@ -451,7 +451,7 @@ deferred with a retryable error. */
+ static int
+ ibase_find(void *handle, uschar * filename, uschar * query, int length,
+-           uschar ** result, uschar ** errmsg, BOOL *do_cache)
++           uschar ** result, uschar ** errmsg, uint *do_cache)
+ {
+     int sep = 0;
+     uschar *server;
+diff --git a/src/src/lookups/ldap.c b/src/src/lookups/ldap.c
+index a56eff3..b870df1 100644
+--- a/src/src/lookups/ldap.c
++++ b/src/src/lookups/ldap.c
+@@ -1339,7 +1339,7 @@ The handle and filename arguments are not used. */
+ static int
+ eldap_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ /* Keep picky compilers happy */
+ do_cache = do_cache;
+@@ -1348,7 +1348,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_SINGLE, result, errmsg));
+ static int
+ eldapm_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ /* Keep picky compilers happy */
+ do_cache = do_cache;
+@@ -1357,7 +1357,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_MULTIPLE, result, errmsg));
+ static int
+ eldapdn_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ /* Keep picky compilers happy */
+ do_cache = do_cache;
+@@ -1366,7 +1366,7 @@ return(control_ldap_search(ldap_url, SEARCH_LDAP_DN, result, errmsg));
  
- /* Called from both server and client code. In the case of a server, errors
-@@ -1112,8 +1117,10 @@ if (!exim_gnutls_base_init_done)
+ int
+ eldapauth_find(void *handle, uschar *filename, const uschar *ldap_url, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ /* Keep picky compilers happy */
+ do_cache = do_cache;
+diff --git a/src/src/lookups/lf_functions.h b/src/src/lookups/lf_functions.h
+index 73e9303..d2487d3 100644
+--- a/src/src/lookups/lf_functions.h
++++ b/src/src/lookups/lf_functions.h
+@@ -12,7 +12,7 @@ extern int     lf_check_file(int, uschar *, int, int, uid_t *, gid_t *,
+ extern uschar *lf_quote(uschar *, uschar *, int, uschar *, int *, int *);
+ extern int     lf_sqlperform(const uschar *, const uschar *, const uschar *,
+                const uschar *, uschar **,
+-                 uschar **, BOOL *, int(*)(const uschar *, uschar *, uschar **,
+-                 uschar **, BOOL *, BOOL *));
++                 uschar **, uint *, int(*)(const uschar *, uschar *, uschar **,
++                 uschar **, BOOL *, uint *));
+ /* End of lf_functions.h */
+diff --git a/src/src/lookups/lf_sqlperform.c b/src/src/lookups/lf_sqlperform.c
+index 2d7f326..6d4f7a7 100644
+--- a/src/src/lookups/lf_sqlperform.c
++++ b/src/src/lookups/lf_sqlperform.c
+@@ -27,7 +27,7 @@ Arguments:
+   query          the query
+   result         where to pass back the result
+   errmsg         where to pass back an error message
+-  do_cache       to be set FALSE if data is changed
++  do_cache       to be set zero if data is changed
+   func           the lookup function to call
+ Returns:         the return from the lookup function, or DEFER
+@@ -36,8 +36,8 @@ Returns:         the return from the lookup function, or DEFER
+ int
+ lf_sqlperform(const uschar *name, const uschar *optionname,
+   const uschar *optserverlist, const uschar *query,
+-  uschar **result, uschar **errmsg, BOOL *do_cache,
+-  int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, BOOL *))
++  uschar **result, uschar **errmsg, uint *do_cache,
++  int(*fn)(const uschar *, uschar *, uschar **, uschar **, BOOL *, uint *))
+ {
+ int sep, rc;
+ uschar *server;
+diff --git a/src/src/lookups/lsearch.c b/src/src/lookups/lsearch.c
+index 3883d4b..eb70a45 100644
+--- a/src/src/lookups/lsearch.c
++++ b/src/src/lookups/lsearch.c
+@@ -323,7 +323,7 @@ return FAIL;
+ static int
+ lsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ do_cache = do_cache;  /* Keep picky compilers happy */
+ return internal_lsearch_find(handle, filename, keystring, length, result,
+@@ -340,7 +340,7 @@ return internal_lsearch_find(handle, filename, keystring, length, result,
+ static int
+ wildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ do_cache = do_cache;  /* Keep picky compilers happy */
+ return internal_lsearch_find(handle, filename, keystring, length, result,
+@@ -357,7 +357,7 @@ return internal_lsearch_find(handle, filename, keystring, length, result,
+ static int
+ nwildlsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ do_cache = do_cache;  /* Keep picky compilers happy */
+ return internal_lsearch_find(handle, filename, keystring, length, result,
+@@ -375,7 +375,7 @@ return internal_lsearch_find(handle, filename, keystring, length, result,
+ static int
+ iplsearch_find(void *handle, uschar *filename, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ do_cache = do_cache;  /* Keep picky compilers happy */
+ if ((length == 1 && keystring[0] == '*') ||
+diff --git a/src/src/lookups/mysql.c b/src/src/lookups/mysql.c
+index 8dff86a..1ce8831 100644
+--- a/src/src/lookups/mysql.c
++++ b/src/src/lookups/mysql.c
+@@ -74,7 +74,7 @@ Arguments:
+   resultptr    where to store the result
+   errmsg       where to point an error message
+   defer_break  TRUE if no more servers are to be tried after DEFER
+-  do_cache     set false if data is changed
++  do_cache     set zero if data is changed
+ The server string is of the form "host/dbname/user/password". The host can be
+ host:port. This string is in a nextinlist temporary buffer, so can be
+@@ -85,7 +85,7 @@ Returns:       OK, FAIL, or DEFER
+ static int
+ perform_mysql_search(const uschar *query, uschar *server, uschar **resultptr,
+-  uschar **errmsg, BOOL *defer_break, BOOL *do_cache)
++  uschar **errmsg, BOOL *defer_break, uint *do_cache)
+ {
+ MYSQL *mysql_handle = NULL;        /* Keep compilers happy */
+ MYSQL_RES *mysql_result = NULL;
+@@ -225,7 +225,7 @@ can be detected by calling mysql_field_count(). If its result is zero, no data
+ was expected (this is all explained clearly in the MySQL manual). In this case,
+ we return the number of rows affected by the command. In this event, we do NOT
+ want to cache the result; also the whole cache for the handle must be cleaned
+-up. Setting do_cache FALSE requests this. */
++up. Setting do_cache zero requests this. */
+ if ((mysql_result = mysql_use_result(mysql_handle)) == NULL)
+   {
+@@ -233,7 +233,7 @@ if ((mysql_result = mysql_use_result(mysql_handle)) == NULL)
+     {
+     DEBUG(D_lookup) debug_printf("MYSQL: query was not one that returns data\n");
+     result = string_sprintf("%d", mysql_affected_rows(mysql_handle));
+-    *do_cache = FALSE;
++    *do_cache = 0;
+     goto MYSQL_EXIT;
      }
- #endif
+   *errmsg = string_sprintf("MYSQL: lookup result failed: %s\n",
+@@ -341,7 +341,7 @@ shared with other SQL lookups. */
  
--  if ((gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
-+#ifndef DISABLE_OCSP
-+  if (tls_ocsp_file && (gnutls_buggy_ocsp = tls_is_buggy_ocsp()))
-     log_write(0, LOG_MAIN, "OCSP unusable with this GnuTLS library version");
-+#endif
+ static int
+ mysql_find(void *handle, uschar *filename, const uschar *query, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ return lf_sqlperform(US"MySQL", US"mysql_servers", mysql_servers, query,
+   result, errmsg, do_cache, perform_mysql_search);
+diff --git a/src/src/lookups/nis.c b/src/src/lookups/nis.c
+index 7b012b1..1faa884 100644
+--- a/src/src/lookups/nis.c
++++ b/src/src/lookups/nis.c
+@@ -42,7 +42,7 @@ code. */
+ static int
+ nis_find(void *handle, uschar *filename, uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ int rc;
+ uschar *nis_data;
+@@ -68,7 +68,7 @@ return (rc == YPERR_KEY || rc == YPERR_MAP)? FAIL : DEFER;
  
-   exim_gnutls_base_init_done = TRUE;
-   }
-
-commit c528cec4dbfdb6e367a6ac0ed72e2e768a9c4392
-Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
-Date:   Wed Aug 12 23:45:44 2015 +0200
-
-    Adjust the timeout after interrupted select()
-
-diff --git a/src/src/ip.c b/src/src/ip.c
-index cb54f16..7991a58 100644
---- a/src/src/ip.c
-+++ b/src/src/ip.c
-@@ -451,8 +451,8 @@ BOOL
- fd_ready(int fd, int timeout)
+ static int
+ nis0_find(void *handle, uschar *filename, uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
  {
- fd_set select_inset;
--struct timeval tv;
- time_t start_recv = time(NULL);
-+int time_left = timeout;
  int rc;
+ uschar *nis_data;
+diff --git a/src/src/lookups/nisplus.c b/src/src/lookups/nisplus.c
+index 8895cee..a4a7a2d 100644
+--- a/src/src/lookups/nisplus.c
++++ b/src/src/lookups/nisplus.c
+@@ -43,7 +43,7 @@ equals sign. */
+ static int
+ nisplus_find(void *handle, uschar *filename, uschar *query, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ int i;
+ int ssize = 0;
+diff --git a/src/src/lookups/oracle.c b/src/src/lookups/oracle.c
+index 1f2520a..adb17b4 100644
+--- a/src/src/lookups/oracle.c
++++ b/src/src/lookups/oracle.c
+@@ -517,7 +517,7 @@ deferred with a retryable error. */
+ static int
+ oracle_find(void *handle, uschar *filename, uschar *query, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ int sep = 0;
+ uschar *server;
+diff --git a/src/src/lookups/passwd.c b/src/src/lookups/passwd.c
+index e726f3e..315677f 100644
+--- a/src/src/lookups/passwd.c
++++ b/src/src/lookups/passwd.c
+@@ -34,7 +34,7 @@ return (void *)(-1);     /* Just return something non-null */
+ static int
+ passwd_find(void *handle, uschar *filename, const uschar *keystring, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ struct passwd *pw;
+diff --git a/src/src/lookups/pgsql.c b/src/src/lookups/pgsql.c
+index c86ac23..4be3d98 100644
+--- a/src/src/lookups/pgsql.c
++++ b/src/src/lookups/pgsql.c
+@@ -119,7 +119,7 @@ Returns:       OK, FAIL, or DEFER
+ static int
+ perform_pgsql_search(const uschar *query, uschar *server, uschar **resultptr,
+-  uschar **errmsg, BOOL *defer_break, BOOL *do_cache)
++  uschar **errmsg, BOOL *defer_break, uint *do_cache)
+ {
+ PGconn *pg_conn = NULL;
+ PGresult *pg_result = NULL;
+@@ -290,10 +290,10 @@ else
+     /* The command was successful but did not return any data since it was
+      * not SELECT but either an INSERT, UPDATE or DELETE statement. Tell the
+      * high level code to not cache this query, and clean the current cache for
+-     * this handle by setting *do_cache FALSE. */
++     * this handle by setting *do_cache zero. */
+     result = string_copy(US PQcmdTuples(pg_result));
+     offset = Ustrlen(result);
+-    *do_cache = FALSE;
++    *do_cache = 0;
+     DEBUG(D_lookup) debug_printf("PGSQL: command does not return any data "
+       "but was successful. Rows affected: %s\n", result);
+@@ -399,7 +399,7 @@ shared with other SQL lookups. */
+ static int
+ pgsql_find(void *handle, uschar *filename, const uschar *query, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ return lf_sqlperform(US"PostgreSQL", US"pgsql_servers", pgsql_servers, query,
+   result, errmsg, do_cache, perform_pgsql_search);
+diff --git a/src/src/lookups/redis.c b/src/src/lookups/redis.c
+index ac4d0ec..18cd3a0 100644
+--- a/src/src/lookups/redis.c
++++ b/src/src/lookups/redis.c
+@@ -65,7 +65,7 @@ redis_tidy(void)
+  */
+ static int
+ perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
+-  uschar **errmsg, BOOL *defer_break, BOOL *do_cache)
++  uschar **errmsg, BOOL *defer_break, uint *do_cache)
+ {
+        redisContext *redis_handle = NULL;        /* Keep compilers happy */
+        redisReply *redis_reply = NULL;
+@@ -197,7 +197,7 @@ perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
+        case REDIS_REPLY_ERROR:
+                *errmsg = string_sprintf("REDIS: lookup result failed: %s\n", redis_reply->str);
+                *defer_break = FALSE;
+-               *do_cache = FALSE;
++               *do_cache = 0;
+                goto REDIS_EXIT;
+                /* NOTREACHED */
+@@ -205,7 +205,7 @@ perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
+        case REDIS_REPLY_NIL:
+                DEBUG(D_lookup) debug_printf("REDIS: query was not one that returned any data\n");
+                result = string_sprintf("");
+-               *do_cache = FALSE;
++               *do_cache = 0;
+                goto REDIS_EXIT;
+                /* NOTREACHED */
+@@ -304,7 +304,7 @@ perform_redis_search(uschar *command, uschar *server, uschar **resultptr,
+ static int
+ redis_find(void *handle __attribute__((unused)), uschar *filename __attribute__((unused)),
+-           uschar *command, int length, uschar **result, uschar **errmsg, BOOL *do_cache)
++           uschar *command, int length, uschar **result, uschar **errmsg, uint *do_cache)
+ {
+        return lf_sqlperform(US"Redis", US"redis_servers", redis_servers, command,
+          result, errmsg, do_cache, perform_redis_search);
+diff --git a/src/src/lookups/spf.c b/src/src/lookups/spf.c
+index 23ad2ad..2671fc9 100644
+--- a/src/src/lookups/spf.c
++++ b/src/src/lookups/spf.c
+@@ -31,7 +31,9 @@ static void dummy(int x) { dummy2(x-1); }
+ #include <spf2/spf_dns_resolv.h>
+ #include <spf2/spf_dns_cache.h>
+-static void *spf_open(uschar *filename, uschar **errmsg) {
++static void *
++spf_open(uschar *filename, uschar **errmsg)
++{
+   SPF_server_t *spf_server = NULL;
+   spf_server = SPF_server_new(SPF_DNS_CACHE, 0);
+   if (spf_server == NULL) {
+@@ -41,13 +43,17 @@ static void *spf_open(uschar *filename, uschar **errmsg) {
+   return (void *) spf_server;
+ }
+-static void spf_close(void *handle) {
++static void
++spf_close(void *handle)
++{
+   SPF_server_t *spf_server = handle;
+   if (spf_server) SPF_server_free(spf_server);
+ }
+-static int spf_find(void *handle, uschar *filename, uschar *keystring, int key_len,
+-             uschar **result, uschar **errmsg, BOOL *do_cache) {
++static int
++spf_find(void *handle, uschar *filename, uschar *keystring, int key_len,
++             uschar **result, uschar **errmsg, uint *do_cache)
++{
+   SPF_server_t *spf_server = handle;
+   SPF_request_t *spf_request = NULL;
+   SPF_response_t *spf_response = NULL;
+diff --git a/src/src/lookups/sqlite.c b/src/src/lookups/sqlite.c
+index bb92c8c..e2330f9 100644
+--- a/src/src/lookups/sqlite.c
++++ b/src/src/lookups/sqlite.c
+@@ -81,7 +81,7 @@ return 0;
+ static int
+ sqlite_find(void *handle, uschar *filename, const uschar *query, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ int ret;
+ struct strbuf res = { NULL, 0, 0 };
+@@ -93,7 +93,7 @@ if (ret != SQLITE_OK)
+   return FAIL;
+   }
  
- if (timeout <= 0)
-@@ -464,10 +464,9 @@ if (timeout <= 0)
+-if (res.string == NULL) *do_cache = FALSE;
++if (res.string == NULL) *do_cache = 0;
+ *result = res.string;
+ return OK;
+diff --git a/src/src/lookups/testdb.c b/src/src/lookups/testdb.c
+index c82fa7f..401f7c8 100644
+--- a/src/src/lookups/testdb.c
++++ b/src/src/lookups/testdb.c
+@@ -38,7 +38,7 @@ return (void *)(1);    /* Just return something non-null */
+ static int
+ testdb_find(void *handle, uschar *filename, const uschar *query, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ handle = handle;          /* Keep picky compilers happy */
+ filename = filename;
+@@ -57,7 +57,7 @@ if (Ustrcmp(query, "defer") == 0)
+   return DEFER;
+   }
  
- do
+-if (Ustrcmp(query, "nocache") == 0) *do_cache = FALSE;
++if (Ustrcmp(query, "nocache") == 0) *do_cache = 0;
+ *result = string_copy(query);
+ return OK;
+diff --git a/src/src/lookups/whoson.c b/src/src/lookups/whoson.c
+index 4166089..9ac5a3a 100644
+--- a/src/src/lookups/whoson.c
++++ b/src/src/lookups/whoson.c
+@@ -36,7 +36,7 @@ return (void *)(1);    /* Just return something non-null */
+ static int
+ whoson_find(void *handle, uschar *filename, uschar *query, int length,
+-  uschar **result, uschar **errmsg, BOOL *do_cache)
++  uschar **result, uschar **errmsg, uint *do_cache)
+ {
+ uschar buffer[80];
+ handle = handle;          /* Keep picky compilers happy */
+diff --git a/src/src/search.c b/src/src/search.c
+index a055291..cd522da 100644
+--- a/src/src/search.c
++++ b/src/src/search.c
+@@ -466,6 +466,7 @@ internal_search_find(void *handle, uschar *filename, uschar *keystring)
+ {
+ tree_node *t = (tree_node *)handle;
+ search_cache *c = (search_cache *)(t->data.ptr);
++expiring_data *e;
+ uschar *data = NULL;
+ int search_type = t->name[0] - '0';
+ int old_pool = store_pool;
+@@ -491,18 +492,27 @@ store_pool = POOL_SEARCH;
+ /* Look up the data for the key, unless it is already in the cache for this
+ file. No need to check c->item_cache for NULL, tree_search will do so. */
+-if ((t = tree_search(c->item_cache, keystring)) == NULL)
++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;
++  DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n",
++    keystring,
++    filename ? US"\n  in " : US"", filename ? filename : US"");
++  }
++else
    {
-+  struct timeval tv = { time_left, 0 };
-   FD_ZERO (&select_inset);
-   FD_SET (fd, &select_inset);
--  tv.tv_sec = timeout;
--  tv.tv_usec = 0;
-   /*DEBUG(D_transport) debug_printf("waiting for data on fd\n");*/
-   rc = select(fd + 1, (SELECT_ARG2_TYPE *)&select_inset, NULL, NULL, &tv);
-@@ -479,25 +478,24 @@ do
-   Aug 2004: Somebody set up a cron job that ran exiwhat every 2 minutes, making
-   the interrupt not at all rare. Since the timeout is typically more than 2
-   minutes, the effect was to block the timeout completely. To prevent this
--  happening again, we do an explicit time test. */
-+  happening again, we do an explicit time test and adjust the timeout
-+  accordingly */
-   if (rc < 0 && errno == EINTR)
-     {
-     DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
--    if (time(NULL) - start_recv < timeout) continue;
--    DEBUG(D_transport) debug_printf("total wait time exceeds timeout\n");
-+    /* Watch out, 'continue' jumps to the condition, not to the loops top */
-+    if (time_left = timeout - (time(NULL) - start_recv)) continue;
-     }
+-  BOOL do_cache = TRUE;
++  uint do_cache = UINT_MAX;
+   int keylength = Ustrlen(keystring);
  
--  /* Handle a timeout, and treat any other select error as a timeout, including
--  an EINTR when we have been in this loop for longer than timeout. */
--
-   if (rc <= 0)
+   DEBUG(D_lookup)
      {
-     errno = ETIMEDOUT;
-     return FALSE;
+-    if (filename != NULL)
+-      debug_printf("file lookup required for %s\n  in %s\n",
+-        keystring, filename);
+-    else
+-      debug_printf("database lookup required for %s\n", keystring);
++    if (t) debug_printf("cached data found but past valid time; ");
++    debug_printf("%s lookup required for %s%s%s\n",
++      filename ? US"file" : US"database",
++      keystring,
++      filename ? US"\n  in " : US"", filename ? filename : US"");
      }
  
--  /* If the socket is ready, break out of the loop. */
-+  /* Checking the FD_ISSET is not enough, if we're interrupted, the
-+  select_inset may still contain the 'input'. */
-   }
- while (rc < 0 || !FD_ISSET(fd, &select_inset));
- return TRUE;
-
-commit 85ff3cf9f3ab78c4dfa9f9ff34d27e6fe8f73c39
-Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
-Date:   Thu Aug 13 00:20:12 2015 +0200
-
-    Fix timeout adjustment in c528cec4
-
-diff --git a/src/src/ip.c b/src/src/ip.c
-index 7991a58..2d71705 100644
---- a/src/src/ip.c
-+++ b/src/src/ip.c
-@@ -455,7 +455,7 @@ time_t start_recv = time(NULL);
- int time_left = timeout;
- int rc;
--if (timeout <= 0)
-+if (time_left <= 0)
-   {
-   errno = ETIMEDOUT;
-   return FALSE;
-@@ -484,8 +484,10 @@ do
-   if (rc < 0 && errno == EINTR)
-     {
-     DEBUG(D_transport) debug_printf("EINTR while waiting for socket data\n");
-+
-     /* Watch out, 'continue' jumps to the condition, not to the loops top */
--    if (time_left = timeout - (time(NULL) - start_recv)) continue;
-+    time_left = timeout - (time(NULL) - start_recv);
-+    if (time_left > 0) continue;
-     }
-   if (rc <= 0)
-
-commit 6c6d6e483411af2c087ff258f4041d38eb65e775
-Author: Tony Finch <dot@dotat.at>
-Date:   Thu Aug 13 15:16:48 2015 +0100
-
-    Overhaul the debug_selector and log_selector machinery to support variable-length bit vectors. No functional change.
-
-diff --git a/src/src/acl.c b/src/src/acl.c
-index 91ee571..f2e0ef2 100644
---- a/src/src/acl.c
-+++ b/src/src/acl.c
-@@ -4287,7 +4287,7 @@ while (acl != NULL)
-     case ACL_WARN:
-     if (cond == OK)
-       acl_warn(where, *user_msgptr, *log_msgptr);
--    else if (cond == DEFER && (log_extra_selector & LX_acl_warn_skipped) != 0)
-+    else if (cond == DEFER && LOGGING(acl_warn_skipped))
-       log_write(0, LOG_MAIN, "%s Warning: ACL \"warn\" statement skipped: "
-         "condition test deferred%s%s", host_and_ident(TRUE),
-         (*log_msgptr == NULL)? US"" : US": ",
-diff --git a/src/src/daemon.c b/src/src/daemon.c
-index 894a96f..2d10387 100644
---- a/src/src/daemon.c
-+++ b/src/src/daemon.c
-@@ -145,7 +145,7 @@ int dup_accept_socket = -1;
- int max_for_this_host = 0;
- int wfsize = 0;
- int wfptr = 0;
--int use_log_write_selector = log_write_selector;
-+int save_log_selector = *log_selector;
- uschar *whofrom = NULL;
- void *reset_point = store_get(0);
-@@ -206,11 +206,11 @@ memory is reclaimed. */
- whofrom = string_append(whofrom, &wfsize, &wfptr, 3, "[", sender_host_address, "]");
--if ((log_extra_selector & LX_incoming_port) != 0)
-+if (LOGGING(incoming_port))
-   whofrom = string_append(whofrom, &wfsize, &wfptr, 2, ":", string_sprintf("%d",
-     sender_host_port));
--if ((log_extra_selector & LX_incoming_interface) != 0)
-+if (LOGGING(incoming_interface))
-   whofrom = string_append(whofrom, &wfsize, &wfptr, 4, " I=[",
-     interface_address, "]:", string_sprintf("%d", interface_port));
-@@ -338,11 +338,11 @@ the generalized logging code each time when the selector is false. If the
- selector is set, check whether the host is on the list for logging. If not,
- arrange to unset the selector in the subprocess. */
--if ((log_write_selector & L_smtp_connection) != 0)
-+if (LOGGING(smtp_connection))
-   {
-   uschar *list = hosts_connection_nolog;
-   if (list != NULL && verify_check_host(&list) == OK)
--    use_log_write_selector &= ~L_smtp_connection;
-+    save_log_selector &= ~L_smtp_connection;
-   else
-     log_write(L_smtp_connection, LOG_MAIN, "SMTP connection from %s "
-       "(TCP/IP connection count = %d)", whofrom, smtp_accept_count + 1);
-@@ -372,7 +372,7 @@ if (pid == 0)
-   /* May have been modified for the subprocess */
--  log_write_selector = use_log_write_selector;
-+  *log_selector = save_log_selector;
-   /* Get the local interface address into permanent store */
-diff --git a/src/src/deliver.c b/src/src/deliver.c
-index 78f8f4b..c796de0 100644
---- a/src/src/deliver.c
-+++ b/src/src/deliver.c
-@@ -682,7 +682,7 @@ d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
- {
-   s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
-     US" [", addr->host_used->address, US"]");
--  if ((log_extra_selector & LX_outgoing_port) != 0)
-+  if (LOGGING(outgoing_port))
-     s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
-       addr->host_used->port));
-   return s;
-@@ -692,10 +692,9 @@ d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
- static uschar *
- d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
- {
--  if ((log_extra_selector & LX_tls_cipher) != 0 && addr->cipher != NULL)
-+  if (LOGGING(tls_cipher) && addr->cipher != NULL)
-     s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
--  if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
--       addr->cipher != NULL)
-+  if (LOGGING(tls_certificate_verified) && addr->cipher != NULL)
-     s = string_append(s, sizep, ptrp, 2, US" CV=",
-       testflag(addr, af_cert_verified)
-       ?
-@@ -706,7 +705,7 @@ d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
- #endif
-         "yes"
-       : "no");
--  if ((log_extra_selector & LX_tls_peerdn) != 0 && addr->peerdn != NULL)
-+  if (LOGGING(tls_peerdn) && addr->peerdn != NULL)
-     s = string_append(s, sizep, ptrp, 3, US" DN=\"",
-       string_printing(addr->peerdn), US"\"");
-   return s;
-@@ -808,7 +807,7 @@ pointer to a single host item in their host list, for use by the transport. */
- s = reset_point = store_get(size);
--log_address = string_log_address(addr, (log_write_selector & L_all_parents) != 0, TRUE);
-+log_address = string_log_address(addr, LOGGING(all_parents), TRUE);
- if (msg)
-   s = string_append(s, &size, &ptr, 3, host_and_ident(TRUE), US" ", log_address);
- else
-@@ -817,11 +816,11 @@ else
-   s = string_append(s, &size, &ptr, 2, US"> ", log_address);
-   }
--if (log_extra_selector & LX_incoming_interface  &&  sending_ip_address)
-+if (LOGGING(incoming_interface) && sending_ip_address)
-   s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]");
-   /* for the port:  string_sprintf("%d", sending_port) */
--if ((log_extra_selector & LX_sender_on_delivery) != 0  ||  msg)
-+if (LOGGING(sender_on_delivery) || msg)
-   s = string_append(s, &size, &ptr, 3, US" F=<",
- #ifdef EXPERIMENTAL_INTERNATIONAL
-     testflag(addr, af_utf8_downcvt)
-@@ -841,8 +840,7 @@ delivery; indeed, I did for some time, until this statement crashed. The case
- when it is not set is for a delivery to /dev/null which is optimised by not
- being run at all. */
--if (used_return_path != NULL &&
--      (log_extra_selector & LX_return_path_on_delivery) != 0)
-+if (used_return_path != NULL && LOGGING(return_path_on_delivery))
-   s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
- if (msg)
-@@ -854,7 +852,7 @@ if (addr->router != NULL)
- s = string_append(s, &size, &ptr, 2, US" T=", addr->transport->name);
--if ((log_extra_selector & LX_delivery_size) != 0)
-+if (LOGGING(delivery_size))
-   s = string_append(s, &size, &ptr, 2, US" S=",
-     string_sprintf("%d", transport_count));
-@@ -901,7 +899,7 @@ else
-     if (addr->auth_id)
-       {
-       s = string_append(s, &size, &ptr, 2, US":", addr->auth_id);
--      if (log_extra_selector & LX_smtp_mailauth  &&  addr->auth_sndr)
-+      if (LOGGING(smtp_mailauth) && addr->auth_sndr)
-         s = string_append(s, &size, &ptr, 2, US":", addr->auth_sndr);
-       }
-     }
-@@ -914,8 +912,7 @@ else
- /* confirmation message (SMTP (host_used) and LMTP (driver_name)) */
--if (log_extra_selector & LX_smtp_confirmation &&
--    addr->message &&
-+if (LOGGING(smtp_confirmation) && addr->message &&
-     (addr->host_used || Ustrcmp(addr->transport->driver_name, "lmtp") == 0))
-   {
-   unsigned i;
-@@ -935,11 +932,11 @@ if (log_extra_selector & LX_smtp_confirmation &&
- /* Time on queue and actual time taken to deliver */
--if ((log_extra_selector & LX_queue_time) != 0)
-+if (LOGGING(queue_time))
-   s = string_append(s, &size, &ptr, 2, US" QT=",
-     readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
--if ((log_extra_selector & LX_deliver_time) != 0)
-+if (LOGGING(deliver_time))
-   s = string_append(s, &size, &ptr, 2, US" DT=",
-     readconf_printtime(addr->more_errno));
-@@ -1230,8 +1227,7 @@ else if (result == DEFER || result == PANIC)
-     /* Create the address string for logging. Must not do this earlier, because
-     an OK result may be changed to FAIL when a pipe returns text. */
--    log_address = string_log_address(addr,
--      (log_write_selector & L_all_parents) != 0, result == OK);
-+    log_address = string_log_address(addr, LOGGING(all_parents), result == OK);
-     s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address));
-@@ -1342,18 +1338,16 @@ else
-   /* Create the address string for logging. Must not do this earlier, because
-   an OK result may be changed to FAIL when a pipe returns text. */
--  log_address = string_log_address(addr,
--    (log_write_selector & L_all_parents) != 0, result == OK);
-+  log_address = string_log_address(addr, LOGGING(all_parents), result == OK);
-   s = string_cat(s, &size, &ptr, log_address, Ustrlen(log_address));
--  if ((log_extra_selector & LX_sender_on_delivery) != 0)
-+  if (LOGGING(sender_on_delivery))
-     s = string_append(s, &size, &ptr, 3, US" F=<", sender_address, US">");
-   /* Return path may not be set if no delivery actually happened */
--  if (used_return_path != NULL &&
--      (log_extra_selector & LX_return_path_on_delivery) != 0)
-+  if (used_return_path != NULL && LOGGING(return_path_on_delivery))
-     s = string_append(s, &size, &ptr, 3, US" P=<", used_return_path, US">");
-   if (addr->router != NULL)
-@@ -4449,7 +4443,7 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
-       }
-     /* Local interface address/port */
--    if (log_extra_selector & LX_incoming_interface  &&  sending_ip_address)
-+    if (LOGGING(incoming_interface) && sending_ip_address)
-       {
-       uschar * ptr = big_buffer;
-       sprintf(CS ptr, "%.128s", sending_ip_address);
-@@ -7365,7 +7359,7 @@ if (addr_defer == NULL)
-   /* Log the end of this message, with queue time if requested. */
--  if ((log_extra_selector & LX_queue_time_overall) != 0)
-+  if (LOGGING(queue_time_overall))
-     log_write(0, LOG_MAIN, "Completed QT=%s",
-       readconf_printtime( (int) ((long)time(NULL) - (long)received_time)) );
-   else
-diff --git a/src/src/exim.c b/src/src/exim.c
-index f9d57ab..d7cb5d8 100644
---- a/src/src/exim.c
-+++ b/src/src/exim.c
-@@ -1639,6 +1639,10 @@ if (log_buffer == NULL)
-   exit(EXIT_FAILURE);
-   }
-+/* Initialize the default log options. */
-+
-+bits_set(log_selector, log_selector_size, log_default);
-+
- /* Set log_stderr to stderr, provided that stderr exists. This gets reset to
- NULL when the daemon is run and the file is closed. We have to use this
- indirection, because some systems don't allow writing to the variable "stderr".
-@@ -2451,8 +2455,8 @@ for (i = 1; i < argc; i++)
-         argrest++;
-         }
-       if (*argrest != 0)
--        decode_bits(&selector, NULL, D_memory, 0, argrest, debug_options,
--          debug_options_count, US"debug", 0);
-+        decode_bits(&selector, 1, debug_notall, argrest,
-+          debug_options, debug_options_count, US"debug", 0);
-       debug_selector = selector;
-       }
-     break;
-@@ -3787,14 +3791,17 @@ else
- /* Handle the decoding of logging options. */
--decode_bits(&log_write_selector, &log_extra_selector, 0, 0,
-+decode_bits(log_selector, log_selector_size, log_notall,
-   log_selector_string, log_options, log_options_count, US"log", 0);
- DEBUG(D_any)
-   {
-+  int i;
-   debug_printf("configuration file is %s\n", config_main_filename);
--  debug_printf("log selectors = %08x %08x\n", log_write_selector,
--    log_extra_selector);
-+  debug_printf("log selectors =");
-+  for (i = 0; i < log_selector_size; i++)
-+    debug_printf(" %08x", log_selector[i]);
-+  debug_printf("\n");
-   }
- /* If domain literals are not allowed, check the sender address that was
-@@ -4001,7 +4008,7 @@ a debugging feature for finding out what arguments certain MUAs actually use.
- Don't attempt it if logging is disabled, or if listing variables or if
- verifying/testing addresses or expansions. */
--if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
-+if (((debug_selector & D_any) != 0 || LOGGING(arguments))
-       && really_exim && !list_options && !checking)
-   {
-   int i;
-@@ -4036,7 +4043,7 @@ if (((debug_selector & D_any) != 0 || (log_extra_selector & LX_arguments) != 0)
-     while (*p) p++;
-     }
--  if ((log_extra_selector & LX_arguments) != 0)
-+  if (LOGGING(arguments))
-     log_write(0, LOG_MAIN, "%s", big_buffer);
-   else
-     debug_printf("%s\n", big_buffer);
-@@ -5021,7 +5028,7 @@ if (host_checking)
-       sender_host_address);
-   if (verify_check_host(&hosts_connection_nolog) == OK)
--    log_write_selector &= ~L_smtp_connection;
-+    BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
-   log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
-   /* NOTE: We do *not* call smtp_log_no_mail() if smtp_start_session() fails,
-@@ -5195,7 +5202,7 @@ if (smtp_input)
-   smtp_in = stdin;
-   smtp_out = stdout;
-   if (verify_check_host(&hosts_connection_nolog) == OK)
--    log_write_selector &= ~L_smtp_connection;
-+    BIT_CLEAR(log_selector, log_selector_size, Li_smtp_connection);
-   log_write(L_smtp_connection, LOG_MAIN, "%s", smtp_get_connection_info());
-   if (!smtp_start_session())
-     {
-diff --git a/src/src/functions.h b/src/src/functions.h
-index 0257904..4af0017 100644
---- a/src/src/functions.h
-+++ b/src/src/functions.h
-@@ -99,6 +99,9 @@ extern int     auth_get_no64_data(uschar **, uschar *);
- extern uschar *auth_xtextencode(uschar *, int);
- extern int     auth_xtextdecode(uschar *, uschar **);
-+extern void    bits_clear(unsigned int *, size_t, int *);
-+extern void    bits_set(unsigned int *, size_t, int *);
-+
- extern void    cancel_cutthrough_connection(const char *);
- extern int     check_host(void *, const uschar *, const uschar **, uschar **);
- extern uschar **child_exec_exim(int, BOOL, int *, BOOL, int, ...);
-@@ -123,8 +126,8 @@ extern void    debug_print_ids(uschar *);
- extern void    debug_print_string(uschar *);
- extern void    debug_print_tree(tree_node *);
- extern void    debug_vprintf(const char *, va_list);
--extern void    decode_bits(unsigned int *, unsigned int *,
--                  int, int, uschar *, bit_table *, int, uschar *, int);
-+extern void    decode_bits(unsigned int *, size_t, int *,
-+                 uschar *, bit_table *, int, uschar *, int);
- extern address_item *deliver_make_addr(uschar *, BOOL);
- extern void    deliver_init(void);
- extern void    delivery_log(int, address_item *, int, uschar *);
-diff --git a/src/src/globals.c b/src/src/globals.c
-index 66baffe..1344b5a 100644
---- a/src/src/globals.c
-+++ b/src/src/globals.c
-@@ -536,40 +536,45 @@ uschar *dccifd_options         = US"header";
- BOOL    debug_daemon           = FALSE;
- int     debug_fd               = -1;
- FILE   *debug_file             = NULL;
--bit_table debug_options[]      = {
--  { US"acl",            D_acl },
--  { US"all",            D_all },
--  { US"auth",           D_auth },
--  { US"deliver",        D_deliver },
--  { US"dns",            D_dns },
--  { US"dnsbl",          D_dnsbl },
--  { US"exec",           D_exec },
--  { US"expand",         D_expand },
--  { US"filter",         D_filter },
--  { US"hints_lookup",   D_hints_lookup },
--  { US"host_lookup",    D_host_lookup },
--  { US"ident",          D_ident },
--  { US"interface",      D_interface },
--  { US"lists",          D_lists },
--  { US"load",           D_load },
--  { US"local_scan",     D_local_scan },
--  { US"lookup",         D_lookup },
--  { US"memory",         D_memory },
--  { US"pid",            D_pid },
--  { US"process_info",   D_process_info },
--  { US"queue_run",      D_queue_run },
--  { US"receive",        D_receive },
--  { US"resolver",       D_resolver },
--  { US"retry",          D_retry },
--  { US"rewrite",        D_rewrite },
--  { US"route",          D_route },
--  { US"timestamp",      D_timestamp },
--  { US"tls",            D_tls },
--  { US"transport",      D_transport },
--  { US"uid",            D_uid },
--  { US"verify",         D_verify }
-+int     debug_notall[]         = {
-+  Di_memory,
-+  -1
- };
--int     debug_options_count    = sizeof(debug_options)/sizeof(bit_table);
-+bit_table debug_options[]      = { /* must be in alphabetical order */
-+  BIT_TABLE(D, acl),
-+  BIT_TABLE(D, all),
-+  BIT_TABLE(D, auth),
-+  BIT_TABLE(D, deliver),
-+  BIT_TABLE(D, dns),
-+  BIT_TABLE(D, dnsbl),
-+  BIT_TABLE(D, exec),
-+  BIT_TABLE(D, expand),
-+  BIT_TABLE(D, filter),
-+  BIT_TABLE(D, hints_lookup),
-+  BIT_TABLE(D, host_lookup),
-+  BIT_TABLE(D, ident),
-+  BIT_TABLE(D, interface),
-+  BIT_TABLE(D, lists),
-+  BIT_TABLE(D, load),
-+  BIT_TABLE(D, local_scan),
-+  BIT_TABLE(D, lookup),
-+  BIT_TABLE(D, memory),
-+  BIT_TABLE(D, pid),
-+  BIT_TABLE(D, process_info),
-+  BIT_TABLE(D, queue_run),
-+  BIT_TABLE(D, receive),
-+  BIT_TABLE(D, resolver),
-+  BIT_TABLE(D, retry),
-+  BIT_TABLE(D, rewrite),
-+  BIT_TABLE(D, route),
-+  BIT_TABLE(D, timestamp),
-+  BIT_TABLE(D, tls),
-+  BIT_TABLE(D, transport),
-+  BIT_TABLE(D, uid),
-+  BIT_TABLE(D, verify),
-+};
-+int     debug_options_count    = nelem(debug_options);
-+
- unsigned int debug_selector    = 0;
- int     delay_warning[DELAY_WARNING_SIZE] = { DELAY_WARNING_SIZE, 1, 24*60*60 };
- uschar *delay_warning_condition=
-@@ -813,78 +818,91 @@ uid_t   local_user_uid         = (uid_t)(-1);
- tree_node *localpartlist_anchor= NULL;
- int     localpartlist_count    = 0;
- uschar *log_buffer             = NULL;
--unsigned int log_extra_selector = LX_default;
-+
-+int     log_default[]          = { /* for initializing log_selector */
-+  Li_acl_warn_skipped,
-+  Li_connection_reject,
-+  Li_delay_delivery,
-+  Li_dnslist_defer,
-+  Li_etrn,
-+  Li_host_lookup_failed,
-+  Li_lost_incoming_connection,
-+  Li_queue_run,
-+  Li_rejected_header,
-+  Li_retry_defer,
-+  Li_sender_verify_fail,
-+  Li_size_reject,
-+  Li_skip_delivery,
-+  Li_smtp_confirmation,
-+  Li_tls_certificate_verified,
-+  Li_tls_cipher,
-+  -1
-+};
-+
- uschar *log_file_path          = US LOG_FILE_PATH
-                            "\0<--------------Space to patch log_file_path->";
--/* Those log options with L_xxx identifiers have values less than 0x800000 and
--are the ones that get put into log_write_selector. They can be used in calls to
--log_write() to test for the bit. The options with LX_xxx identifiers have
--values greater than 0x80000000 and are put into log_extra_selector (without the
--top bit). They are never used in calls to log_write(), but are tested
--independently. This separation became necessary when the number of log
--selectors was getting close to filling a 32-bit word. */
--
--/* Note that this list must be in alphabetical order. */
--
--bit_table log_options[]        = {
--  { US"8bitmime",                     LX_8bitmime },
--  { US"acl_warn_skipped",             LX_acl_warn_skipped },
--  { US"address_rewrite",              L_address_rewrite },
--  { US"all",                          L_all },
--  { US"all_parents",                  L_all_parents },
--  { US"arguments",                    LX_arguments },
--  { US"connection_reject",            L_connection_reject },
--  { US"delay_delivery",               L_delay_delivery },
--  { US"deliver_time",                 LX_deliver_time },
--  { US"delivery_size",                LX_delivery_size },
--  { US"dnslist_defer",                L_dnslist_defer },
--  { US"etrn",                         L_etrn },
--  { US"host_lookup_failed",           L_host_lookup_failed },
--  { US"ident_timeout",                LX_ident_timeout },
--  { US"incoming_interface",           LX_incoming_interface },
--  { US"incoming_port",                LX_incoming_port },
--  { US"lost_incoming_connection",     L_lost_incoming_connection },
--  { US"outgoing_port",                LX_outgoing_port },
--  { US"pid",                          LX_pid },
-+int     log_notall[]           = {
-+  -1
-+};
-+bit_table log_options[]        = { /* must be in alphabetical order */
-+  BIT_TABLE(L, 8bitmime),
-+  BIT_TABLE(L, acl_warn_skipped),
-+  BIT_TABLE(L, address_rewrite),
-+  BIT_TABLE(L, all),
-+  BIT_TABLE(L, all_parents),
-+  BIT_TABLE(L, arguments),
-+  BIT_TABLE(L, connection_reject),
-+  BIT_TABLE(L, delay_delivery),
-+  BIT_TABLE(L, deliver_time),
-+  BIT_TABLE(L, delivery_size),
-+  BIT_TABLE(L, dnslist_defer),
-+  BIT_TABLE(L, etrn),
-+  BIT_TABLE(L, host_lookup_failed),
-+  BIT_TABLE(L, ident_timeout),
-+  BIT_TABLE(L, incoming_interface),
-+  BIT_TABLE(L, incoming_port),
-+  BIT_TABLE(L, lost_incoming_connection),
-+  BIT_TABLE(L, outgoing_port),
-+  BIT_TABLE(L, pid),
- #ifdef EXPERIMENTAL_PROXY
--  { US"proxy",                        LX_proxy },
-+  BIT_TABLE(L, proxy),
- #endif
--  { US"queue_run",                    L_queue_run },
--  { US"queue_time",                   LX_queue_time },
--  { US"queue_time_overall",           LX_queue_time_overall },
--  { US"received_recipients",          LX_received_recipients },
--  { US"received_sender",              LX_received_sender },
--  { US"rejected_header",              LX_rejected_header },
--  { US"rejected_headers",             LX_rejected_header },
--  { US"retry_defer",                  L_retry_defer },
--  { US"return_path_on_delivery",      LX_return_path_on_delivery },
--  { US"sender_on_delivery",           LX_sender_on_delivery },
--  { US"sender_verify_fail",           LX_sender_verify_fail },
--  { US"size_reject",                  L_size_reject },
--  { US"skip_delivery",                L_skip_delivery },
--  { US"smtp_confirmation",            LX_smtp_confirmation },
--  { US"smtp_connection",              L_smtp_connection },
--  { US"smtp_incomplete_transaction",  L_smtp_incomplete_transaction },
--  { US"smtp_mailauth",                LX_smtp_mailauth },
--  { US"smtp_no_mail",                 LX_smtp_no_mail },
--  { US"smtp_protocol_error",          L_smtp_protocol_error },
--  { US"smtp_syntax_error",            L_smtp_syntax_error },
--  { US"subject",                      LX_subject },
--  { US"tls_certificate_verified",     LX_tls_certificate_verified },
--  { US"tls_cipher",                   LX_tls_cipher },
--  { US"tls_peerdn",                   LX_tls_peerdn },
--  { US"tls_sni",                      LX_tls_sni },
--  { US"unknown_in_list",              LX_unknown_in_list }
-+  BIT_TABLE(L, queue_run),
-+  BIT_TABLE(L, queue_time),
-+  BIT_TABLE(L, queue_time_overall),
-+  BIT_TABLE(L, received_recipients),
-+  BIT_TABLE(L, received_sender),
-+  BIT_TABLE(L, rejected_header),
-+  { US"rejected_headers", Li_rejected_header },
-+  BIT_TABLE(L, retry_defer),
-+  BIT_TABLE(L, return_path_on_delivery),
-+  BIT_TABLE(L, sender_on_delivery),
-+  BIT_TABLE(L, sender_verify_fail),
-+  BIT_TABLE(L, size_reject),
-+  BIT_TABLE(L, skip_delivery),
-+  BIT_TABLE(L, smtp_confirmation),
-+  BIT_TABLE(L, smtp_connection),
-+  BIT_TABLE(L, smtp_incomplete_transaction),
-+  BIT_TABLE(L, smtp_mailauth),
-+  BIT_TABLE(L, smtp_no_mail),
-+  BIT_TABLE(L, smtp_protocol_error),
-+  BIT_TABLE(L, smtp_syntax_error),
-+  BIT_TABLE(L, subject),
-+  BIT_TABLE(L, tls_certificate_verified),
-+  BIT_TABLE(L, tls_cipher),
-+  BIT_TABLE(L, tls_peerdn),
-+  BIT_TABLE(L, tls_sni),
-+  BIT_TABLE(L, unknown_in_list),
- };
-+int     log_options_count      = nelem(log_options);
--int     log_options_count      = sizeof(log_options)/sizeof(bit_table);
- int     log_reject_target      = 0;
-+unsigned int log_selector[log_selector_size]; /* initialized in main() */
- uschar *log_selector_string    = NULL;
- FILE   *log_stderr             = NULL;
- BOOL    log_testing_mode       = FALSE;
- BOOL    log_timezone           = FALSE;
--unsigned int log_write_selector= L_default;
- uschar *login_sender_address   = NULL;
- uschar *lookup_dnssec_authenticated = NULL;
- int     lookup_open_max        = 25;
-diff --git a/src/src/globals.h b/src/src/globals.h
-index ab03302..978a4cc 100644
---- a/src/src/globals.h
-+++ b/src/src/globals.h
-@@ -319,6 +319,7 @@ extern uschar *dccifd_options;         /* options for the dccifd daemon */
- extern BOOL    debug_daemon;           /* Debug the daemon process only */
- extern int     debug_fd;               /* The fd for debug_file */
- extern FILE   *debug_file;             /* Where to write debugging info */
-+extern int     debug_notall[];         /* Debug options excluded from +all */
- extern bit_table debug_options[];      /* Table of debug options */
- extern int     debug_options_count;    /* Size of table */
- extern int     delay_warning[];        /* Times between warnings */
-@@ -531,16 +532,17 @@ extern uid_t   local_user_uid;         /* As it says; may be set in routers */
- extern tree_node *localpartlist_anchor;/* Tree of defined localpart lists */
- extern int     localpartlist_count;    /* Number defined */
- extern uschar *log_buffer;             /* For constructing log entries */
--extern unsigned int log_extra_selector;/* Bit map of logging options other than used by log_write() */
-+extern int     log_default[];          /* Initialization list for log_selector */
- extern uschar *log_file_path;          /* If unset, use default */
-+extern int     log_notall[];           /* Log options excluded from +all */
- extern bit_table log_options[];        /* Table of options */
- extern int     log_options_count;      /* Size of table */
- extern int     log_reject_target;      /* Target log for ACL rejections */
-+extern unsigned int log_selector[];    /* Bit map of logging options */
- extern uschar *log_selector_string;    /* As supplied in the config */
- extern FILE   *log_stderr;             /* Copy of stderr for log use, or NULL */
- extern BOOL    log_testing_mode;       /* TRUE in various testing modes */
- extern BOOL    log_timezone;           /* TRUE to include the timezone in log lines */
--extern unsigned int log_write_selector;/* Bit map of logging options for log_write() */
- extern uschar *login_sender_address;   /* The actual sender address */
- extern lookup_info **lookup_list;      /* Array of pointers to available lookups */
- extern int     lookup_list_count;      /* Number of entries in the list */
-diff --git a/src/src/host.c b/src/src/host.c
-index 31c2bbf..5c69c7f 100644
---- a/src/src/host.c
-+++ b/src/src/host.c
-@@ -544,7 +544,7 @@ use this directly as the first item for Received: because it ain't an RFC 2822
- domain. Sigh. */
- address = string_sprintf("[%s]:%d", sender_host_address, sender_host_port);
--if ((log_extra_selector & LX_incoming_port) == 0 || sender_host_port <= 0)
-+if (!LOGGING(incoming_port) || sender_host_port <= 0)
-   *(Ustrrchr(address, ':')) = 0;
- /* If there's no EHLO/HELO data, we can't show it. */
-@@ -695,8 +695,7 @@ else
-   {
-   uschar *flag = useflag? US"H=" : US"";
-   uschar *iface = US"";
--  if ((log_extra_selector & LX_incoming_interface) != 0 &&
--       interface_address != NULL)
-+  if (LOGGING(incoming_interface) && interface_address != NULL)
-     iface = string_sprintf(" I=[%s]:%d", interface_address, interface_port);
-   if (sender_ident == NULL)
-     (void)string_format(big_buffer, big_buffer_size, "%s%s%s",
-diff --git a/src/src/log.c b/src/src/log.c
-index 11b3edf..b2d1fcf 100644
---- a/src/src/log.c
-+++ b/src/src/log.c
-@@ -613,7 +613,7 @@ If a message_id exists, we include it after the timestamp.
- Arguments:
-   selector  write to main log or LOG_INFO only if this value is zero, or if
--              its bit is set in log_write_selector
-+              its bit is set in log_selector[0]
-   flags     each bit indicates some independent action:
-               LOG_SENDER      add raw sender to the message
-               LOG_RECIPIENTS  add raw recipients list to message
-@@ -749,15 +749,12 @@ DEBUG(D_any|D_v)
-   Ustrcpy(ptr, "LOG:");
-   ptr += 4;
--  /* Show the options that were passed into the call. These are those whose
--  flag values do not have the 0x80000000 bit in them. Note that this
--  automatically exclude the "all" setting. */
-+  /* Show the selector that was passed into the call. */
-   for (i = 0; i < log_options_count; i++)
-     {
-     unsigned int bit = log_options[i].bit;
--    if ((bit & 0x80000000) != 0) continue;
--    if ((selector & bit) != 0)
-+    if (bit < BITWORDSIZE && selector == BIT(bit))
-       {
-       *ptr++ = ' ';
-       Ustrcpy(ptr, log_options[i].name);
-@@ -809,7 +806,7 @@ ptr = log_buffer;
- sprintf(CS ptr, "%s ", tod_stamp(tod_log));
- while(*ptr) ptr++;
--if ((log_extra_selector & LX_pid) != 0)
-+if (LOGGING(pid))
-   {
-   sprintf(CS ptr, "[%d] ", (int)getpid());
-   while (*ptr) ptr++;
-@@ -869,7 +866,7 @@ or unless there is no log_stderr (expn called from daemon, for example). */
- if (!really_exim || log_testing_mode)
-   {
-   if (debug_selector == 0 && log_stderr != NULL &&
--      (selector == 0 || (selector & log_write_selector) != 0))
-+      (selector == 0 || (selector & log_selector[0]) != 0))
-     {
-     if (host_checking)
-       fprintf(log_stderr, "LOG: %s", CS(log_buffer + 20));  /* no timestamp */
-@@ -887,7 +884,7 @@ has been renamed. Therefore, do a stat() and see if the inode has changed, and
- if so, re-open. */
- if ((flags & LOG_MAIN) != 0 &&
--    (selector == 0 || (selector & log_write_selector) != 0))
-+    (selector == 0 || (selector & log_selector[0]) != 0))
-   {
-   if ((logging_mode & LOG_MODE_SYSLOG) != 0 &&
-       (syslog_duplication || (flags & (LOG_REJECT|LOG_PANIC)) == 0))
-@@ -956,7 +953,7 @@ if ((flags & LOG_REJECT) != 0)
-   {
-   header_line *h;
--  if (header_list != NULL && (log_extra_selector & LX_rejected_header) != 0)
-+  if (header_list != NULL && LOGGING(rejected_header))
-     {
-     if (recipients_count > 0)
-       {
-@@ -1142,6 +1139,35 @@ syslog_open = FALSE;
- /*************************************************
-+*             Multi-bit set or clear             *
-+*************************************************/
-+
-+/* These functions take a list of bit indexes (terminated by -1) and
-+clear or set the corresponding bits in the selector.
-+
-+Arguments:
-+  selector       address of the bit string
-+  selsize        number of words in the bit string
-+  bits           list of bits to set
-+*/
-+
-+void
-+bits_clear(unsigned int *selector, size_t selsize, int *bits)
-+{
-+for(; *bits != -1; ++bits)
-+  BIT_CLEAR(selector, selsize, *bits);
-+}
-+
-+void
-+bits_set(unsigned int *selector, size_t selsize, int *bits)
-+{
-+for(; *bits != -1; ++bits)
-+  BIT_SET(selector, selsize, *bits);
-+}
-+
-+
-+
-+/*************************************************
- *         Decode bit settings for log/debug      *
- *************************************************/
-@@ -1151,13 +1177,9 @@ also recognizes a numeric setting of the form =<number>, but this is not
- intended for user use. It's an easy way for Exim to pass the debug settings
- when it is re-exec'ed.
--The log options are held in two unsigned ints (because there became too many
--for one). The top bit in the table means "put in 2nd selector". This does not
--yet apply to debug options, so the "=" facility sets only the first selector.
--
--The "all" selector, which must be equal to 0xffffffff, is recognized specially.
--It sets all the bits in both selectors. However, there is a facility for then
--unsetting certain bits, because we want to turn off "memory" in the debug case.
-+The option table is a list of names and bit indexes. The index -1
-+means "set all bits, except for those listed in notall". The notall
-+list is terminated by -1.
- The action taken for bad values varies depending upon why we're here.
- For log messages, or if the debugging is triggered from config, then we write
-@@ -1165,10 +1187,9 @@ to the log on the way out.  For debug setting triggered from the command-line,
- we treat it as an unknown option: error message to stderr and die.
- Arguments:
--  selector1      address of the first bit string
--  selector2      address of the second bit string, or NULL
--  notall1        bits to exclude from "all" for selector1
--  notall2        bits to exclude from "all" for selector2
-+  selector       address of the bit string
-+  selsize        number of words in the bit string
-+  notall         list of bits to exclude from "all"
-   string         the configured string
-   options        the table of option names
-   count          size of table
-@@ -1179,9 +1200,8 @@ Returns:         nothing on success - bomb out on failure
- */
- void
--decode_bits(unsigned int *selector1, unsigned int *selector2, int notall1,
--  int notall2, uschar *string, bit_table *options, int count, uschar *which,
--  int flags)
-+decode_bits(unsigned int *selector, size_t selsize, int *notall,
-+  uschar *string, bit_table *options, int count, uschar *which, int flags)
- {
- uschar *errmsg;
- if (string == NULL) return;
-@@ -1189,7 +1209,8 @@ if (string == NULL) return;
- if (*string == '=')
-   {
-   char *end;    /* Not uschar */
--  *selector1 = strtoul(CS string+1, &end, 0);
-+  memset(selector, 0, sizeof(*selector)*selsize);
-+  *selector = strtoul(CS string+1, &end, 0);
-   if (*end == 0) return;
-   errmsg = string_sprintf("malformed numeric %s_selector setting: %s", which,
-     string);
-@@ -1232,40 +1253,22 @@ else for(;;)
-       if (middle->name[len] != 0) c = -1; else
-         {
-         unsigned int bit = middle->bit;
--        unsigned int *selector;
--
--        /* The value with all bits set means "force all bits in both selectors"
--        in the case where two are being handled. However, the top bit in the
--        second selector is never set. When setting, some bits can be excluded.
--        */
--
--        if (bit == 0xffffffff)
--          {
--          if (adding)
--            {
--            *selector1 = 0xffffffff ^ notall1;
--            if (selector2 != NULL) *selector2 = 0x7fffffff ^ notall2;
--            }
--          else
--            {
--            *selector1 = 0;
--            if (selector2 != NULL) *selector2 = 0;
--            }
--          }
--
--        /* Otherwise, the 0x80000000 bit means "this value, without the top
--        bit, belongs in the second selector". */
--        else
--          {
--          if ((bit & 0x80000000) != 0)
--            {
--            selector = selector2;
--            bit &= 0x7fffffff;
--            }
--          else selector = selector1;
--          if (adding) *selector |= bit; else *selector &= ~bit;
--          }
-+      if (bit == -1)
-+        {
-+        if (adding)
-+          {
-+          memset(selector, -1, sizeof(*selector)*selsize);
-+          bits_clear(selector, selsize, notall);
-+          }
-+        else
-+          memset(selector, 0, sizeof(*selector)*selsize);
-+        }
-+      else if (adding)
-+        BIT_SET(selector, selsize, bit);
-+      else
-+        BIT_CLEAR(selector, selsize, bit);
-+
-         break;  /* Out of loop to match selector name */
-         }
-       }
-@@ -1335,10 +1338,8 @@ if (tag_name != NULL && (Ustrchr(tag_name, '/') != NULL))
- debug_selector = D_default;
- if (opts)
--  {
--  decode_bits(&debug_selector, NULL, D_memory, 0, opts,
-+  decode_bits(&debug_selector, 1, debug_notall, opts,
-       debug_options, debug_options_count, US"debug", DEBUG_FROM_CONFIG);
--  }
- /* When activating from a transport process we may never have logged at all
- resulting in certain setup not having been done.  Hack this for now so we
-diff --git a/src/src/macros.h b/src/src/macros.h
-index 61f9ca6..d63025e 100644
---- a/src/src/macros.h
-+++ b/src/src/macros.h
-@@ -321,46 +321,84 @@ for having to swallow the rest of an SMTP message is whether the value is
- #define END_SIZE       4    /* Reading ended because message too big */
- #define END_WERROR     5    /* Write error while reading the message */
--/* Options bits for debugging; D_v and D_local_scan are also in local_scan.h */
--
--#define D_v                          0x00000001
--#define D_local_scan                 0x00000002
--
--#define D_acl                        0x00000004
--#define D_auth                       0x00000008
--#define D_deliver                    0x00000010
--#define D_dns                        0x00000020
--#define D_dnsbl                      0x00000040
--#define D_exec                       0x00000080
--#define D_expand                     0x00000100
--#define D_filter                     0x00000200
--#define D_hints_lookup               0x00000400
--#define D_host_lookup                0x00000800
--#define D_ident                      0x00001000
--#define D_interface                  0x00002000
--#define D_lists                      0x00004000
--#define D_load                       0x00008000
--#define D_lookup                     0x00010000
--#define D_memory                     0x00020000
--#define D_pid                        0x00040000
--#define D_process_info               0x00080000
--#define D_queue_run                  0x00100000
--#define D_receive                    0x00200000
--#define D_resolver                   0x00400000
--#define D_retry                      0x00800000
--#define D_rewrite                    0x01000000
--#define D_route                      0x02000000
--#define D_timestamp                  0x04000000
--#define D_tls                        0x08000000
--#define D_transport                  0x10000000
--#define D_uid                        0x20000000
--#define D_verify                     0x40000000
--
--/* The D_all value must always have all bits set, as it is recognized specially
--by the function that decodes debug and log selectors. This is to enable it to
--set all the bits in a multi-word selector. Debug doesn't use this yet, but we
--are getting close. In fact, we want to omit "memory" for -d+all, but can't
--handle this here. It is fudged externally. */
-+/* Bit masks for debug and log selectors */
-+
-+/* Assume words are 32 bits wide. Tiny waste of space on 64 bit
-+platforms, but this ensures bit vectors always work the same way. */
-+#define BITWORDSIZE 32
-+
-+/* This macro is for single-word bit vectors: the debug selector,
-+and the first word of the log selector. */
-+#define BIT(n) (1 << (n))
-+
-+/* And these are for multi-word vectors. */
-+#define BITWORD(n) (     (n) / BITWORDSIZE)
-+#define BITMASK(n) (1 << (n) % BITWORDSIZE)
-+
-+#define BIT_CLEAR(s,z,n) ((s)[BITWORD(n)] &= ~BITMASK(n))
-+#define BIT_SET(s,z,n)   ((s)[BITWORD(n)] |=  BITMASK(n))
-+#define BIT_TEST(s,z,n) (((s)[BITWORD(n)] &   BITMASK(n)) != 0)
-+
-+/* Used in globals.c for initializing bit_table structures. T will be either
-+D or L correspondong to the debug and log selector bits declared below. */
-+
-+#define BIT_TABLE(T,name) { US #name, T##i_##name }
-+
-+/* IOTA allows us to keep an implicit sequential count, like a simple enum,
-+but we can have sequentially numbered identifiers which are not declared
-+sequentially. We use this for more compact declarations of bit indexes and
-+masks, alternating between sequential bit index and corresponding mask. */
-+
-+#define IOTA(iota)      (__LINE__ - iota)
-+#define IOTA_INIT(zero) (__LINE__ - zero + 1)
-+
-+/* Options bits for debugging. DEBUG_BIT() declares both a bit index and the
-+corresponding mask. Di_all is a special value recognized by decode_bits().
-+
-+Exim's code assumes in a number of places that the debug_selector is one
-+word, and this is exposed in the local_scan ABI. The D_v and D_local_scan bit
-+masks are part of the local_scan API so are #defined in local_scan.h */
-+
-+#define DEBUG_BIT(name) Di_##name = IOTA(Di_iota), D_##name = BIT(Di_##name)
-+
-+enum {
-+  Di_all        = -1,
-+  Di_v          = 0,
-+  Di_local_scan = 1,
-+
-+  Di_iota = IOTA_INIT(2),
-+  DEBUG_BIT(acl),
-+  DEBUG_BIT(auth),
-+  DEBUG_BIT(deliver),
-+  DEBUG_BIT(dns),
-+  DEBUG_BIT(dnsbl),
-+  DEBUG_BIT(exec),
-+  DEBUG_BIT(expand),
-+  DEBUG_BIT(filter),
-+  DEBUG_BIT(hints_lookup),
-+  DEBUG_BIT(host_lookup),
-+  DEBUG_BIT(ident),
-+  DEBUG_BIT(interface),
-+  DEBUG_BIT(lists),
-+  DEBUG_BIT(load),
-+  DEBUG_BIT(lookup),
-+  DEBUG_BIT(memory),
-+  DEBUG_BIT(pid),
-+  DEBUG_BIT(process_info),
-+  DEBUG_BIT(queue_run),
-+  DEBUG_BIT(receive),
-+  DEBUG_BIT(resolver),
-+  DEBUG_BIT(retry),
-+  DEBUG_BIT(rewrite),
-+  DEBUG_BIT(route),
-+  DEBUG_BIT(timestamp),
-+  DEBUG_BIT(tls),
-+  DEBUG_BIT(transport),
-+  DEBUG_BIT(uid),
-+  DEBUG_BIT(verify),
-+};
-+
-+/* Multi-bit debug masks */
- #define D_all                        0xffffffff
-@@ -380,81 +418,67 @@ handle this here. It is fudged externally. */
-                                          D_timestamp   | \
-                                          D_resolver))
--/* Options bits for logging. Those that will end up in log_write_selector have
--values < 0x80000000. They can be used in calls to log_write(). The others have
--values > 0x80000000 and are put into log_extra_selector (without the top bit).
--These are only ever tested independently. "All" is a magic value that is used
--only in the name table to set all options in both bit maps. */
--
--/* The L_all value must always have all bits set, as it is recognized specially
--by the function that decodes debug and log selectors. This is to enable it to
--set all the bits in a multi-word selector. */
--
--#define L_all                          0xffffffff
--
--#define L_address_rewrite              0x00000001
--#define L_all_parents                  0x00000002
--#define L_connection_reject            0x00000004
--#define L_delay_delivery               0x00000008
--#define L_dnslist_defer                0x00000010
--#define L_etrn                         0x00000020
--#define L_host_lookup_failed           0x00000040
--#define L_lost_incoming_connection     0x00000080
--#define L_queue_run                    0x00000100
--#define L_retry_defer                  0x00000200
--#define L_size_reject                  0x00000400
--#define L_skip_delivery                0x00000800
--#define L_smtp_connection              0x00001000
--#define L_smtp_incomplete_transaction  0x00002000
--#define L_smtp_protocol_error          0x00004000
--#define L_smtp_syntax_error            0x00008000
--
--#define LX_acl_warn_skipped            0x80000001
--#define LX_arguments                   0x80000002
--#define LX_deliver_time                0x80000004
--#define LX_delivery_size               0x80000008
--#define LX_ident_timeout               0x80000010
--#define LX_incoming_interface          0x80000020
--#define LX_incoming_port               0x80000040
--#define LX_outgoing_port               0x80000080
--#define LX_pid                         0x80000100
--#define LX_queue_time                  0x80000200
--#define LX_queue_time_overall          0x80000400
--#define LX_received_sender             0x80000800
--#define LX_received_recipients         0x80001000
--#define LX_rejected_header             0x80002000
--#define LX_return_path_on_delivery     0x80004000
--#define LX_sender_on_delivery          0x80008000
--#define LX_sender_verify_fail          0x80010000
--#define LX_smtp_confirmation           0x80020000
--#define LX_smtp_no_mail                0x80040000
--#define LX_subject                     0x80080000
--#define LX_tls_certificate_verified    0x80100000
--#define LX_tls_cipher                  0x80200000
--#define LX_tls_peerdn                  0x80400000
--#define LX_tls_sni                     0x80800000
--#define LX_unknown_in_list             0x81000000
--#define LX_8bitmime                    0x82000000
--#define LX_smtp_mailauth               0x84000000
--#define LX_proxy                       0x88000000
--
--#define L_default     (L_connection_reject        | \
--                       L_delay_delivery           | \
--                       L_dnslist_defer            | \
--                       L_etrn                     | \
--                       L_host_lookup_failed       | \
--                       L_lost_incoming_connection | \
--                       L_queue_run                | \
--                       L_retry_defer              | \
--                       L_size_reject              | \
--                       L_skip_delivery)
--
--#define LX_default   ((LX_acl_warn_skipped        | \
--                       LX_rejected_header         | \
--                       LX_sender_verify_fail      | \
--                       LX_smtp_confirmation       | \
--                       LX_tls_certificate_verified| \
--                       LX_tls_cipher) & 0x7fffffff)
-+/* Options bits for logging. Those that have values < BITWORDSIZE can be used
-+in calls to log_write(). The others are put into later words in log_selector
-+and are only ever tested independently, so they do not need bit mask
-+declarations. The Li_all value is recognized specially by decode_bits(). */
-+
-+#define LOG_BIT(name) Li_##name = IOTA(Li_iota), L_##name = BIT(Li_##name)
-+
-+enum {
-+  Li_all = -1,
-+
-+  Li_iota = IOTA_INIT(0),
-+  LOG_BIT(address_rewrite),
-+  LOG_BIT(all_parents),
-+  LOG_BIT(connection_reject),
-+  LOG_BIT(delay_delivery),
-+  LOG_BIT(dnslist_defer),
-+  LOG_BIT(etrn),
-+  LOG_BIT(host_lookup_failed),
-+  LOG_BIT(lost_incoming_connection),
-+  LOG_BIT(queue_run),
-+  LOG_BIT(retry_defer),
-+  LOG_BIT(size_reject),
-+  LOG_BIT(skip_delivery),
-+  LOG_BIT(smtp_connection),
-+  LOG_BIT(smtp_incomplete_transaction),
-+  LOG_BIT(smtp_protocol_error),
-+  LOG_BIT(smtp_syntax_error),
-+
-+  Li_acl_warn_skipped = BITWORDSIZE,
-+  Li_arguments,
-+  Li_deliver_time,
-+  Li_delivery_size,
-+  Li_ident_timeout,
-+  Li_incoming_interface,
-+  Li_incoming_port,
-+  Li_outgoing_port,
-+  Li_pid,
-+  Li_queue_time,
-+  Li_queue_time_overall,
-+  Li_received_sender,
-+  Li_received_recipients,
-+  Li_rejected_header,
-+  Li_return_path_on_delivery,
-+  Li_sender_on_delivery,
-+  Li_sender_verify_fail,
-+  Li_smtp_confirmation,
-+  Li_smtp_no_mail,
-+  Li_subject,
-+  Li_tls_certificate_verified,
-+  Li_tls_cipher,
-+  Li_tls_peerdn,
-+  Li_tls_sni,
-+  Li_unknown_in_list,
-+  Li_8bitmime,
-+  Li_smtp_mailauth,
-+  Li_proxy,
-+
-+  log_selector_size = BITWORD(Li_proxy) + 1
-+};
-+
-+#define LOGGING(opt) BIT_TEST(log_selector, log_selector_size, Li_##opt)
- /* Private error numbers for delivery failures, set negative so as not
- to conflict with system errno values. */
-diff --git a/src/src/match.c b/src/src/match.c
-index 9e47110..fa42187 100644
---- a/src/src/match.c
-+++ b/src/src/match.c
-@@ -771,7 +771,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
-             include_unknown? "yes":"no", error);
-           if (!include_unknown)
-             {
--            if ((log_extra_selector & LX_unknown_in_list) != 0)
-+            if (LOGGING(unknown_in_list))
-               log_write(0, LOG_MAIN, "list matching forced to fail: %s", error);
-             return FAIL;
-             }
-@@ -880,7 +880,7 @@ while ((sss = string_nextinlist(&list, &sep, buffer, sizeof(buffer))) != NULL)
-           (void)fclose(f);
-           if (!include_unknown)
-             {
--            if ((log_extra_selector & LX_unknown_in_list) != 0)
-+            if (LOGGING(unknown_in_list))
-               log_write(0, LOG_MAIN, "list matching forced to fail: %s", error);
-             return FAIL;
-             }
-diff --git a/src/src/rda.c b/src/src/rda.c
-index 7596466..2afd6dc 100644
---- a/src/src/rda.c
-+++ b/src/src/rda.c
-@@ -635,7 +635,7 @@ if ((pid = fork()) == 0)
-     {
-     DEBUG(D_rewrite) debug_printf("turned off address rewrite logging (not "
-       "root or exim in this process)\n");
--    log_write_selector &= ~L_address_rewrite;
-+    BIT_CLEAR(log_selector, log_selector_size, Li_address_rewrite);
-     }
-   /* Now do the business */
-diff --git a/src/src/receive.c b/src/src/receive.c
-index 64cf1ae..b430ee2 100644
---- a/src/src/receive.c
-+++ b/src/src/receive.c
-@@ -1118,8 +1118,7 @@ add_host_info_for_log(uschar *s, int *sizeptr, int *ptrptr)
- if (sender_fullhost != NULL)
-   {
-   s = string_append(s, sizeptr, ptrptr, 2, US" H=", sender_fullhost);
--  if ((log_extra_selector & LX_incoming_interface) != 0 &&
--       interface_address != NULL)
-+  if (LOGGING(incoming_interface) && interface_address != NULL)
-     {
-     uschar *ss = string_sprintf(" I=[%s]:%d", interface_address,
-       interface_port);
-@@ -2529,7 +2528,7 @@ if (msgid_header == NULL &&
- rewriting. Must copy the count, because later ACLs and the local_scan()
- function may mess with the real recipients. */
--if ((log_extra_selector & LX_received_recipients) != 0)
-+if (LOGGING(received_recipients))
-   {
-   raw_recipients = store_get(recipients_count * sizeof(uschar *));
-   for (i = 0; i < recipients_count; i++)
-@@ -3573,7 +3572,7 @@ else
-     goto TEMPREJECT;
-     case LOCAL_SCAN_REJECT_NOLOGHDR:
--    log_extra_selector &= ~LX_rejected_header;
-+    BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
-     /* Fall through */
-     case LOCAL_SCAN_REJECT:
-@@ -3582,7 +3581,7 @@ else
-     break;
-     case LOCAL_SCAN_TEMPREJECT_NOLOGHDR:
--    log_extra_selector &= ~LX_rejected_header;
-+    BIT_CLEAR(log_selector, log_selector_size, Li_rejected_header);
-     /* Fall through */
-     case LOCAL_SCAN_TEMPREJECT:
-@@ -3747,15 +3746,15 @@ if (message_reference != NULL)
- s = add_host_info_for_log(s, &size, &sptr);
- #ifdef SUPPORT_TLS
--if (log_extra_selector & LX_tls_cipher && tls_in.cipher)
-+if (LOGGING(tls_cipher) && tls_in.cipher)
-   s = string_append(s, &size, &sptr, 2, US" X=", tls_in.cipher);
--if (log_extra_selector & LX_tls_certificate_verified && tls_in.cipher)
-+if (LOGGING(tls_certificate_verified) && tls_in.cipher)
-   s = string_append(s, &size, &sptr, 2, US" CV=",
-     tls_in.certificate_verified? "yes":"no");
--if (log_extra_selector & LX_tls_peerdn && tls_in.peerdn)
-+if (LOGGING(tls_peerdn) && tls_in.peerdn)
-   s = string_append(s, &size, &sptr, 3, US" DN=\"",
-     string_printing(tls_in.peerdn), US"\"");
--if (log_extra_selector & LX_tls_sni && tls_in.sni)
-+if (LOGGING(tls_sni) && tls_in.sni)
-   s = string_append(s, &size, &sptr, 3, US" SNI=\"",
-     string_printing(tls_in.sni), US"\"");
- #endif
-@@ -3766,7 +3765,7 @@ if (sender_host_authenticated)
-   if (authenticated_id != NULL)
-     {
-     s = string_append(s, &size, &sptr, 2, US":", authenticated_id);
--    if (log_extra_selector & LX_smtp_mailauth  &&  authenticated_sender != NULL)
-+    if (LOGGING(smtp_mailauth) && authenticated_sender != NULL)
-       s = string_append(s, &size, &sptr, 2, US":", authenticated_sender);
-     }
-   }
-@@ -3777,7 +3776,7 @@ if (prdr_requested)
- #endif
- #ifdef EXPERIMENTAL_PROXY
--if (proxy_session &&  log_extra_selector & LX_proxy)
-+if (proxy_session && LOGGING(proxy))
-   s = string_append(s, &size, &sptr, 2, US" PRX=", proxy_host_address);
- #endif
-@@ -3788,7 +3787,7 @@ s = string_append(s, &size, &sptr, 2, US" S=", big_buffer);
-    0 ... no BODY= used
-    7 ... 7BIT
-    8 ... 8BITMIME */
--if (log_extra_selector & LX_8bitmime)
-+if (LOGGING(8bitmime))
-   {
-   sprintf(CS big_buffer, "%d", body_8bitmime);
-   s = string_append(s, &size, &sptr, 2, US" M8S=", big_buffer);
-@@ -3814,7 +3813,7 @@ if (msgid_header != NULL)
- /* If subject logging is turned on, create suitable printing-character
- text. By expanding $h_subject: we make use of the MIME decoding. */
--if ((log_extra_selector & LX_subject) != 0 && subject_header != NULL)
-+if (LOGGING(subject) && subject_header != NULL)
-   {
-   int i;
-   uschar *p = big_buffer;
-@@ -4003,8 +4002,8 @@ if(!smtp_reply)
- #endif
-   {
-   log_write(0, LOG_MAIN |
--    (((log_extra_selector & LX_received_recipients) != 0)? LOG_RECIPIENTS : 0) |
--    (((log_extra_selector & LX_received_sender) != 0)? LOG_SENDER : 0),
-+    (LOGGING(received_recipients)? LOG_RECIPIENTS : 0) |
-+    (LOGGING(received_sender)? LOG_SENDER : 0),
-     "%s", s);
-   /* Log any control actions taken by an ACL or local_scan(). */
-diff --git a/src/src/rewrite.c b/src/src/rewrite.c
-index 296fe8c..ca7fb6a 100644
---- a/src/src/rewrite.c
-+++ b/src/src/rewrite.c
-@@ -247,8 +247,7 @@ for (rule = rewrite_rules;
-   /* We have a validly rewritten address */
--  if ((log_write_selector & L_address_rewrite) != 0 ||
--      (debug_selector & D_rewrite) != 0)
-+  if (LOGGING(address_rewrite) || (debug_selector & D_rewrite) != 0)
-     {
-     int i;
-     const uschar *where = CUS"?";
-diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
-index effc636..9982451 100644
---- a/src/src/smtp_in.c
-+++ b/src/src/smtp_in.c
-@@ -1234,8 +1234,7 @@ if (sender_host_unknown || sender_host_notsocket)
- if (is_inetd)
-   return string_sprintf("SMTP connection from %s (via inetd)", hostname);
--if ((log_extra_selector & LX_incoming_interface) != 0 &&
--     interface_address != NULL)
-+if (LOGGING(incoming_interface) && interface_address != NULL)
-   return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname,
-     interface_address, interface_port);
-@@ -1260,16 +1259,15 @@ s_tlslog(uschar * s, int * sizep, int * ptrp)
-   int size = sizep ? *sizep : 0;
-   int ptr = ptrp ? *ptrp : 0;
--  if ((log_extra_selector & LX_tls_cipher) != 0 && tls_in.cipher != NULL)
-+  if (LOGGING(tls_cipher) && tls_in.cipher != NULL)
-     s = string_append(s, &size, &ptr, 2, US" X=", tls_in.cipher);
--  if ((log_extra_selector & LX_tls_certificate_verified) != 0 &&
--       tls_in.cipher != NULL)
-+  if (LOGGING(tls_certificate_verified) && tls_in.cipher != NULL)
-     s = string_append(s, &size, &ptr, 2, US" CV=",
-       tls_in.certificate_verified? "yes":"no");
--  if ((log_extra_selector & LX_tls_peerdn) != 0 && tls_in.peerdn != NULL)
-+  if (LOGGING(tls_peerdn) && tls_in.peerdn != NULL)
-     s = string_append(s, &size, &ptr, 3, US" DN=\"",
-       string_printing(tls_in.peerdn), US"\"");
--  if ((log_extra_selector & LX_tls_sni) != 0 && tls_in.sni != NULL)
-+  if (LOGGING(tls_sni) && tls_in.sni != NULL)
-     s = string_append(s, &size, &ptr, 3, US" SNI=\"",
-       string_printing(tls_in.sni), US"\"");
-@@ -1301,7 +1299,7 @@ smtp_log_no_mail(void)
- int size, ptr, i;
- uschar *s, *sep;
--if (smtp_mailcmd_count > 0 || (log_extra_selector & LX_smtp_no_mail) == 0)
-+if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail))
-   return;
- s = NULL;
-@@ -2510,8 +2508,8 @@ static void
- incomplete_transaction_log(uschar *what)
- {
- if (sender_address == NULL ||                 /* No transaction in progress */
--    (log_write_selector & L_smtp_incomplete_transaction) == 0  /* Not logging */
--  ) return;
-+    !LOGGING(smtp_incomplete_transaction))
-+  return;
- /* Build list of recipients for logging */
-@@ -2762,7 +2760,7 @@ if (sender_verified_failed != NULL &&
-   setflag(sender_verified_failed, af_sverify_told);
--  if (rc != FAIL || (log_extra_selector & LX_sender_verify_fail) != 0)
-+  if (rc != FAIL || LOGGING(sender_verify_fail))
-     log_write(0, LOG_MAIN|LOG_REJECT, "%s sender verify %s for <%s>%s",
-       host_and_ident(TRUE),
-       ((sender_verified_failed->special_action & 255) == DEFER)? "defer":"fail",
-diff --git a/src/src/structs.h b/src/src/structs.h
-index 6f143d6..438b521 100644
---- a/src/src/structs.h
-+++ b/src/src/structs.h
-@@ -38,7 +38,7 @@ typedef struct macro_item {
- typedef struct bit_table {
-   uschar *name;
--  unsigned int bit;
-+  int bit;
- } bit_table;
- /* Block for holding a uid and gid, possibly unset, and an initgroups flag. */
-diff --git a/src/src/tls-openssl.c b/src/src/tls-openssl.c
-index b1dccb8..73ac807 100644
---- a/src/src/tls-openssl.c
-+++ b/src/src/tls-openssl.c
-@@ -1111,8 +1111,7 @@ len = SSL_get_tlsext_status_ocsp_resp(s, &p);
- if(!p)
-  {
-   /* Expect this when we requested ocsp but got none */
--  if (  cbinfo->u_ocsp.client.verify_required
--     && log_extra_selector & LX_tls_cipher)
-+  if (cbinfo->u_ocsp.client.verify_required && LOGGING(tls_cipher))
-     log_write(0, LOG_MAIN, "Received TLS status callback, null content");
-   else
-     DEBUG(D_tls) debug_printf(" null\n");
-@@ -1122,7 +1121,7 @@ if(!p)
- if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
-  {
-   tls_out.ocsp = OCSP_FAILED;
--  if (log_extra_selector & LX_tls_cipher)
-+  if (LOGGING(tls_cipher))
-     log_write(0, LOG_MAIN, "Received TLS cert status response, parse error");
-   else
-     DEBUG(D_tls) debug_printf(" parse error\n");
-@@ -1132,7 +1131,7 @@ if(!(rsp = d2i_OCSP_RESPONSE(NULL, &p, len)))
- if(!(bs = OCSP_response_get1_basic(rsp)))
-   {
-   tls_out.ocsp = OCSP_FAILED;
--  if (log_extra_selector & LX_tls_cipher)
-+  if (LOGGING(tls_cipher))
-     log_write(0, LOG_MAIN, "Received TLS cert status response, error parsing response");
-   else
-     DEBUG(D_tls) debug_printf(" error parsing response\n");
-@@ -1163,7 +1162,7 @@ if(!(bs = OCSP_response_get1_basic(rsp)))
-             cbinfo->u_ocsp.client.verify_store, 0)) <= 0)
-       {
-       tls_out.ocsp = OCSP_FAILED;
--      if (log_extra_selector & LX_tls_cipher)
-+      if (LOGGING(tls_cipher))
-       log_write(0, LOG_MAIN, "Received TLS cert status response, itself unverifiable");
-       BIO_printf(bp, "OCSP response verify failure\n");
-       ERR_print_errors(bp);
-diff --git a/src/src/transports/lmtp.c b/src/src/transports/lmtp.c
-index 0cd89af..1f4d7a6 100644
---- a/src/src/transports/lmtp.c
-+++ b/src/src/transports/lmtp.c
-@@ -654,7 +654,7 @@ if (send_data)
-     if (lmtp_read_response(out, buffer, sizeof(buffer), '2', timeout))
-       {
-       addr->transport_return = OK;
--      if ((log_extra_selector & LX_smtp_confirmation) != 0)
-+      if (LOGGING(smtp_confirmation))
-         {
-         const uschar *s = string_printing(buffer);
-       /* de-const safe here as string_printing known to have alloc'n'copied */
-diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
-index a952413..609dba3 100644
---- a/src/src/transports/smtp.c
-+++ b/src/src/transports/smtp.c
-@@ -638,7 +638,7 @@ if (addr->message)
-   }
- else
-   {
--  if (log_extra_selector & LX_outgoing_port)
-+  if (LOGGING(outgoing_port))
-     message = string_sprintf("%s:%d", message,
-               host->port == PORT_NONE ? 25 : host->port);
-   log_write(0, LOG_MAIN, "%s %s", message, strerror(addr->basic_errno));
-@@ -2380,7 +2380,7 @@ if (!ok) ok = TRUE; else
-     if (
- #ifndef EXPERIMENTAL_EVENT
--          (log_extra_selector & LX_smtp_confirmation) != 0 &&
-+          LOGGING(smtp_confirmation) &&
- #endif
-           !lmtp
-        )
-@@ -2435,7 +2435,7 @@ if (!ok) ok = TRUE; else
-           continue;
-           }
-         completed_address = TRUE;   /* NOW we can set this flag */
--        if ((log_extra_selector & LX_smtp_confirmation) != 0)
-+        if (LOGGING(smtp_confirmation))
-           {
-           const uschar *s = string_printing(buffer);
-         /* deconst cast ok here as string_printing was checked to have alloc'n'copied */
-diff --git a/src/src/verify.c b/src/src/verify.c
-index e00e7b9..7992d58 100644
---- a/src/src/verify.c
-+++ b/src/src/verify.c
-@@ -2916,7 +2916,7 @@ if (ip_bind(sock, host_af, interface_address, 0) < 0)
- if (ip_connect(sock, host_af, sender_host_address, port, rfc1413_query_timeout)
-      < 0)
-   {
--  if (errno == ETIMEDOUT && (log_extra_selector & LX_ident_timeout) != 0)
-+  if (errno == ETIMEDOUT && LOGGING(ident_timeout))
-     {
-     log_write(0, LOG_MAIN, "ident connection to %s timed out",
-       sender_host_address);
-
-commit ac881e2749754fbe167b5f38784dd85b088571cf
-Author: Tony Finch <dot@dotat.at>
-Date:   Thu Aug 13 15:16:51 2015 +0100
-
-    Improve the consistency of logging incoming and outgoing interfaces.
-    
-    The I= interface field on outgoing lines is now after the H= remote
-    host field, same as incoming lines. There is a separate outgoing_interface
-    log selector which allows you to disable the outgoing I= field.
-    
-    (slight massaging by JH)
-
-diff --git a/src/src/deliver.c b/src/src/deliver.c
-index c796de0..0e7cea3 100644
---- a/src/src/deliver.c
-+++ b/src/src/deliver.c
-@@ -676,39 +676,78 @@ while (addr->parent != NULL)
-+/*************************************************
-+*      Delivery logging support functions        *
-+*************************************************/
-+
-+/* The LOGGING() checks in d_log_interface() are complicated for backwards
-+compatibility. When outgoing interface logging was originally added, it was
-+conditional on just incoming_interface (which is off by default). The
-+outgoing_interface option is on by default to preserve this behaviour, but
-+you can enable incoming_interface and disable outgoing_interface to get I=
-+fields on incoming lines only.
-+
-+Arguments:
-+  s         The log line buffer
-+  sizep     Pointer to the buffer size
-+  ptrp      Pointer to current index into buffer
-+  addr      The address to be logged
-+
-+Returns:    New value for s
-+*/
- static uschar *
--d_hostlog(uschar * s, int * sizep, int * ptrp, address_item * addr)
-+d_log_interface(uschar *s, int *sizep, int *ptrp)
- {
--  s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
--    US" [", addr->host_used->address, US"]");
-+if (LOGGING(incoming_interface) && LOGGING(outgoing_interface)
-+    && sending_ip_address != NULL)
-+  {
-+  s = string_append(s, sizep, ptrp, 2, US" I=[", sending_ip_address);
-   if (LOGGING(outgoing_port))
--    s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
--      addr->host_used->port));
--  return s;
-+    s = string_append(s, sizep, ptrp, 2, US"]:",
-+      string_sprintf("%d", sending_port));
-+  else
-+    s = string_cat(s, sizep, ptrp, "]", 1);
-+  }
-+return s;
- }
-+
-+
-+static uschar *
-+d_hostlog(uschar *s, int *sizep, int *ptrp, address_item *addr)
-+{
-+s = string_append(s, sizep, ptrp, 5, US" H=", addr->host_used->name,
-+  US" [", addr->host_used->address, US"]");
-+if (LOGGING(outgoing_port))
-+  s = string_append(s, sizep, ptrp, 2, US":", string_sprintf("%d",
-+    addr->host_used->port));
-+return d_log_interface(s, sizep, ptrp);
-+}
-+
-+
-+
- #ifdef SUPPORT_TLS
- static uschar *
- d_tlslog(uschar * s, int * sizep, int * ptrp, address_item * addr)
- {
--  if (LOGGING(tls_cipher) && addr->cipher != NULL)
--    s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
--  if (LOGGING(tls_certificate_verified) && addr->cipher != NULL)
--    s = string_append(s, sizep, ptrp, 2, US" CV=",
--      testflag(addr, af_cert_verified)
--      ?
-+if (LOGGING(tls_cipher) && addr->cipher != NULL)
-+  s = string_append(s, sizep, ptrp, 2, US" X=", addr->cipher);
-+if (LOGGING(tls_certificate_verified) && addr->cipher != NULL)
-+  s = string_append(s, sizep, ptrp, 2, US" CV=",
-+    testflag(addr, af_cert_verified)
-+    ?
- #ifdef EXPERIMENTAL_DANE
--        testflag(addr, af_dane_verified)
--      ? "dane"
--      :
-+      testflag(addr, af_dane_verified)
-+    ? "dane"
-+    :
- #endif
--        "yes"
--      : "no");
--  if (LOGGING(tls_peerdn) && addr->peerdn != NULL)
--    s = string_append(s, sizep, ptrp, 3, US" DN=\"",
--      string_printing(addr->peerdn), US"\"");
--  return s;
-+      "yes"
-+    : "no");
-+if (LOGGING(tls_peerdn) && addr->peerdn != NULL)
-+  s = string_append(s, sizep, ptrp, 3, US" DN=\"",
-+    string_printing(addr->peerdn), US"\"");
-+return s;
- }
- #endif
-@@ -816,10 +855,6 @@ else
-   s = string_append(s, &size, &ptr, 2, US"> ", log_address);
-   }
--if (LOGGING(incoming_interface) && sending_ip_address)
--  s = string_append(s, &size, &ptr, 3, US" I=[", sending_ip_address, US"]");
--  /* for the port:  string_sprintf("%d", sending_port) */
--
- if (LOGGING(sender_on_delivery) || msg)
-   s = string_append(s, &size, &ptr, 3, US" F=<",
- #ifdef EXPERIMENTAL_INTERNATIONAL
-@@ -862,6 +897,7 @@ if (addr->transport->info->local)
-   {
-   if (addr->host_list)
-     s = string_append(s, &size, &ptr, 2, US" H=", addr->host_list->name);
-+  s = d_log_interface(s, &size, &ptr);
-   if (addr->shadow_message != NULL)
-     s = string_cat(s, &size, &ptr, addr->shadow_message,
-       Ustrlen(addr->shadow_message));
-diff --git a/src/src/globals.c b/src/src/globals.c
-index 1344b5a..4188b4d 100644
---- a/src/src/globals.c
-+++ b/src/src/globals.c
-@@ -827,6 +827,7 @@ int     log_default[]          = { /* for initializing log_selector */
-   Li_etrn,
-   Li_host_lookup_failed,
-   Li_lost_incoming_connection,
-+  Li_outgoing_interface, /* see d_log_interface in deliver.c */
-   Li_queue_run,
-   Li_rejected_header,
-   Li_retry_defer,
-@@ -863,6 +864,7 @@ bit_table log_options[]        = { /* must be in alphabetical order */
-   BIT_TABLE(L, incoming_interface),
-   BIT_TABLE(L, incoming_port),
-   BIT_TABLE(L, lost_incoming_connection),
-+  BIT_TABLE(L, outgoing_interface),
-   BIT_TABLE(L, outgoing_port),
-   BIT_TABLE(L, pid),
- #ifdef EXPERIMENTAL_PROXY
-diff --git a/src/src/log.c b/src/src/log.c
-index b2d1fcf..558c000 100644
---- a/src/src/log.c
-+++ b/src/src/log.c
-@@ -753,8 +753,8 @@ DEBUG(D_any|D_v)
-   for (i = 0; i < log_options_count; i++)
-     {
--    unsigned int bit = log_options[i].bit;
--    if (bit < BITWORDSIZE && selector == BIT(bit))
-+    unsigned int bitnum = log_options[i].bit;
-+    if (bitnum < BITWORDSIZE && selector == BIT(bitnum))
-       {
-       *ptr++ = ' ';
-       Ustrcpy(ptr, log_options[i].name);
-diff --git a/src/src/macros.h b/src/src/macros.h
-index d63025e..0ce24f8 100644
---- a/src/src/macros.h
-+++ b/src/src/macros.h
-@@ -474,8 +474,9 @@ enum {
-   Li_8bitmime,
-   Li_smtp_mailauth,
-   Li_proxy,
-+  Li_outgoing_interface,
--  log_selector_size = BITWORD(Li_proxy) + 1
-+  log_selector_size = BITWORD(Li_outgoing_interface) + 1
- };
- #define LOGGING(opt) BIT_TEST(log_selector, log_selector_size, Li_##opt)
-
-commit 6b51df8340eacc95e3def9a4376506610e91996c
-Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
-Date:   Wed Aug 19 15:22:41 2015 +0200
-
-    Fix post-transport-crash.
-    
-    The crash probably was introduced in a39bd74d3e94 and
-    needs 'split_spool_directory=yes' to expose.
-    
-    Thanks to Wolfgang Breyha, who found the same fix.
-
-diff --git a/src/src/transport.c b/src/src/transport.c
-index fa6f869..a6ad3ed 100644
---- a/src/src/transport.c
-+++ b/src/src/transport.c
-@@ -1752,7 +1752,7 @@ while (1)
-     {
-     if (split_spool_directory)
-       sprintf(CS spool_file, "%s%c/%s-D",
--                    spool_dir, new_message_id[5], msgq[i].message_id);
-+                    spool_dir, msgq[i].message_id[5], msgq[i].message_id);
-     else
-       sprintf(CS spool_file, "%s%s-D", spool_dir, msgq[i].message_id);
-
-commit b20b82a0b4169cb23380a373ed2a898b0cb337d2
-Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
-Date:   Fri Aug 21 12:26:50 2015 +0200
-
-    Add a .ctags file to src
-
-diff --git a/src/.ctags b/src/.ctags
-new file mode 100644
-index 0000000..c764086
---- /dev/null
-+++ b/src/.ctags
-@@ -0,0 +1,2 @@
-+--recurse
-+--exclude=build-*
-
-commit dadff1d47e54962b0fdf98e8ce5cef42b6cb7fb5
-Author: Heiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>
-Date:   Thu Aug 20 13:58:06 2015 +0200
-
-    Fix post-transport-crash: safeguard for missing spool BUG 1671
-    
-    Based on a proposal from Wolfgang Breyha.
-
-diff --git a/src/src/deliver.c b/src/src/deliver.c
-index 0e7cea3..b5aa9b9 100644
---- a/src/src/deliver.c
-+++ b/src/src/deliver.c
-@@ -9,6 +9,7 @@
- #include "exim.h"
-+#include <assert.h>
- /* Data block for keeping track of subprocesses for parallel remote
-@@ -7934,17 +7935,36 @@ if (!regex_IGNOREQUOTA) regex_IGNOREQUOTA =
- uschar *
- deliver_get_sender_address (uschar * id)
- {
-+int rc;
-+uschar * new_sender_address,
-+       * save_sender_address;
-+
- if (!spool_open_datafile(id))
-   return NULL;
-+/* Save and restore the global sender_address.  I'm not sure if we should
-+not save/restore all the other global variables too, because
-+spool_read_header() may change all of them. But OTOH, when this
-+deliver_get_sender_address() gets called, the current message is done
-+already and nobody needs the globals anymore. (HS12, 2015-08-21) */
-+
- sprintf(CS spoolname, "%s-H", id);
--if (spool_read_header(spoolname, TRUE, TRUE) != spool_read_OK)
-+save_sender_address = sender_address;
-+
-+rc = spool_read_header(spoolname, TRUE, TRUE);
-+
-+new_sender_address = sender_address;
-+sender_address = save_sender_address;
-+
-+if (rc != spool_read_OK)
-   return NULL;
-+assert(new_sender_address);
-+
- (void)close(deliver_datafile);
- deliver_datafile = -1;
--return sender_address;
-+return new_sender_address;
- }
- /* vi: aw ai sw=2
-diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
-index 609dba3..c93f2ef 100644
---- a/src/src/transports/smtp.c
-+++ b/src/src/transports/smtp.c
-@@ -1274,14 +1274,19 @@ we will veto this new message.  */
- static BOOL
- smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare)
- {
--uschar * save_sender_address = sender_address;
--uschar * current_local_identity =
-+
-+uschar * message_local_identity,
-+       * current_local_identity,
-+       * new_sender_address;
-+
-+current_local_identity =
-   smtp_local_identity(s_compare->current_sender_address, s_compare->tblock);
--uschar * new_sender_address = deliver_get_sender_address(message_id);
--uschar * message_local_identity =
--  smtp_local_identity(new_sender_address, s_compare->tblock);
+   /* Call the code for the different kinds of search. DEFER is handled
+@@ -511,9 +521,7 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL)
  
--sender_address = save_sender_address;
-+if (!(new_sender_address = deliver_get_sender_address(message_id)))
-+    return 0;
-+
-+message_local_identity =
-+  smtp_local_identity(new_sender_address, s_compare->tblock);
- return Ustrcmp(current_local_identity, message_local_identity) == 0;
- }
-
-commit 3703d8187af01d13ca71f7918c7ef78529bb784d
-Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date:   Fri Aug 21 18:08:39 2015 +0100
-
-    Remember the fail reason for verify=headers_syntax.  Bug 264
-
-diff --git a/src/src/acl.c b/src/src/acl.c
-index f2e0ef2..064ee6c 100644
---- a/src/src/acl.c
-+++ b/src/src/acl.c
-@@ -1803,27 +1803,27 @@ switch(vp->value)
-     test whether it was successful or not. (This is for optional verification; for
-     mandatory verification, the connection doesn't last this long.) */
--      if (tls_in.certificate_verified) return OK;
--      *user_msgptr = US"no verified certificate";
--      return FAIL;
-+    if (tls_in.certificate_verified) return OK;
-+    *user_msgptr = US"no verified certificate";
-+    return FAIL;
-   case VERIFY_HELO:
-     /* We can test the result of optional HELO verification that might have
-     occurred earlier. If not, we can attempt the verification now. */
--      if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
--      return helo_verified? OK : FAIL;
-+    if (!helo_verified && !helo_verify_failed) smtp_verify_helo();
-+    return helo_verified? OK : FAIL;
-   case VERIFY_CSA:
-     /* Do Client SMTP Authorization checks in a separate function, and turn the
-     result code into user-friendly strings. */
--      rc = acl_verify_csa(list);
--      *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s",
-+    rc = acl_verify_csa(list);
-+    *log_msgptr = *user_msgptr = string_sprintf("client SMTP authorization %s",
-                                               csa_reason_string[rc]);
--      csa_status = csa_status_string[rc];
--      DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status);
--      return csa_return_code[rc];
-+    csa_status = csa_status_string[rc];
-+    DEBUG(D_acl) debug_printf("CSA result %s\n", csa_status);
-+    return csa_return_code[rc];
-   case VERIFY_HDR_SYNTAX:
-     /* Check that all relevant header lines have the correct syntax. If there is
-@@ -1832,8 +1832,11 @@ switch(vp->value)
-     always). */
-     rc = verify_check_headers(log_msgptr);
--    if (rc != OK && smtp_return_error_details && *log_msgptr != NULL)
--      *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
-+    if (rc != OK && *log_msgptr)
-+      if (smtp_return_error_details)
-+      *user_msgptr = string_sprintf("Rejected after DATA: %s", *log_msgptr);
-+      else
-+      acl_verify_message = *log_msgptr;
-     return rc;
-   case VERIFY_HDR_NAMES_ASCII:
-@@ -3788,7 +3791,8 @@ for (; cb != NULL; cb = cb->next)
-     case ACLC_VERIFY:
-     rc = acl_verify(where, addr, arg, user_msgptr, log_msgptr, basic_errno);
--    acl_verify_message = *user_msgptr;
-+    if (*user_msgptr)
-+      acl_verify_message = *user_msgptr;
-     if (verb == ACL_WARN) *user_msgptr = NULL;
-     break;
-
-commit c8899c20aa08c9ae6a4c291aad23ba90512bebe4
-Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date:   Tue Aug 25 10:36:27 2015 +0100
-
-    Close logs after daemon-process exceptional write.  Bug 728
-
-diff --git a/src/src/daemon.c b/src/src/daemon.c
-index 2d10387..e1ff9a1 100644
---- a/src/src/daemon.c
-+++ b/src/src/daemon.c
-@@ -735,6 +735,7 @@ else (void)close(dup_accept_socket);
- /* Release any store used in this process, including the store used for holding
- the incoming host address and an expanded active_hostname. */
-+log_close_all();
- store_reset(reset_point);
- sender_host_address = NULL;
- }
-
-commit f38917cc94ab337c15ff70c254dd564ee2dcafe7
-Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date:   Tue Sep 8 23:05:20 2015 +0100
-
-        Capture substrings in ACL regex= .  Bug 425.
-
-diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
-index c33e098..596e651 100644
---- a/src/src/config.h.defaults
-+++ b/src/src/config.h.defaults
-@@ -116,6 +116,8 @@ it's a default value. */
- #define RADIUS_CONFIG_FILE
- #define RADIUS_LIB_TYPE
-+#define REGEX_VARS                 9
-+
- #define ROUTER_ACCEPT
- #define ROUTER_DNSLOOKUP
- #define ROUTER_IPLITERAL
-diff --git a/src/src/exim.c b/src/src/exim.c
-index d7cb5d8..999b94c 100644
---- a/src/src/exim.c
-+++ b/src/src/exim.c
-@@ -1753,6 +1753,8 @@ regex_whitelisted_macro =
-   regex_must_compile(US"^[A-Za-z0-9_/.-]*$", FALSE, TRUE);
- #endif
-+for (i = 0; i < REGEX_VARS; i++) regex_vars[i] = NULL;
-+
- /* If the program is called as "mailq" treat it as equivalent to "exim -bp";
- this seems to be a generally accepted convention, since one finds symbolic
-diff --git a/src/src/expand.c b/src/src/expand.c
-index 89e0ac7..1bff521 100644
---- a/src/src/expand.c
-+++ b/src/src/expand.c
-@@ -1726,7 +1726,14 @@ if (Ustrncmp(name, "auth", 4) == 0)
-   uschar *endptr;
-   int n = Ustrtoul(name + 4, &endptr, 10);
-   if (*endptr == 0 && n != 0 && n <= AUTH_VARS)
--    return (auth_vars[n-1] == NULL)? US"" : auth_vars[n-1];
-+    return !auth_vars[n-1] ? US"" : auth_vars[n-1];
-+  }
-+else if (Ustrncmp(name, "regex", 5) == 0)
-+  {
-+  uschar *endptr;
-+  int n = Ustrtoul(name + 5, &endptr, 10);
-+  if (*endptr == 0 && n != 0 && n <= REGEX_VARS)
-+    return !regex_vars[n-1] ? US"" : regex_vars[n-1];
-   }
+   if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength,
+       &data, &search_error_message, &do_cache) == DEFER)
+-    {
+     search_find_defer = TRUE;
+-    }
  
- /* For all other variables, search the table */
-diff --git a/src/src/globals.c b/src/src/globals.c
-index 4188b4d..8445f00 100644
---- a/src/src/globals.c
-+++ b/src/src/globals.c
-@@ -1090,8 +1090,9 @@ const pcre *regex_From         = NULL;
- const pcre *regex_IGNOREQUOTA  = NULL;
- const pcre *regex_PIPELINING   = NULL;
- const pcre *regex_SIZE         = NULL;
--const pcre *regex_smtp_code    = NULL;
- const pcre *regex_ismsgid      = NULL;
-+const pcre *regex_smtp_code    = NULL;
-+uschar *regex_vars[REGEX_VARS];
- #ifdef WHITELIST_D_MACROS
- const pcre *regex_whitelisted_macro = NULL;
- #endif
-diff --git a/src/src/globals.h b/src/src/globals.h
-index 978a4cc..3c69e43 100644
---- a/src/src/globals.h
-+++ b/src/src/globals.h
-@@ -717,8 +717,9 @@ extern const pcre  *regex_From;        /* For recognizing "From_" lines */
- extern const pcre  *regex_IGNOREQUOTA; /* For recognizing IGNOREQUOTA (LMTP) */
- extern const pcre  *regex_PIPELINING;  /* For recognizing PIPELINING */
- extern const pcre  *regex_SIZE;        /* For recognizing SIZE settings */
--extern const pcre  *regex_smtp_code;   /* For recognizing SMTP codes */
- extern const pcre  *regex_ismsgid;     /* Compiled r.e. for message it */
-+extern const pcre  *regex_smtp_code;   /* For recognizing SMTP codes */
-+extern uschar *regex_vars[];           /* $regexN variables */
- #ifdef WHITELIST_D_MACROS
- extern const pcre  *regex_whitelisted_macro; /* For -D macro values */
- #endif
-diff --git a/src/src/regex.c b/src/src/regex.c
-index ed73b6e..93422fa 100644
---- a/src/src/regex.c
-+++ b/src/src/regex.c
-@@ -25,109 +25,120 @@ uschar regex_match_string_buffer[1024];
- extern FILE *mime_stream;
- extern uschar *mime_current_boundary;
--int
--regex(const uschar **listptr)
-+static pcre_list *
-+compile(const uschar * list)
- {
-   int sep = 0;
--  const uschar *list = *listptr;
-   uschar *regex_string;
-   uschar regex_string_buffer[1024];
--  unsigned long mbox_size;
--  FILE *mbox_file;
--  pcre *re;
--  pcre_list *re_list_head = NULL;
--  pcre_list *re_list_item;
-   const char *pcre_error;
-   int pcre_erroffset;
-+  pcre_list *re_list_head = NULL;
-+  pcre_list *ri;
-+
-+  /* precompile our regexes */
-+  while ((regex_string = string_nextinlist(&list, &sep,
-+                                           regex_string_buffer,
-+                                           sizeof(regex_string_buffer))) != NULL) {
-+    pcre *re;
-+
-+    /* parse option */
-+    if ( (strcmpic(regex_string,US"false") == 0) ||
-+         (Ustrcmp(regex_string,"0") == 0) )
-+      continue;                               /* explicitly no matching */
-+
-+    /* compile our regular expression */
-+    if (!(re = pcre_compile( CS regex_string,
-+                       0, &pcre_error, &pcre_erroffset, NULL ))) {
-+      log_write(0, LOG_MAIN,
-+           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.",
-+         regex_string, pcre_error, pcre_erroffset);
-+      continue;
-+    }
-+
-+    ri = store_get(sizeof(pcre_list));
-+    ri->re = re;
-+    ri->pcre_text = string_copy(regex_string);
-+    ri->next = re_list_head;
-+    re_list_head = ri;
-+  }
-+  return re_list_head;
-+}
-+
-+static int
-+matcher(pcre_list * re_list_head, uschar * linebuffer, int len)
-+{
-+  pcre_list * ri;
-+
-+  for(ri = re_list_head; ri; ri = ri->next)
-+    {
-+    int ovec[3*(REGEX_VARS+1)];
-+    int n, nn;
-+
-+    /* try matcher on the line */
-+    n = pcre_exec(ri->re, NULL,
-+       CS linebuffer, len, 0, 0,
-+       ovec, nelem(ovec));
-+    if (n > 0)
+   /* A record that has been found is now in data, which is either NULL
+   or points to a bit of dynamic store. Cache the result of the lookup if
+@@ -524,10 +532,22 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL)
+   else if (do_cache)
+     {
+     int len = keylength + 1;
+-    t = store_get(sizeof(tree_node) + len);
+-    memcpy(t->name, keystring, len);
+-    t->data.ptr = data;
+-    tree_insertnode(&c->item_cache, t);
++
++    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;
++      }
++    else
 +      {
-+      Ustrncpy(regex_match_string_buffer, ri->pcre_text, 1023);
-+      regex_match_string = regex_match_string_buffer;
-+
-+      for (nn = 1; nn < n; nn++)
-+      regex_vars[nn-1] =
-+        string_copyn(linebuffer + ovec[nn*2], ovec[nn*2+1] - ovec[nn*2]);
-+
-+      return OK;
++      t = store_get(sizeof(tree_node) + len + sizeof(expiring_data));
++      e = (expiring_data *)((char *)t + sizeof(tree_node) + len);
++      e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache;
++      e->ptr = data;
++      memcpy(t->name, keystring, len);
++      t->data.ptr = e;
++      tree_insertnode(&c->item_cache, t);
 +      }
-+    }
-+  return FAIL;
-+}
-+
-+int
-+regex(const uschar **listptr)
-+{
-+  unsigned long mbox_size;
-+  FILE *mbox_file;
-+  pcre_list *re_list_head;
-   uschar *linebuffer;
-   long f_pos = 0;
-+  int ret = FAIL;
-   /* reset expansion variable */
-   regex_match_string = NULL;
--  if (mime_stream == NULL) {
--    /* We are in the DATA ACL */
-+  if (mime_stream == NULL) {                  /* We are in the DATA ACL */
-     mbox_file = spool_mbox(&mbox_size, NULL);
--    if (mbox_file == NULL) {
--      /* error while spooling */
-+    if (mbox_file == NULL) {                  /* error while spooling */
-       log_write(0, LOG_MAIN|LOG_PANIC,
-              "regex acl condition: error while creating mbox spool file");
-       return DEFER;
--    };
-+    }
-   }
-   else {
-     f_pos = ftell(mime_stream);
-     mbox_file = mime_stream;
--  };
-+  }
-   /* precompile our regexes */
--  while ((regex_string = string_nextinlist(&list, &sep,
--                                           regex_string_buffer,
--                                           sizeof(regex_string_buffer))) != NULL) {
--
--    /* parse option */
--    if ( (strcmpic(regex_string,US"false") == 0) ||
--         (Ustrcmp(regex_string,"0") == 0) ) {
--      /* explicitly no matching */
--      continue;
--    };
--
--    /* compile our regular expression */
--    re = pcre_compile( CS regex_string,
--                       0,
--                       &pcre_error,
--                       &pcre_erroffset,
--                       NULL );
--
--    if (re == NULL) {
--      log_write(0, LOG_MAIN,
--           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
--      continue;
--    }
--    else {
--      re_list_item = store_get(sizeof(pcre_list));
--      re_list_item->re = re;
--      re_list_item->pcre_text = string_copy(regex_string);
--      re_list_item->next = re_list_head;
--      re_list_head = re_list_item;
--    };
--  };
--
--  /* no regexes -> nothing to do */
--  if (re_list_head == NULL) {
--    return FAIL;
--  };
-+  if (!(re_list_head = compile(*listptr)))
-+    return FAIL;                      /* no regexes -> nothing to do */
-   /* match each line against all regexes */
-   linebuffer = store_get(32767);
-   while (fgets(CS linebuffer, 32767, mbox_file) != NULL) {
--    if ( (mime_stream != NULL) && (mime_current_boundary != NULL) ) {
--      /* check boundary */
--      if (Ustrncmp(linebuffer,"--",2) == 0) {
--        if (Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
--          /* found boundary */
--          break;
--      };
--    };
--    re_list_item = re_list_head;
--    do {
--      /* try matcher on the line */
--      if (pcre_exec(re_list_item->re, NULL, CS linebuffer,
--      (int)Ustrlen(linebuffer), 0, 0, NULL, 0) >= 0) {
--        Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
--        regex_match_string = regex_match_string_buffer;
--        if (mime_stream == NULL)
--          (void)fclose(mbox_file);
--        else {
--          clearerr(mime_stream);
--          fseek(mime_stream,f_pos,SEEK_SET);
--        };
--        return OK;
--      };
--      re_list_item = re_list_item->next;
--    } while (re_list_item != NULL);
--  };
-+    if (  mime_stream && mime_current_boundary                /* check boundary */
-+       && Ustrncmp(linebuffer,"--",2) == 0
-+       && Ustrncmp((linebuffer+2),mime_current_boundary,Ustrlen(mime_current_boundary)) == 0)
-+      break;                                          /* found boundary */
-+
-+    if ((ret = matcher(re_list_head, linebuffer, (int)Ustrlen(linebuffer))) == OK)
-+      goto done;
-+  }
-+  /* no matches ... */
-+
-+done:
-   if (mime_stream == NULL)
-     (void)fclose(mbox_file);
-   else {
-@@ -135,67 +146,25 @@ regex(const uschar **listptr)
-     fseek(mime_stream,f_pos,SEEK_SET);
-   };
--  /* no matches ... */
--  return FAIL;
-+  return ret;
- }
+     }
  
+   /* If caching was disabled, empty the cache tree. We just set the cache
+@@ -540,34 +560,19 @@ if ((t = tree_search(c->item_cache, keystring)) == NULL)
+     }
+   }
  
- int
- mime_regex(const uschar **listptr)
- {
--  int sep = 0;
--  const uschar *list = *listptr;
--  uschar *regex_string;
--  uschar regex_string_buffer[1024];
--  pcre *re;
-   pcre_list *re_list_head = NULL;
--  pcre_list *re_list_item;
--  const char *pcre_error;
--  int pcre_erroffset;
-   FILE *f;
-   uschar *mime_subject = NULL;
-   int mime_subject_len = 0;
-+  int ret;
-   /* reset expansion variable */
-   regex_match_string = NULL;
-   /* precompile our regexes */
--  while ((regex_string = string_nextinlist(&list, &sep,
--                                           regex_string_buffer,
--                                           sizeof(regex_string_buffer))) != NULL) {
+-/* Data was in the cache already; set the pointer from the tree node */
 -
--    /* parse option */
--    if ( (strcmpic(regex_string,US"false") == 0) ||
--         (Ustrcmp(regex_string,"0") == 0) ) {
--      /* explicitly no matching */
--      continue;
--    };
+-else
+-  {
+-  data = US t->data.ptr;
+-  DEBUG(D_lookup) debug_printf("cached data used for lookup of %s%s%s\n",
+-    keystring,
+-    (filename == NULL)? US"" : US"\n  in ",
+-    (filename == NULL)? US"" : filename);
+-  }
 -
--    /* compile our regular expression */
--    re = pcre_compile( CS regex_string,
--                       0,
--                       &pcre_error,
--                       &pcre_erroffset,
--                       NULL );
+-/* Debug: output the answer */
 -
--    if (re == NULL) {
--      log_write(0, LOG_MAIN,
--           "regex acl condition warning - error in regex '%s': %s at offset %d, skipped.", regex_string, pcre_error, pcre_erroffset);
--      continue;
+ DEBUG(D_lookup)
+   {
+-  if (data == NULL)
+-    {
+-    if (search_find_defer) debug_printf("lookup deferred: %s\n",
+-      search_error_message);
+-    else debug_printf("lookup failed\n");
 -    }
--    else {
--      re_list_item = store_get(sizeof(pcre_list));
--      re_list_item->re = re;
--      re_list_item->pcre_text = string_copy(regex_string);
--      re_list_item->next = re_list_head;
--      re_list_head = re_list_item;
--    };
--  };
--
--  /* no regexes -> nothing to do */
--  if (re_list_head == NULL) {
--    return FAIL;
--  };
-+  if (!(re_list_head = compile(*listptr)))
-+    return FAIL;                      /* no regexes -> nothing to do */
-   /* check if the file is already decoded */
-   if (mime_decoded_filename == NULL) {
-@@ -207,43 +176,25 @@ mime_regex(const uschar **listptr)
-       log_write(0, LOG_MAIN,
-            "mime_regex acl condition warning - could not decode MIME part to file.");
-       return DEFER;
--    };
--  };
--
-+    }
-+  }
+-  else debug_printf("lookup yielded: %s\n", data);
++  if (data)
++    debug_printf("lookup yielded: %s\n", data);
++  else if (search_find_defer)
++    debug_printf("lookup deferred: %s\n", search_error_message);
++  else debug_printf("lookup failed\n");
+   }
  
-   /* open file */
--  f = fopen(CS mime_decoded_filename, "rb");
--  if (f == NULL) {
--    /* open failed */
-+  if (!(f = fopen(CS mime_decoded_filename, "rb"))) {
-     log_write(0, LOG_MAIN,
--         "mime_regex acl condition warning - can't open '%s' for reading.", mime_decoded_filename);
-+         "mime_regex acl condition warning - can't open '%s' for reading.",
-+       mime_decoded_filename);
-     return DEFER;
--  };
-+  }
+ /* Return it in new dynamic store in the regular pool */
  
-   /* get 32k memory */
-   mime_subject = (uschar *)store_get(32767);
--  /* read max 32k chars from file */
-   mime_subject_len = fread(mime_subject, 1, 32766, f);
--  re_list_item = re_list_head;
--  do {
--    /* try matcher on the mmapped file */
--    debug_printf("Matching '%s'\n", re_list_item->pcre_text);
--    if (pcre_exec(re_list_item->re, NULL, CS mime_subject,
--                  mime_subject_len, 0, 0, NULL, 0) >= 0) {
--      Ustrncpy(regex_match_string_buffer, re_list_item->pcre_text, 1023);
--      regex_match_string = regex_match_string_buffer;
--      (void)fclose(f);
--      return OK;
--    };
--    re_list_item = re_list_item->next;
--  } while (re_list_item != NULL);
--
-+  ret = matcher(re_list_head, mime_subject, mime_subject_len);
-   (void)fclose(f);
--
--  /* no matches ... */
--  return FAIL;
-+  return ret;
+ store_pool = old_pool;
+-return (data == NULL)? NULL : string_copy(data);
++return data ? string_copy(data) : NULL;
  }
  
- #endif /* WITH_CONTENT_SCAN */
-
-commit 895fbaf26d3450d4eeacbad8fe04c328a77645f0
-Author: Jeremy Harris <jgh146exb@wizmail.org>
-Date:   Wed Sep 9 16:03:38 2015 +0100
-
-    DSN: Under EXPERIMENTAL_DSN_INFO add extras to bounce messages.  Bug 1686
-
-diff --git a/src/src/config.h.defaults b/src/src/config.h.defaults
-index 596e651..6af3b4d 100644
---- a/src/src/config.h.defaults
-+++ b/src/src/config.h.defaults
-@@ -172,6 +172,7 @@ it's a default value. */
- #define EXPERIMENTAL_BRIGHTMAIL
- #define EXPERIMENTAL_DANE
- #define EXPERIMENTAL_DCC
-+#define EXPERIMENTAL_DSN_INFO
- #define EXPERIMENTAL_DMARC
- #define EXPERIMENTAL_EVENT
- #define EXPERIMENTAL_INTERNATIONAL
-diff --git a/src/src/deliver.c b/src/src/deliver.c
-index b5aa9b9..3f22dc9 100644
---- a/src/src/deliver.c
-+++ b/src/src/deliver.c
-@@ -3223,41 +3223,56 @@ while (!done)
-       break;
-       }
--    addr->transport_return = *ptr++;
--    addr->special_action = *ptr++;
--    memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno));
--    ptr += sizeof(addr->basic_errno);
--    memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno));
--    ptr += sizeof(addr->more_errno);
--    memcpy(&(addr->flags), ptr, sizeof(addr->flags));
--    ptr += sizeof(addr->flags);
--    addr->message = (*ptr)? string_copy(ptr) : NULL;
--    while(*ptr++);
--    addr->user_message = (*ptr)? string_copy(ptr) : NULL;
--    while(*ptr++);
--
--    /* Always two strings for host information, followed by the port number and DNSSEC mark */
--
--    if (*ptr != 0)
-+    switch (subid)
-       {
--      h = store_get(sizeof(host_item));
--      h->name = string_copy(ptr);
--      while (*ptr++);
--      h->address = string_copy(ptr);
--      while(*ptr++);
--      memcpy(&(h->port), ptr, sizeof(h->port));
--      ptr += sizeof(h->port);
--      h->dnssec = *ptr == '2' ? DS_YES
--              : *ptr == '1' ? DS_NO
--              : DS_UNK;
--      ptr++;
--      addr->host_used = h;
--      }
--    else ptr++;
-+#ifdef EXPERIMENTAL_DSN_INFO
-+      case '1':       /* must arrive before A0, and applies to that addr */
-+                      /* Two strings: smtp_greeting and helo_response */
-+      addr->smtp_greeting = string_copy(ptr);
-+      while(*ptr++);
-+      addr->helo_response = string_copy(ptr);
-+      while(*ptr++);
-+      break;
-+#endif
--    /* Finished with this address */
-+      case '0':
-+      addr->transport_return = *ptr++;
-+      addr->special_action = *ptr++;
-+      memcpy(&(addr->basic_errno), ptr, sizeof(addr->basic_errno));
-+      ptr += sizeof(addr->basic_errno);
-+      memcpy(&(addr->more_errno), ptr, sizeof(addr->more_errno));
-+      ptr += sizeof(addr->more_errno);
-+      memcpy(&(addr->flags), ptr, sizeof(addr->flags));
-+      ptr += sizeof(addr->flags);
-+      addr->message = (*ptr)? string_copy(ptr) : NULL;
-+      while(*ptr++);
-+      addr->user_message = (*ptr)? string_copy(ptr) : NULL;
-+      while(*ptr++);
--    addr = addr->next;
-+      /* Always two strings for host information, followed by the port number and DNSSEC mark */
-+
-+      if (*ptr != 0)
-+        {
-+        h = store_get(sizeof(host_item));
-+        h->name = string_copy(ptr);
-+        while (*ptr++);
-+        h->address = string_copy(ptr);
-+        while(*ptr++);
-+        memcpy(&(h->port), ptr, sizeof(h->port));
-+        ptr += sizeof(h->port);
-+        h->dnssec = *ptr == '2' ? DS_YES
-+                  : *ptr == '1' ? DS_NO
-+                  : DS_UNK;
-+        ptr++;
-+        addr->host_used = h;
-+        }
-+      else ptr++;
-+
-+      /* Finished with this address */
-+
-+      addr = addr->next;
-+      break;
-+      }
-     break;
-     /* Local interface address/port */
-@@ -4423,7 +4438,6 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
-       for (r = addr->retries; r != NULL; r = r->next)
-         {
--        uschar *ptr;
-         sprintf(CS big_buffer, "%c%.500s", r->flags, r->key);
-         ptr = big_buffer + Ustrlen(big_buffer+2) + 3;
-         memcpy(ptr, &(r->basic_errno), sizeof(r->basic_errno));
-@@ -4438,11 +4452,31 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
-         rmt_dlv_checked_write(fd, 'R', '0', big_buffer, ptr - big_buffer);
-         }
--      /* The rest of the information goes in an 'A' item. */
-+#ifdef EXPERIMENTAL_DSN_INFO
-+/*um, are they really per-addr?  Other per-conn stuff is not (auth, tls).  But host_used is! */
-+      if (addr->smtp_greeting)
-+      {
-+      ptr = big_buffer;
-+      DEBUG(D_deliver) debug_printf("smtp_greeting '%s'\n", addr->smtp_greeting);
-+        sprintf(CS ptr, "%.128s", addr->smtp_greeting);
-+        while(*ptr++);
-+      if (addr->helo_response)
-+        {
-+        DEBUG(D_deliver) debug_printf("helo_response '%s'\n", addr->helo_response);
-+        sprintf(CS ptr, "%.128s", addr->helo_response);
-+        while(*ptr++);
-+        }
-+      else
-+        *ptr++ = '\0';
-+        rmt_dlv_checked_write(fd, 'A', '1', big_buffer, ptr - big_buffer);
-+      }
-+#endif
-+
-+      /* The rest of the information goes in an 'A0' item. */
--      ptr = big_buffer + 2;
-       sprintf(CS big_buffer, "%c%c", addr->transport_return,
-         addr->special_action);
-+      ptr = big_buffer + 2;
-       memcpy(ptr, &(addr->basic_errno), sizeof(addr->basic_errno));
-       ptr += sizeof(addr->basic_errno);
-       memcpy(ptr, &(addr->more_errno), sizeof(addr->more_errno));
-@@ -4480,7 +4514,11 @@ for (delivery_count = 0; addr_remote != NULL; delivery_count++)
-       }
-     /* Local interface address/port */
-+#ifdef EXPERIMENTAL_DSN_INFO
-+    if (sending_ip_address)
-+#else
-     if (LOGGING(incoming_interface) && sending_ip_address)
-+#endif
-       {
-       uschar * ptr = big_buffer;
-       sprintf(CS ptr, "%.128s", sending_ip_address);
-@@ -7209,16 +7247,32 @@ wording. */
-  
-       for (addr = handled_addr; addr; addr = addr->next)
-         {
-+      host_item * hu;
-         fprintf(f, "Action: failed\n"
-           "Final-Recipient: rfc822;%s\n"
-           "Status: 5.0.0\n",
-           addr->address);
--        if (addr->host_used && addr->host_used->name)
--          {
--          fprintf(f, "Remote-MTA: dns; %s\n",
--          addr->host_used->name);
--          print_dsn_diagnostic_code(addr, f);
--          }
-+        if ((hu = addr->host_used) && hu->name)
-+        {
-+        const uschar * s;
-+        fprintf(f, "Remote-MTA: dns; %s\n",
-+          hu->name);
-+#ifdef EXPERIMENTAL_DSN_INFO
-+        if (hu->address)
-+          {
-+          uschar * p = hu->port == 25
-+            ? US"" : string_sprintf(":%d", hu->port);
-+          fprintf(f, "Remote-MTA: X-ip; [%s]%s\n", hu->address, p);
-+          }
-+        if ((s = addr->smtp_greeting) && *s)
-+          fprintf(f, "X-Remote-MTA-smtp-greeting: X-str; %s\n", s);
-+        if ((s = addr->helo_response) && *s)
-+          fprintf(f, "X-Remote-MTA-helo-response: X-str; %s\n", s);
-+        if ((s = addr->message) && *s)
-+          fprintf(f, "X-Exim-Diagnostic: X-str; %s\n", s);
-+#endif
-+        print_dsn_diagnostic_code(addr, f);
-+        }
-       fputc('\n', f);
-         }
  
-diff --git a/src/src/exim.c b/src/src/exim.c
-index 999b94c..084d649 100644
---- a/src/src/exim.c
-+++ b/src/src/exim.c
-@@ -847,6 +847,12 @@ fprintf(f, "Support for:");
- #ifdef EXPERIMENTAL_DMARC
-   fprintf(f, " Experimental_DMARC");
- #endif
-+#ifdef EXPERIMENTAL_DSN_INFO
-+  fprintf(f, " Experimental_DSN_info");
-+#endif
-+#ifdef EXPERIMENTAL_INTERNATIONAL
-+  fprintf(f, " Experimental_International");
-+#endif
- #ifdef EXPERIMENTAL_PROXY
-   fprintf(f, " Experimental_Proxy");
- #endif
-@@ -859,9 +865,6 @@ fprintf(f, "Support for:");
- #ifdef EXPERIMENTAL_SOCKS
-   fprintf(f, " Experimental_SOCKS");
- #endif
--#ifdef EXPERIMENTAL_INTERNATIONAL
--  fprintf(f, " Experimental_International");
--#endif
- fprintf(f, "\n");
- fprintf(f, "Lookups (built-in):");
-diff --git a/src/src/globals.c b/src/src/globals.c
-index 8445f00..f3b6791 100644
---- a/src/src/globals.c
-+++ b/src/src/globals.c
-@@ -354,13 +354,17 @@ address_item address_defaults = {
-   NULL,                 /* return_filename */
-   NULL,                 /* self_hostname */
-   NULL,                 /* shadow_message */
--  #ifdef SUPPORT_TLS
-+#ifdef SUPPORT_TLS
-   NULL,                 /* cipher */
-   NULL,                       /* ourcert */
-   NULL,                       /* peercert */
-   NULL,                 /* peerdn */
-   OCSP_NOT_REQ,         /* ocsp */
--  #endif
-+#endif
-+#ifdef EXPERIMENTAL_DSN_INFO
-+  NULL,                       /* smtp_greeting */
-+  NULL,                       /* helo_response */
-+#endif
-   NULL,                       /* authenticator */
-   NULL,                       /* auth_id */
-   NULL,                       /* auth_sndr */
 diff --git a/src/src/structs.h b/src/src/structs.h
-index 438b521..db9e843 100644
+index 6f143d6..d9da38b 100644
 --- a/src/src/structs.h
 +++ b/src/src/structs.h
-@@ -561,13 +561,18 @@ typedef struct address_item {
-   uschar *self_hostname;          /* after self=pass */
-   uschar *shadow_message;         /* info about shadow transporting */
--  #ifdef SUPPORT_TLS
-+#ifdef SUPPORT_TLS
-   uschar *cipher;                 /* Cipher used for transport */
-   void   *ourcert;                /* Certificate offered to peer, binary */
-   void   *peercert;               /* Certificate from peer, binary */
-   uschar *peerdn;                 /* DN of server's certificate */
-   int    ocsp;                          /* OCSP status of peer cert */
--  #endif
-+#endif
-+
-+#ifdef EXPERIMENTAL_DSN_INFO
-+  const uschar *smtp_greeting;          /* peer self-identification */
-+  const uschar *helo_response;          /* peer message */
-+#endif
-   uschar *authenticator;        /* auth driver name used by transport */
-   uschar *auth_id;              /* auth "login" name used by transport */
-diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
-index c93f2ef..ac40460 100644
---- a/src/src/transports/smtp.c
-+++ b/src/src/transports/smtp.c
-@@ -440,6 +440,8 @@ Arguments:
-   rc             to put in each address's transport_return field
-   pass_message   if TRUE, set the "pass message" flag in the address
-   host           if set, mark addrs as having used this host
-+  smtp_greeting  from peer
-+  helo_response  from peer
- If errno_value has the special value ERRNO_CONNECTTIMEOUT, ETIMEDOUT is put in
- the errno field, and RTEF_CTOUT is ORed into the more_errno field, to indicate
-@@ -450,7 +452,11 @@ Returns:       nothing
- static void
- set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc,
--  BOOL pass_message, host_item * host)
-+  BOOL pass_message, host_item * host
-+#ifdef EXPERIMENTAL_DSN_INFO
-+  , const uschar * smtp_greeting, const uschar * helo_response
-+#endif
-+  )
- {
- address_item *addr;
- int orvalue = 0;
-@@ -459,7 +465,7 @@ if (errno_value == ERRNO_CONNECTTIMEOUT)
-   errno_value = ETIMEDOUT;
-   orvalue = RTEF_CTOUT;
-   }
--for (addr = addrlist; addr != NULL; addr = addr->next)
-+for (addr = addrlist; addr; addr = addr->next)
-   if (addr->transport_return >= PENDING)
-     {
-     addr->basic_errno = errno_value;
-@@ -471,10 +477,31 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
-       }
-     addr->transport_return = rc;
-     if (host)
-+      {
-       addr->host_used = host;
-+#ifdef EXPERIMENTAL_DSN_INFO
-+      if (smtp_greeting)
-+      {uschar * s = Ustrchr(smtp_greeting, '\n'); if (s) *s = '\0';}
-+      addr->smtp_greeting = smtp_greeting;
-+
-+      if (helo_response)
-+      {uschar * s = Ustrchr(helo_response, '\n'); if (s) *s = '\0';}
-+      addr->helo_response = helo_response;
-+#endif
-+      }
-     }
- }
-+static void
-+set_errno_nohost(address_item *addrlist, int errno_value, uschar *msg, int rc,
-+  BOOL pass_message)
-+{
-+set_errno(addrlist, errno_value, msg, rc, pass_message, NULL
-+#ifdef EXPERIMENTAL_DSN_INFO
-+        , NULL, NULL
-+#endif
-+        );
-+}
- /*************************************************
-@@ -847,7 +874,7 @@ while (count-- > 0)
-     {
-     uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
-                         transport_rcpt_address(addr, include_affixes));
--    set_errno(addrlist, ETIMEDOUT, message, DEFER, FALSE, NULL);
-+    set_errno_nohost(addrlist, ETIMEDOUT, message, DEFER, FALSE);
-     retry_add_item(addr, addr->address_retry_key, 0);
-     update_waiting = FALSE;
-     return -1;
-@@ -1096,8 +1123,8 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
-         /* Internal problem, message in buffer. */
-         case ERROR:
--        set_errno(addrlist, ERRNO_AUTHPROB, string_copy(buffer),
--                  DEFER, FALSE, NULL);
-+        set_errno_nohost(addrlist, ERRNO_AUTHPROB, string_copy(buffer),
-+                  DEFER, FALSE);
-         return ERROR;
-         }
-@@ -1111,9 +1138,9 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
- if (require_auth == OK && !smtp_authenticated)
-   {
--  set_errno(addrlist, ERRNO_AUTHFAIL,
-+  set_errno_nohost(addrlist, ERRNO_AUTHFAIL,
-     string_sprintf("authentication required but %s", fail_reason), DEFER,
--    FALSE, NULL);
-+    FALSE);
-   return DEFER;
-   }
-@@ -1152,7 +1179,7 @@ if (ob->authenticated_sender != NULL)
-       {
-       uschar *message = string_sprintf("failed to expand "
-         "authenticated_sender: %s", expand_string_message);
--      set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL);
-+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
-       return TRUE;
-       }
-     }
-@@ -1381,6 +1408,10 @@ smtp_outblock outblock;
- int max_rcpt = tblock->max_addresses;
- uschar *igquotstr = US"";
-+#ifdef EXPERIMENTAL_DSN_INFO
-+uschar *smtp_greeting = NULL;
-+uschar *helo_response = NULL;
-+#endif
- uschar *helo_data = NULL;
- uschar *message = NULL;
-@@ -1432,8 +1463,8 @@ tls_modify_variables(&tls_out);
- #ifndef SUPPORT_TLS
- if (smtps)
-   {
--  set_errno(addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
--          DEFER, FALSE, NULL);
-+  set_errno_nohost(addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
-+          DEFER, FALSE);
-   return ERROR;
-   }
- #endif
-@@ -1450,8 +1481,8 @@ if (continue_hostname == NULL)
-   if (inblock.sock < 0)
-     {
--    set_errno(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno,
--      NULL, DEFER, FALSE, NULL);
-+    set_errno_nohost(addrlist, (errno == ETIMEDOUT)? ERRNO_CONNECTTIMEOUT : errno,
-+      NULL, DEFER, FALSE);
-     return DEFER;
-     }
-@@ -1469,18 +1500,18 @@ if (continue_hostname == NULL)
-       && dane_required        /* do not error on only dane-requested */
-       )
-       {
--      set_errno(addrlist, ERRNO_DNSDEFER,
-+      set_errno_nohost(addrlist, ERRNO_DNSDEFER,
-         string_sprintf("DANE error: tlsa lookup %s",
-           rc == DEFER ? "DEFER" : "FAIL"),
--        rc, FALSE, NULL);
-+        rc, FALSE);
-       return rc;
-       }
-       }
-     else if (dane_required)
-       {
--      set_errno(addrlist, ERRNO_DNSDEFER,
-+      set_errno_nohost(addrlist, ERRNO_DNSDEFER,
-       string_sprintf("DANE error: %s lookup not DNSSEC", host->name),
--      FAIL, FALSE, NULL);
-+      FAIL, FALSE);
-       return  FAIL;
-       }
+@@ -657,6 +657,16 @@ typedef struct tree_node {
+   uschar  name[1];                /* node name - variable length */
+ } tree_node;
++/* Structure for holding time-limited data such as DNS returns.
++We use this rather than extending tree_node to avoid wasting
++space for most tree use (variables...) at the cost of complexity
++for the lookups cache */
++
++typedef struct expiring_data {
++  time_t expiry;                /* if nonzero, data invalid after this time */
++  void   *ptr;                          /* pointer to data */
++} expiring_data;
++
+ /* Structure for holding the handle and the cached last lookup for searches.
+ This block is pointed to by the tree entry for the file. The file can get
+ closed if too many are opened at once. There is a LRU chain for deciding which
+@@ -676,6 +686,7 @@ uncompressed, but the data pointer is into the raw data. */
+ typedef struct {
+   uschar  name[DNS_MAXNAME];      /* domain name */
+   int     type;                   /* record type */
++  unsigned short ttl;           /* time-to-live, seconds */
+   int     size;                   /* size of data */
+   uschar *data;                   /* pointer to data */
+ } dns_record;
+diff --git a/src/src/verify.c b/src/src/verify.c
+index e00e7b9..d392fda 100644
+--- a/src/src/verify.c
++++ b/src/src/verify.c
+@@ -21,6 +21,7 @@ uschar ctbuffer[8192];
+ /* Structure for caching DNSBL lookups */
  
-@@ -1501,7 +1532,7 @@ if (continue_hostname == NULL)
-     if ((helo_data = string_domain_utf8_to_alabel(helo_data, &errstr)), errstr)
-       {
-       errstr = string_sprintf("failed to expand helo_data: %s", errstr);
--      set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL);
-+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
-       yield = DEFER;
-       goto SEND_QUIT;
-       }
-@@ -1514,8 +1545,12 @@ if (continue_hostname == NULL)
+ typedef struct dnsbl_cache_block {
++  time_t expiry;
+   dns_address *rhs;
+   uschar *text;
+   int rc;
+@@ -3584,21 +3585,37 @@ if (!string_format(query, sizeof(query), "%s.%s", prepend, domain))
  
-   if (!smtps)
-     {
--    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
--      ob->command_timeout)) goto RESPONSE_FAILED;
-+    BOOL good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
-+      '2', ob->command_timeout);
-+#ifdef EXPERIMENTAL_DSN_INFO
-+    smtp_greeting = string_copy(buffer);
-+#endif
-+    if (!good_response) goto RESPONSE_FAILED;
- #ifdef EXPERIMENTAL_EVENT
-       {
-@@ -1525,9 +1560,9 @@ if (continue_hostname == NULL)
-       s = event_raise(tblock->event_action, US"smtp:connect", buffer);
-       if (s)
-       {
--      set_errno(addrlist, ERRNO_EXPANDFAIL,
-+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL,
-         string_sprintf("deferred by smtp:connect event expansion: %s", s),
--        DEFER, FALSE, NULL);
-+        DEFER, FALSE);
-       yield = DEFER;
-       goto SEND_QUIT;
-       }
-@@ -1541,7 +1576,7 @@ if (continue_hostname == NULL)
-       {
-       uschar *message = string_sprintf("failed to expand helo_data: %s",
-         expand_string_message);
--      set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL);
-+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
-       yield = DEFER;
-       goto SEND_QUIT;
-       }
-@@ -1606,9 +1641,18 @@ goto SEND_QUIT;
-     if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
-            ob->command_timeout))
-       {
--      if (errno != 0 || buffer[0] == 0 || lmtp) goto RESPONSE_FAILED;
-+      if (errno != 0 || buffer[0] == 0 || lmtp)
-+      {
-+#ifdef EXPERIMENTAL_DSN_INFO
-+      helo_response = string_copy(buffer);
-+#endif
-+      goto RESPONSE_FAILED;
-+      }
-       esmtp = FALSE;
-       }
-+#ifdef EXPERIMENTAL_DSN_INFO
-+    helo_response = string_copy(buffer);
-+#endif
-     }
-   else
-     {
-@@ -1618,10 +1662,16 @@ goto SEND_QUIT;
+ /* Look for this query in the cache. */
  
-   if (!esmtp)
-     {
-+    BOOL good_response;
+-t = tree_search(dnsbl_cache, query);
++if (  (t = tree_search(dnsbl_cache, query))
++   && (cb = t->data.ptr)->expiry > time(NULL)
++   )
 +
-     if (smtp_write_command(&outblock, FALSE, "HELO %s\r\n", helo_data) < 0)
-       goto SEND_FAILED;
--    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
--      ob->command_timeout)) goto RESPONSE_FAILED;
-+    good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
-+      '2', ob->command_timeout);
-+#ifdef EXPERIMENTAL_DSN_INFO
-+    helo_response = string_copy(buffer);
-+#endif
-+    if (!good_response) goto RESPONSE_FAILED;
-     }
-   /* Set IGNOREQUOTA if the response to LHLO specifies support and the
-@@ -1671,6 +1721,11 @@ error messages. Note that smtp_use_size and smtp_use_pipelining will have been
- set from the command line if they were set in the process that passed the
- connection on. */
-+/*XXX continue case needs to propagate DSN_INFO, prob. in deliver.c
-+as the contine goes via transport_pass_socket() and doublefork and exec.
-+It does not wait.  Unclear how we keep separate host's responses
-+separate - we could match up by host ip+port as a bodge. */
++/* Previous lookup was cached */
 +
- else
-   {
-   inblock.sock = outblock.sock = fileno(stdin);
-@@ -1749,7 +1804,7 @@ if (  tls_offered
++  {
++  HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n");
++  }
  
-     /* TLS session is set up */
+ /* If not cached from a previous lookup, we must do a DNS lookup, and
+ cache the result in permanent memory. */
  
--    for (addr = addrlist; addr != NULL; addr = addr->next)
-+    for (addr = addrlist; addr; addr = addr->next)
-       if (addr->transport_return == PENDING_DEFER)
-         {
-         addr->cipher = tls_out.cipher;
-@@ -1774,6 +1829,8 @@ start of the Exim process (in exim.c). */
- if (tls_out.active >= 0)
+-if (t == NULL)
++else
    {
-   char *greeting_cmd;
-+  BOOL good_response;
++  uint ttl = 3600;
 +
-   if (helo_data == NULL)
-     {
-     helo_data = expand_string(ob->helo_data);
-@@ -1781,7 +1838,7 @@ if (tls_out.active >= 0)
-       {
-       uschar *message = string_sprintf("failed to expand helo_data: %s",
-         expand_string_message);
--      set_errno(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE, NULL);
-+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
-       yield = DEFER;
-       goto SEND_QUIT;
-       }
-@@ -1790,8 +1847,12 @@ if (tls_out.active >= 0)
-   /* For SMTPS we need to wait for the initial OK response. */
-   if (smtps)
-     {
--    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
--      ob->command_timeout)) goto RESPONSE_FAILED;
-+    good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
-+      '2', ob->command_timeout);
-+#ifdef EXPERIMENTAL_DSN_INFO
-+    smtp_greeting = string_copy(buffer);
-+#endif
-+    if (!good_response) goto RESPONSE_FAILED;
-     }
-   if (esmtp)
-@@ -1806,9 +1867,12 @@ if (tls_out.active >= 0)
-   if (smtp_write_command(&outblock, FALSE, "%s %s\r\n",
-         lmtp? "LHLO" : greeting_cmd, helo_data) < 0)
-     goto SEND_FAILED;
--  if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
--       ob->command_timeout))
--    goto RESPONSE_FAILED;
-+  good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
-+    '2', ob->command_timeout);
-+#ifdef EXPERIMENTAL_DSN_INFO
-+  helo_response = string_copy(buffer);
-+#endif
-+  if (!good_response) goto RESPONSE_FAILED;
-   }
- /* If the host is required to use a secure channel, ensure that we
-@@ -1935,8 +1999,8 @@ if (tblock->filter_command != NULL)
+   store_pool = POOL_PERM;
  
-   if (!rc)
-     {
--    set_errno(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
--      FALSE, NULL);
-+    set_errno_nohost(addrlist->next, addrlist->basic_errno, addrlist->message, DEFER,
-+      FALSE);
-     yield = ERROR;
-     goto SEND_QUIT;
-     }
-@@ -2065,7 +2129,7 @@ pending_MAIL = TRUE;     /* The block starts with MAIL */
-     {
-     if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr)
-       {
--      set_errno(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE, NULL);
-+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
-       yield = ERROR;
-       goto SEND_QUIT;
-       }
-@@ -2217,8 +2281,8 @@ if (mua_wrapper)
-     if (badaddr->transport_return != PENDING_OK)
-       {
-       /*XXX could we find a better errno than 0 here? */
--      set_errno(addrlist, 0, badaddr->message, FAIL,
--      testflag(badaddr, af_pass_message), NULL);
-+      set_errno_nohost(addrlist, 0, badaddr->message, FAIL,
-+      testflag(badaddr, af_pass_message));
-       ok = FALSE;
-       break;
-       }
-@@ -2475,7 +2539,7 @@ if (!ok) ok = TRUE; else
-         else
-           sprintf(CS buffer, "%.500s\n", addr->unique);
--        DEBUG(D_deliver) debug_printf("journalling %s", buffer);
-+        DEBUG(D_deliver) debug_printf("journalling %s\n", buffer);
-         len = Ustrlen(CS buffer);
-         if (write(journal_fd, buffer, len) != len)
-           log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
-@@ -2512,7 +2576,7 @@ if (!ok) ok = TRUE; else
-           else
-             sprintf(CS buffer, "%.500s\n", addr->unique);
--          DEBUG(D_deliver) debug_printf("journalling(PRDR) %s", buffer);
-+          DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", buffer);
-           len = Ustrlen(CS buffer);
-           if (write(journal_fd, buffer, len) != len)
-             log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
-@@ -2542,22 +2606,27 @@ the problem is not related to this specific message. */
- if (!ok)
-   {
--  int code;
-+  int code, set_rc;
-+  uschar * set_message;
-   RESPONSE_FAILED:
--  save_errno = errno;
--  message = NULL;
--  send_quit = check_response(host, &save_errno, addrlist->more_errno,
--    buffer, &code, &message, &pass_message);
--  goto FAILED;
+-  /* Set up a tree entry to cache the lookup */
++  if (t)
 +    {
-+    save_errno = errno;
-+    message = NULL;
-+    send_quit = check_response(host, &save_errno, addrlist->more_errno,
-+      buffer, &code, &message, &pass_message);
-+    goto FAILED;
++    HDEBUG(D_dnsbl) debug_printf("cached data found but past valid time; ");
 +    }
  
-   SEND_FAILED:
--  save_errno = errno;
--  code = '4';
--  message = US string_sprintf("send() to %s [%s] failed: %s",
--    host->name, host->address, strerror(save_errno));
--  send_quit = FALSE;
--  goto FAILED;
-+    {
-+    save_errno = errno;
-+    code = '4';
-+    message = US string_sprintf("send() to %s [%s] failed: %s",
-+      host->name, host->address, strerror(save_errno));
-+    send_quit = FALSE;
-+    goto FAILED;
+-  t = store_get(sizeof(tree_node) + Ustrlen(query));
+-  Ustrcpy(t->name, query);
+-  t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
+-  (void)tree_insertnode(&dnsbl_cache, t);
++  else
++    { /* Set up a tree entry to cache the lookup */
++    t = store_get(sizeof(tree_node) + Ustrlen(query));
++    Ustrcpy(t->name, query);
++    t->data.ptr = cb = store_get(sizeof(dnsbl_cache_block));
++    (void)tree_insertnode(&dnsbl_cache, t);
 +    }
  
-   /* This label is jumped to directly when a TLS negotiation has failed,
-   or was not done for a host for which it is required. Values will be set
-@@ -2578,16 +2647,14 @@ if (!ok)
-   FAILED:
-   ok = FALSE;                /* For when reached by GOTO */
-+  set_message = message;
-   if (setting_up)
-     {
-     if (code == '5')
--      set_errno(addrlist, save_errno, message, FAIL, pass_message, host);
-+      set_rc = FAIL;
-     else
--      {
--      set_errno(addrlist, save_errno, message, DEFER, pass_message, host);
--      yield = DEFER;
--      }
-+      yield = set_rc = DEFER;
-     }
-   /* We want to handle timeouts after MAIL or "." and loss of connection after
-@@ -2646,14 +2713,15 @@ if (!ok)
-     if (message_error)
-       {
-       if (mua_wrapper) code = '5';  /* Force hard failure in wrapper mode */
--      set_errno(addrlist, save_errno, message, (code == '5')? FAIL : DEFER,
--        pass_message, host);
+   /* Do the DNS loopup . */
  
-       /* If there's an errno, the message contains just the identity of
-       the host. */
+@@ -3616,7 +3633,10 @@ if (t == NULL)
  
--      if (code != '5')     /* Anything other than 5 is treated as temporary */
-+      if (code == '5')
-+      set_rc = FAIL;
-+      else            /* Anything other than 5 is treated as temporary */
-         {
-+      set_rc = DEFER;
-         if (save_errno > 0)
-           message = US string_sprintf("%s: %s", message, strerror(save_errno));
-         if (host->next != NULL) log_write(0, LOG_MAIN, "%s", message);
-@@ -2670,11 +2738,17 @@ if (!ok)
+   Quite apart from one A6 RR generating multiple addresses, there are DNS
+   lists that return more than one A record, so we must handle multiple
+-  addresses generated in that way as well. */
++  addresses generated in that way as well.
++
++  Mark the cache entry with the "now" plus the minimum of the address TTLs,
++  or some suitably far-future time if none were found. */
  
-     else
-       {
-+      set_rc = DEFER;
-       yield = (save_errno == ERRNO_CHHEADER_FAIL ||
-                save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER;
--      set_errno(addrlist, save_errno, message, DEFER, pass_message, host);
+   if (cb->rc == DNS_SUCCEED)
+     {
+@@ -3634,6 +3654,7 @@ if (t == NULL)
+           *addrp = da;
+           while (da->next != NULL) da = da->next;
+           addrp = &(da->next);
++        if (ttl > rr->ttl) ttl = rr->ttl;
+           }
+         }
        }
+@@ -3645,17 +3666,10 @@ if (t == NULL)
+     if (cb->rhs == NULL) cb->rc = DNS_NODATA;
      }
-+
-+  set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host
-+#ifdef EXPERIMENTAL_DSN_INFO
-+          , smtp_greeting, helo_response
-+#endif
-+          );
-   }
  
++  cb->expiry = time(NULL)+ttl;
+   store_pool = old_pool;
+   }
  
-@@ -2787,6 +2861,9 @@ if (completed_address && ok && send_quit)
-       /* If the socket is successfully passed, we musn't send QUIT (or
-       indeed anything!) from here. */
-+/*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to
-+propagate it from the initial
-+*/
-       if (ok && transport_pass_socket(tblock->name, host->name, host->address,
-             new_message_id, inblock.sock))
-         {
-@@ -2796,7 +2873,11 @@ if (completed_address && ok && send_quit)
+-/* Previous lookup was cached */
+-
+-else
+-  {
+-  HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n");
+-  cb = t->data.ptr;
+-  }
+-
+ /* We now have the result of the DNS lookup, either newly done, or cached
+ from a previous call. If the lookup succeeded, check against the address
+ list if there is one. This may be a positive equality list (introduced by
+
+commit c4dcf906ceb3a45c6b30f76476d73ca836b262cd
+Author: Jeremy Harris <jgh146exb@wizmail.org>
+Date:   Sat Sep 19 13:59:22 2015 +0100
+
+    Retry: always use interface, if set, for retry DB key.  Bug 1678
+    
+    Even constant values must be used, as multiple transports with
+    different values may be in play and should be kept distinct.
+    
+    (cherry picked from commit 6f6dedccb47f231a0712d882da20feffbac8d0bc)
+
+diff --git a/src/src/functions.h b/src/src/functions.h
+index 0257904..94e3f5f 100644
+--- a/src/src/functions.h
++++ b/src/src/functions.h
+@@ -374,7 +374,7 @@ extern int     smtp_sock_connect(host_item *, int, int, uschar *,
+ extern int     smtp_feof(void);
+ extern int     smtp_ferror(void);
+ extern uschar *smtp_get_connection_info(void);
+-extern BOOL    smtp_get_interface(uschar *, int, address_item *, BOOL *,
++extern BOOL    smtp_get_interface(uschar *, int, address_item *,
+                  uschar **, uschar *);
+ extern BOOL    smtp_get_port(uschar *, address_item *, int *, uschar *);
+ extern int     smtp_getc(void);
+diff --git a/src/src/smtp_out.c b/src/src/smtp_out.c
+index c704a0b..2fdf38b 100644
+--- a/src/src/smtp_out.c
++++ b/src/src/smtp_out.c
+@@ -26,7 +26,6 @@ Arguments:
+                which case the function does nothing
+   host_af    AF_INET or AF_INET6 for the outgoing IP address
+   addr       the mail address being handled (for setting errors)
+-  changed    if not NULL, set TRUE if expansion actually changed istring
+   interface  point this to the interface
+   msg        to add to any error message
+@@ -36,7 +35,7 @@ Returns:     TRUE on success, FALSE on failure, with error message
+ BOOL
+ smtp_get_interface(uschar *istring, int host_af, address_item *addr,
+-  BOOL *changed, uschar **interface, uschar *msg)
++  uschar **interface, uschar *msg)
+ {
+ const uschar * expint;
+ uschar *iface;
+@@ -54,8 +53,6 @@ if (expint == NULL)
+   return FALSE;
+   }
  
-     /* If RSET failed and there are addresses left, they get deferred. */
+-if (changed != NULL) *changed = expint != istring;
+-
+ while (isspace(*expint)) expint++;
+ if (*expint == 0) return TRUE;
  
--    else set_errno(first_addr, errno, msg, DEFER, FALSE, host);
-+    else set_errno(first_addr, errno, msg, DEFER, FALSE, host
-+#ifdef EXPERIMENTAL_DSN_INFO
-+                , smtp_greeting, helo_response
-+#endif
-+                );
-     }
-   }
+diff --git a/src/src/transports/smtp.c b/src/src/transports/smtp.c
+index 5ac5533..ba7fb5e 100644
+--- a/src/src/transports/smtp.c
++++ b/src/src/transports/smtp.c
+@@ -3174,7 +3174,6 @@ for (cutoff_retry = 0; expired &&
+     BOOL serialized = FALSE;
+     BOOL host_is_expired = FALSE;
+     BOOL message_defer = FALSE;
+-    BOOL ifchanges = FALSE;
+     BOOL some_deferred = FALSE;
+     address_item *first_addr = NULL;
+     uschar *interface = NULL;
+@@ -3350,15 +3349,18 @@ for (cutoff_retry = 0; expired &&
+     if (Ustrcmp(pistring, ":25") == 0) pistring = US"";
+     /* Select IPv4 or IPv6, and choose an outgoing interface. If the interface
+-    string changes upon expansion, we must add it to the key that is used for
+-    retries, because connections to the same host from a different interface
+-    should be treated separately. */
++    string is set, even if constant (as different transports can have different
++    constant settings), we must add it to the key that is used for retries,
++    because connections to the same host from a different interface should be
++    treated separately. */
+     host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET : AF_INET6;
+-    if (!smtp_get_interface(ob->interface, host_af, addrlist, &ifchanges,
+-         &interface, tid))
+-      return FALSE;
+-    if (ifchanges) pistring = string_sprintf("%s/%s", pistring, interface);
++    if ((rs = ob->interface) && *rs)
++      {
++      if (!smtp_get_interface(rs, host_af, addrlist, &interface, tid))
++      return FALSE;
++      pistring = string_sprintf("%s/%s", pistring, interface);
++      }
  
-@@ -2938,6 +3019,10 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
-     addr->peerdn = NULL;
-     addr->ocsp = OCSP_NOT_REQ;
- #endif
-+#ifdef EXPERIMENTAL_DSN_INFO
-+    addr->smtp_greeting = NULL;
-+    addr->helo_response = NULL;
-+#endif
-     }
- return first_addr;
- }
-@@ -3479,7 +3564,7 @@ for (cutoff_retry = 0; expired &&
-     if (dont_deliver)
-       {
-       host_item *host2;
--      set_errno(addrlist, 0, NULL, OK, FALSE, NULL);
-+      set_errno_nohost(addrlist, 0, NULL, OK, FALSE);
-       for (addr = addrlist; addr != NULL; addr = addr->next)
-         {
-         addr->host_used = host;
+     /* The first time round the outer loop, check the status of the host by
+     inspecting the retry data. The second time round, we are interested only
+diff --git a/src/src/verify.c b/src/src/verify.c
+index d392fda..6411c7e 100644
+--- a/src/src/verify.c
++++ b/src/src/verify.c
+@@ -444,7 +444,7 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
+         host_af = (Ustrchr(host->address, ':') == NULL)? AF_INET:AF_INET6;
+-        if (!smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
++        if (!smtp_get_interface(tf->interface, host_af, addr, &interface,
+                 US"callout") ||
+             !smtp_get_port(tf->port, addr, &port, US"callout"))
+           log_write(0, LOG_MAIN|LOG_PANIC, "<%s>: %s", addr->address,
+@@ -579,7 +579,7 @@ can do it there for the non-rcpt-verify case.  For this we keep an addresscount.
+     deliver_domain = addr->domain;
+     transport_name = addr->transport->name;
+-    if (  !smtp_get_interface(tf->interface, host_af, addr, NULL, &interface,
++    if (  !smtp_get_interface(tf->interface, host_af, addr, &interface,
+             US"callout")
+        || !smtp_get_port(tf->port, addr, &port, US"callout")
+        )
index c6127120110da095a9d03c164ad7b1d7352f1804..ffaa40ea0043676f38b3c0de649c6e2c94ed268f 100644 (file)
--- a/exim.spec
+++ b/exim.spec
@@ -21,7 +21,7 @@ Summary(pl.UTF-8):    Agent Transferu Poczty Uniwersytetu w Cambridge
 Summary(pt_BR.UTF-8):  Servidor de correio eletrônico exim
 Name:          exim
 Version:       4.86
-Release:       2
+Release:       3
 Epoch:         2
 License:       GPL
 Group:         Networking/Daemons/SMTP
@@ -45,7 +45,7 @@ Source14:     ftp://ftp.exim.org/pub/exim/exim4/old/config.samples.tar.bz2
 # Source14-md5:        4b93321938a800caa6127c48ad60a42b
 Source15:      %{name}4-smtp.pamd
 Source16:      %{name}on.png
-# git log -p exim-4_86..master --reverse -- . ":(exclude)doc/doc-*" ":(exclude)test" ":(exclude).*" > exim-git.patch
+# git log -p exim-4_86..exim-4_86+fixes --reverse -- . ":(exclude)doc/doc-*" ":(exclude)test" ":(exclude).*" > exim-git.patch
 Patch100:      %{name}-git.patch
 Patch0:                %{name}4-EDITME.patch
 Patch1:                %{name}4-monitor-EDITME.patch
This page took 0.479135 seconds and 4 git commands to generate.