-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
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 @@
/* 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")
+ )