diff --git a/exim-4.86.2+fixes-867e8fe25dbfb1e31493488ad695bde55b890397.patch b/exim-4.86.2+fixes-867e8fe25dbfb1e31493488ad695bde55b890397.patch deleted file mode 100644 index d72f55c..0000000 --- a/exim-4.86.2+fixes-867e8fe25dbfb1e31493488ad695bde55b890397.patch +++ /dev/null @@ -1,1425 +0,0 @@ -diff -ru a/daemon.c b/daemon.c ---- a/src/daemon.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/daemon.c 2017-04-24 09:33:17.356999655 +0200 -@@ -735,6 +735,7 @@ - /* 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; - } -diff -ru a/deliver.c b/deliver.c ---- a/src/deliver.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/deliver.c 2017-04-24 09:33:17.356999655 +0200 -@@ -9,6 +9,7 @@ - - - #include "exim.h" -+#include - - - /* Data block for keeping track of subprocesses for parallel remote -@@ -7904,17 +7905,36 @@ - 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 -ru a/dns.c b/dns.c ---- a/src/dns.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/dns.c 2017-04-24 09:33:17.356999655 +0200 -@@ -390,7 +390,8 @@ - - 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 -ru a/exim.c b/exim.c ---- a/src/exim.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/exim.c 2017-04-24 09:33:17.360999706 +0200 -@@ -3730,6 +3730,13 @@ - exit(EXIT_FAILURE); - } - -+/* Store the initial cwd before we change directories */ -+if ((initial_cwd = getcwd(NULL, 0)) == NULL) -+ { -+ perror("exim: can't get the current working directory"); -+ exit(EXIT_FAILURE); -+ } -+ - readconf_main(); - - if (cleanup_environment() == FALSE) -@@ -4017,9 +4024,10 @@ - { - int i; - uschar *p = big_buffer; -- char * dummy; - Ustrcpy(p, "cwd= (failed)"); -- dummy = /* quieten compiler */ getcwd(CS p+4, big_buffer_size - 4); -+ -+ Ustrncpy(p + 4, initial_cwd, big_buffer_size-5); -+ - while (*p) p++; - (void)string_format(p, big_buffer_size - (p - big_buffer), " %d args:", argc); - while (*p) p++; -diff -ru a/expand.c b/expand.c ---- a/src/expand.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/expand.c 2017-04-24 09:33:17.360999706 +0200 -@@ -533,6 +533,7 @@ - { "host_lookup_deferred",vtype_int, &host_lookup_deferred }, - { "host_lookup_failed", vtype_int, &host_lookup_failed }, - { "host_port", vtype_int, &deliver_host_port }, -+ { "initial_cwd", vtype_stringptr, &initial_cwd }, - { "inode", vtype_ino, &deliver_inode }, - { "interface_address", vtype_stringptr, &interface_address }, - { "interface_port", vtype_int, &interface_port }, -diff -ru a/functions.h b/functions.h ---- a/src/functions.h 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/functions.h 2017-04-24 09:33:17.360999706 +0200 -@@ -375,7 +375,7 @@ - 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 -ru a/globals.c b/globals.c ---- a/src/globals.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/globals.c 2017-04-24 09:33:17.360999706 +0200 -@@ -783,6 +783,7 @@ - uschar *ignore_fromline_hosts = NULL; - BOOL inetd_wait_mode = FALSE; - int inetd_wait_timeout = -1; -+uschar *initial_cwd = NULL; - uschar *interface_address = NULL; - int interface_port = -1; - BOOL is_inetd = FALSE; -diff -ru a/globals.h b/globals.h ---- a/src/globals.h 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/globals.h 2017-04-24 09:33:17.360999706 +0200 -@@ -508,6 +508,7 @@ - extern uschar *ignore_fromline_hosts; /* Hosts permitted to send "From " */ - extern BOOL inetd_wait_mode; /* Whether running in inetd wait mode */ - extern int inetd_wait_timeout; /* Timeout for inetd wait mode */ -+extern uschar *initial_cwd; /* The directory we where in at startup */ - extern BOOL is_inetd; /* True for inetd calls */ - extern uschar *iterate_item; /* Item from iterate list */ - -diff -ru a/lookupapi.h b/lookupapi.h ---- a/src/lookupapi.h 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookupapi.h 2017-04-24 09:33:17.364999757 +0200 -@@ -34,7 +34,7 @@ - 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 @@ - } 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 -ru a/lookups/cdb.c b/lookups/cdb.c ---- a/src/lookups/cdb.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/cdb.c 2017-04-24 09:33:17.364999757 +0200 -@@ -279,7 +279,7 @@ - int key_len, - uschar **result, - uschar **errmsg, -- BOOL *do_cache) -+ uint *do_cache) - { - struct cdb_state * cdbp = handle; - uint32 item_key_len, -diff -ru a/lookups/dbmdb.c b/lookups/dbmdb.c ---- a/src/lookups/dbmdb.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/dbmdb.c 2017-04-24 09:33:17.364999757 +0200 -@@ -87,7 +87,7 @@ - - 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 @@ - - 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) - { - return dbmdb_find(handle, filename, keystring, length-1, result, errmsg, - do_cache); -@@ -140,7 +140,7 @@ - - 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 -ru a/lookups/dnsdb.c b/lookups/dnsdb.c ---- a/src/lookups/dnsdb.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/dnsdb.c 2017-04-24 09:33:17.364999757 +0200 -@@ -131,7 +131,7 @@ - - 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 @@ - { - if (rr->type != searchtype) continue; - -+ if (*do_cache > rr->ttl) -+ *do_cache = rr->ttl; -+ - if (type == T_A || type == T_AAAA || type == T_ADDRESSES) - { - dns_address *da; -diff -ru a/lookups/dsearch.c b/lookups/dsearch.c ---- a/src/lookups/dsearch.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/dsearch.c 2017-04-24 09:33:17.364999757 +0200 -@@ -67,7 +67,7 @@ - - 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 -ru a/lookups/ibase.c b/lookups/ibase.c ---- a/src/lookups/ibase.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/ibase.c 2017-04-24 09:33:17.364999757 +0200 -@@ -451,7 +451,7 @@ - - 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 -ru a/lookups/ldap.c b/lookups/ldap.c ---- a/src/lookups/ldap.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/ldap.c 2017-04-24 09:33:17.364999757 +0200 -@@ -156,7 +156,7 @@ - uschar *error2 = NULL; /* error message from the server */ - uschar *matched = NULL; /* partially matched DN */ - --int attr_count = 0; -+int attrs_requested = 0; - int error_yield = DEFER; - int msgid; - int rc, ldap_rc, ldap_parse_rc; -@@ -248,7 +248,7 @@ - /* Count the attributes; we need this later to tell us how to format results */ - - for (attrp = USS ludp->lud_attrs; attrp != NULL && *attrp != NULL; attrp++) -- attr_count++; -+ attrs_requested++; - - /* See if we can find a cached connection to this host. The port is not - relevant for ldapi. The host name pointer is set to NULL if no host was given -@@ -713,10 +713,15 @@ - LDAP_RES_SEARCH_ENTRY) - { - LDAPMessage *e; -+ int valuecount; /* We can see an attr spread across several -+ entries. If B is derived from A and we request -+ A and the directory contains both, A and B, -+ then we get two entries, one for A and one for B. -+ Here we just count the values per entry */ - - DEBUG(D_lookup) debug_printf("ldap_result loop\n"); - -- for(e = ldap_first_entry(lcp->ld, result); -+ for(e = ldap_first_entry(lcp->ld, result), valuecount = 0; - e != NULL; - e = ldap_next_entry(lcp->ld, e)) - { -@@ -774,6 +779,11 @@ - attr != NULL; - attr = US ldap_next_attribute(lcp->ld, e, ber)) - { -+ -+ /* In case of attrs_requested == 1 we just count the values, in all other cases -+ (0, >1) we count the values per attribute */ -+ if (attrs_requested != 1) valuecount = 0; -+ - if (attr[0] != 0) - { - /* Get array of values for this attribute. */ -@@ -781,7 +791,8 @@ - if ((firstval = values = USS ldap_get_values(lcp->ld, e, CS attr)) - != NULL) - { -- if (attr_count != 1) -+ -+ if (attrs_requested != 1) - { - if (insert_space) - data = string_cat(data, &size, &ptr, US" ", 1); -@@ -795,6 +806,7 @@ - { - uschar *value = *values; - int len = Ustrlen(value); -+ ++valuecount; - - DEBUG(D_lookup) debug_printf("LDAP attr loop %s:%s\n", attr, value); - -@@ -804,13 +816,13 @@ - * attributeTypes B and C from A and then query for A.) - * In all other cases we detect the different attribute - * and append only every non first value. */ -- if ((attr_count == 1 && data) || (values != firstval)) -+ if (data && valuecount > 1) - data = string_cat(data, &size, &ptr, US",", 1); - - /* For multiple attributes, the data is in quotes. We must escape - internal quotes, backslashes, newlines, and must double commas. */ - -- if (attr_count != 1) -+ if (attrs_requested != 1) - { - int j; - for (j = 0; j < len; j++) -@@ -851,7 +863,7 @@ - - /* Closing quote at the end of the data for a named attribute. */ - -- if (attr_count != 1) -+ if (attrs_requested != 1) - data = string_cat(data, &size, &ptr, US"\"", 1); - - /* Free the values */ -@@ -1339,7 +1351,7 @@ - - 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 +1360,7 @@ - - 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 +1369,7 @@ - - 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 +1378,7 @@ - - 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 -ru a/lookups/lf_functions.h b/lookups/lf_functions.h ---- a/src/lookups/lf_functions.h 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/lf_functions.h 2017-04-24 09:33:17.364999757 +0200 -@@ -12,7 +12,7 @@ - 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 -ru a/lookups/lf_sqlperform.c b/lookups/lf_sqlperform.c ---- a/src/lookups/lf_sqlperform.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/lf_sqlperform.c 2017-04-24 09:33:17.364999757 +0200 -@@ -27,7 +27,7 @@ - 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 @@ - 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 -ru a/lookups/lsearch.c b/lookups/lsearch.c ---- a/src/lookups/lsearch.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/lsearch.c 2017-04-24 09:33:17.364999757 +0200 -@@ -323,7 +323,7 @@ - - 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 @@ - - 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 @@ - - 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 @@ - - 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 -ru a/lookups/mysql.c b/lookups/mysql.c ---- a/src/lookups/mysql.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/mysql.c 2017-04-24 09:33:17.364999757 +0200 -@@ -74,7 +74,7 @@ - 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 @@ - - 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 @@ - 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 @@ - { - 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; - } - *errmsg = string_sprintf("MYSQL: lookup result failed: %s\n", -@@ -341,7 +341,7 @@ - - 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 -ru a/lookups/nis.c b/lookups/nis.c ---- a/src/lookups/nis.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/nis.c 2017-04-24 09:33:17.364999757 +0200 -@@ -42,7 +42,7 @@ - - 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 @@ - - 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) - { - int rc; - uschar *nis_data; -diff -ru a/lookups/nisplus.c b/lookups/nisplus.c ---- a/src/lookups/nisplus.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/nisplus.c 2017-04-24 09:33:17.364999757 +0200 -@@ -43,7 +43,7 @@ - - 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 -ru a/lookups/oracle.c b/lookups/oracle.c ---- a/src/lookups/oracle.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/oracle.c 2017-04-24 09:33:17.364999757 +0200 -@@ -517,7 +517,7 @@ - - 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 -ru a/lookups/passwd.c b/lookups/passwd.c ---- a/src/lookups/passwd.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/passwd.c 2017-04-24 09:33:17.364999757 +0200 -@@ -34,7 +34,7 @@ - - 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 -ru a/lookups/pgsql.c b/lookups/pgsql.c ---- a/src/lookups/pgsql.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/pgsql.c 2017-04-24 09:33:17.364999757 +0200 -@@ -119,7 +119,7 @@ - - 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 @@ - /* 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 @@ - - 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 -ru a/lookups/README b/lookups/README ---- a/src/lookups/README 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/README 2017-04-24 09:33:17.364999757 +0200 -@@ -122,12 +122,15 @@ - 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 -ru a/lookups/redis.c b/lookups/redis.c ---- a/src/lookups/redis.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/redis.c 2017-04-24 09:33:17.364999757 +0200 -@@ -65,7 +65,7 @@ - */ - 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 @@ - 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 @@ - 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 @@ - - 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 -ru a/lookups/spf.c b/lookups/spf.c ---- a/src/lookups/spf.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/spf.c 2017-04-24 09:33:17.364999757 +0200 -@@ -31,7 +31,9 @@ - #include - #include - --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 @@ - 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 -ru a/lookups/sqlite.c b/lookups/sqlite.c ---- a/src/lookups/sqlite.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/sqlite.c 2017-04-24 09:33:17.364999757 +0200 -@@ -81,7 +81,7 @@ - - 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 @@ - return FAIL; - } - --if (res.string == NULL) *do_cache = FALSE; -+if (res.string == NULL) *do_cache = 0; - - *result = res.string; - return OK; -diff -ru a/lookups/testdb.c b/lookups/testdb.c ---- a/src/lookups/testdb.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/testdb.c 2017-04-24 09:33:17.364999757 +0200 -@@ -38,7 +38,7 @@ - - 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 @@ - return DEFER; - } - --if (Ustrcmp(query, "nocache") == 0) *do_cache = FALSE; -+if (Ustrcmp(query, "nocache") == 0) *do_cache = 0; - - *result = string_copy(query); - return OK; -diff -ru a/lookups/whoson.c b/lookups/whoson.c ---- a/src/lookups/whoson.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/lookups/whoson.c 2017-04-24 09:33:17.364999757 +0200 -@@ -36,7 +36,7 @@ - - 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 -ru a/mime.c b/mime.c ---- a/src/mime.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/mime.c 2017-04-24 09:33:17.368999807 +0200 -@@ -550,7 +550,8 @@ - uschar * val = string_cat(NULL, &size, &ptr, US"=?", 2); - uschar c; - --val = string_cat(val, &size, &ptr, charset, Ustrlen(charset)); -+if (charset) -+ val = string_cat(val, &size, &ptr, charset, Ustrlen(charset)); - val = string_cat(val, &size, &ptr, US"?Q?", 3); - - while ((c = *fname)) -@@ -607,7 +608,7 @@ - if (!fgets(CS header, MIME_MAX_HEADER_SIZE, f)) - { - /* Hit EOF or read error. Ugh. */ -- DEBUG(D_acl) debug_printf("Hit EOF ...\n"); -+ DEBUG(D_acl) debug_printf("MIME: Hit EOF ...\n"); - return rc; - } - -@@ -619,12 +620,12 @@ - if (Ustrncmp((header+2+Ustrlen(context->boundary)), "--", 2) == 0) - { - /* END boundary found */ -- DEBUG(D_acl) debug_printf("End boundary found %s\n", -+ DEBUG(D_acl) debug_printf("MIME: End boundary found %s\n", - context->boundary); - return rc; - } - -- DEBUG(D_acl) debug_printf("Next part with boundary %s\n", -+ DEBUG(D_acl) debug_printf("MIME: Next part with boundary %s\n", - context->boundary); - break; - } -@@ -648,7 +649,7 @@ - - for (q = p; *q != ';' && *q; q++) ; - *mh->value = string_copynlc(p, q-p); -- DEBUG(D_acl) debug_printf("found %s MIME header, value is '%s'\n", -+ DEBUG(D_acl) debug_printf("MIME: found %s header, value is '%s'\n", - mh->name, *mh->value); - - if (*(p = q)) p++; /* jump past the ; */ -@@ -666,7 +667,7 @@ - { - mime_parameter * mp; - -- DEBUG(D_acl) debug_printf(" considering paramlist '%s'\n", p); -+ DEBUG(D_acl) debug_printf("MIME: considering paramlist '%s'\n", p); - - if ( !mime_filename - && strncmpic(CUS"content-disposition:", header, 20) == 0 -@@ -700,22 +701,27 @@ - uschar * s = q; - - /* look for a ' in the "filename" */ -- while(*s != '\'' && *s) s++; /* s is ' or NUL */ -+ while(*s != '\'' && *s) s++; /* s is 1st ' or NUL */ - - if ((size = s-q) > 0) -- { - mime_filename_charset = string_copyn(q, size); -- p = s; - -- while(*p == '\'' && *p) p++; /* p is after ' */ -- } -+ if (*(p = s)) p++; -+ while(*p == '\'') p++; /* p is after 2nd ' */ - } - else - p = q; - -+ DEBUG(D_acl) debug_printf("MIME: charset %s fname '%s'\n", -+ mime_filename_charset ? mime_filename_charset : US"", p); -+ - temp_string = rfc2231_to_2047(p, mime_filename_charset, &slen); -- temp_string = rfc2047_decode(temp_string, FALSE, NULL, 32, -+ DEBUG(D_acl) debug_printf("MIME: 2047-name %s\n", temp_string); -+ -+ temp_string = rfc2047_decode(temp_string, FALSE, NULL, ' ', - NULL, &err_msg); -+ DEBUG(D_acl) debug_printf("MIME: plain-name %s\n", temp_string); -+ - size = Ustrlen(temp_string); - - if (size == slen) -@@ -750,7 +756,7 @@ - &dummy_errstr) - : NULL; - DEBUG(D_acl) debug_printf( -- " found %s MIME parameter in %s header, value '%s'\n", -+ "MIME: found %s parameter in %s header, value '%s'\n", - mp->name, mh->name, *mp->value); - - break; /* done matching param names */ -@@ -768,7 +774,7 @@ - if (decoding_failed) mime_filename = mime_fname_rfc2231; - - DEBUG(D_acl) debug_printf( -- " found %s MIME parameter in %s header, value is '%s'\n", -+ "MIME: found %s parameter in %s header, value is '%s'\n", - "filename", mh->name, mime_filename); - } - } -@@ -809,8 +815,9 @@ - (nested_context.boundary != NULL) && - (Ustrncmp(mime_content_type,"multipart",9) == 0) ) - { -- DEBUG(D_acl) debug_printf("Entering multipart recursion, boundary '%s'\n", -- nested_context.boundary); -+ DEBUG(D_acl) -+ debug_printf("MIME: Entering multipart recursion, boundary '%s'\n", -+ nested_context.boundary); - - nested_context.context = - context && context->context == MBC_ATTACHMENT -diff -ru a/pdkim/base64.c b/pdkim/base64.c ---- a/src/pdkim/base64.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/pdkim/base64.c 2017-04-24 09:33:17.368999807 +0200 -@@ -128,20 +128,22 @@ - - for( i = j = n = 0; i < slen; i++ ) - { -+ unsigned char c = src[i]; -+ - if( ( slen - i ) >= 2 && -- src[i] == '\r' && src[i + 1] == '\n' ) -+ c == '\r' && src[i + 1] == '\n' ) - continue; - -- if( src[i] == '\n' ) -+ if( c == '\n' || c == ' ' || c == '\t' ) - continue; - -- if( src[i] == '=' && ++j > 2 ) -+ if( c == '=' && ++j > 2 ) - return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); - -- if( src[i] > 127 || base64_dec_map[src[i]] == 127 ) -+ if( c > 127 || base64_dec_map[src[i]] == 127 ) - return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); - -- if( base64_dec_map[src[i]] < 64 && j != 0 ) -+ if( base64_dec_map[c] < 64 && j != 0 ) - return( POLARSSL_ERR_BASE64_INVALID_CHARACTER ); - - n++; -@@ -160,11 +162,13 @@ - - for( j = 3, n = x = 0, p = dst; i > 0; i--, src++ ) - { -- if( *src == '\r' || *src == '\n' ) -+ unsigned char c = *src; -+ -+ if( c == '\r' || c == '\n' || c == ' ' || c == '\t' ) - continue; - -- j -= ( base64_dec_map[*src] == 64 ); -- x = (x << 6) | ( base64_dec_map[*src] & 0x3F ); -+ j -= ( base64_dec_map[c] == 64 ); -+ x = (x << 6) | ( base64_dec_map[c] & 0x3F ); - - if( ++n == 4 ) - { -diff -ru a/readconf.c b/readconf.c ---- a/src/readconf.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/readconf.c 2017-04-24 09:33:17.368999807 +0200 -@@ -3456,10 +3456,10 @@ - " are obsolete\n"); - #endif /*SUPPORT_TLS*/ - --if ((!add_environment || *add_environment == '\0') && !keep_environment) -+if (!keep_environment && environ && *environ) - log_write(0, LOG_MAIN, -- "WARNING: purging the environment.\n" -- " Suggested action: use keep_environment and add_environment.\n"); -+ "Warning: purging the environment.\n" -+ " Suggested action: use keep_environment."); - } - - -diff -ru a/receive.c b/receive.c ---- a/src/receive.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/receive.c 2017-04-24 09:33:17.368999807 +0200 -@@ -835,7 +835,15 @@ - ch_state = 4; - continue; - } -- ch_state = 1; /* The dot itself is removed */ -+ /* The dot was removed at state 3. For a doubled dot, here, reinstate -+ it to cutthrough. The current ch, dot or not, is passed both to cutthrough -+ and to file below. */ -+ if (ch == '.') -+ { -+ uschar c= ch; -+ (void) cutthrough_puts(&c, 1); -+ } -+ ch_state = 1; - break; - - case 4: /* After [CR] LF . CR */ -diff -ru a/search.c b/search.c ---- a/src/search.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/search.c 2017-04-24 09:33:17.372999860 +0200 -@@ -466,6 +466,7 @@ - { - 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 @@ - /* 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 - { -- BOOL do_cache = TRUE; -+ uint do_cache = UINT_MAX; - int keylength = Ustrlen(keystring); - - DEBUG(D_lookup) - { -- 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""); - } - - /* Call the code for the different kinds of search. DEFER is handled -@@ -511,9 +521,7 @@ - - if (lookup_list[search_type]->find(c->handle, filename, keystring, keylength, - &data, &search_error_message, &do_cache) == DEFER) -- { - search_find_defer = TRUE; -- } - - /* 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 @@ - 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 -+ { -+ e = store_get(sizeof(expiring_data) + sizeof(tree_node) + len); -+ e->expiry = do_cache == UINT_MAX ? 0 : time(NULL)+do_cache; -+ e->ptr = data; -+ t = (tree_node *)(e+1); -+ memcpy(t->name, keystring, len); -+ t->data.ptr = e; -+ tree_insertnode(&c->item_cache, t); -+ } - } - - /* If caching was disabled, empty the cache tree. We just set the cache -@@ -540,34 +560,19 @@ - } - } - --/* Data was in the cache already; set the pointer from the tree node */ -- --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); -- } -- --/* Debug: output the answer */ -- - 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 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"); - } - - /* Return it in new dynamic store in the regular pool */ - - store_pool = old_pool; --return (data == NULL)? NULL : string_copy(data); -+return data ? string_copy(data) : NULL; - } - - -diff -ru a/smtp_in.c b/smtp_in.c ---- a/src/smtp_in.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/smtp_in.c 2017-04-24 09:33:17.372999860 +0200 -@@ -9,6 +9,7 @@ - - - #include "exim.h" -+#include - - - /* Initialize for TCP wrappers if so configured. It appears that the macro -@@ -232,6 +233,7 @@ - - /* Sanity check and validate optional args to MAIL FROM: envelope */ - enum { -+ ENV_MAIL_OPT_NULL, - ENV_MAIL_OPT_SIZE, ENV_MAIL_OPT_BODY, ENV_MAIL_OPT_AUTH, - #ifndef DISABLE_PRDR - ENV_MAIL_OPT_PRDR, -@@ -240,7 +242,6 @@ - #ifdef EXPERIMENTAL_INTERNATIONAL - ENV_MAIL_OPT_UTF8, - #endif -- ENV_MAIL_OPT_NULL - }; - typedef struct { - uschar * name; /* option requested during MAIL cmd */ -@@ -260,7 +261,8 @@ - #ifdef EXPERIMENTAL_INTERNATIONAL - { US"SMTPUTF8",ENV_MAIL_OPT_UTF8, FALSE }, /* rfc6531 */ - #endif -- { US"NULL", ENV_MAIL_OPT_NULL, FALSE } -+ /* keep this the last entry */ -+ { US"NULL", ENV_MAIL_OPT_NULL, FALSE }, - }; - - /* When reading SMTP from a remote host, we have to use our own versions of the -@@ -3887,7 +3889,7 @@ - if (!extract_option(&name, &value)) break; - - for (mail_args = env_mail_type_list; -- (char *)mail_args < (char *)env_mail_type_list + sizeof(env_mail_type_list); -+ mail_args->value != ENV_MAIL_OPT_NULL; - mail_args++ - ) - if (strcmpic(name, mail_args->name) == 0) -@@ -4066,15 +4068,17 @@ - } - break; - #endif -- /* Unknown option. Stick back the terminator characters and break -+ /* No valid option. Stick back the terminator characters and break - the loop. Do the name-terminator second as extract_option sets -- value==name when it found no equal-sign. -- An error for a malformed address will occur. */ -- default: -+ value==name when it found no equal-sign. -+ An error for a malformed address will occur. */ -+ case ENV_MAIL_OPT_NULL: - value[-1] = '='; - name[-1] = ' '; - arg_error = TRUE; - break; -+ -+ default: assert(0); - } - /* Break out of for loop if switch() had bad argument or - when start of the email address is reached */ -diff -ru a/smtp_out.c b/smtp_out.c ---- a/src/smtp_out.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/smtp_out.c 2017-04-24 09:33:17.372999860 +0200 -@@ -26,7 +26,6 @@ - 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 @@ - - 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 @@ - return FALSE; - } - --if (changed != NULL) *changed = expint != istring; -- - while (isspace(*expint)) expint++; - if (*expint == 0) return TRUE; - -diff -ru a/structs.h b/structs.h ---- a/src/structs.h 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/structs.h 2017-04-24 09:33:17.372999860 +0200 -@@ -657,6 +657,16 @@ - 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 @@ - 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 -ru a/tls-gnu.c b/tls-gnu.c ---- a/src/tls-gnu.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/tls-gnu.c 2017-04-24 09:33:17.376999910 +0200 -@@ -1729,6 +1729,7 @@ - - if (!sigalrm_seen) - { -+ gnutls_certificate_free_credentials(state->x509_cred); - (void)fclose(smtp_out); - (void)fclose(smtp_in); - } -@@ -2014,6 +2015,8 @@ - } - - gnutls_deinit(state->session); -+gnutls_certificate_free_credentials(state->x509_cred); -+ - - state->tlsp->active = -1; - memcpy(state, &exim_gnutls_state_init, sizeof(exim_gnutls_state_init)); -@@ -2074,6 +2077,8 @@ - receive_smtp_buffered = smtp_buffered; - - gnutls_deinit(state->session); -+ gnutls_certificate_free_credentials(state->x509_cred); -+ - state->session = NULL; - state->tlsp->active = -1; - state->tlsp->bits = 0; -diff -ru a/transport.c b/transport.c ---- a/src/transport.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/transport.c 2017-04-24 09:33:17.376999910 +0200 -@@ -1752,7 +1752,7 @@ - { - 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); - -diff -ru a/transports/smtp.c b/transports/smtp.c ---- a/src/transports/smtp.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/transports/smtp.c 2017-04-24 09:33:17.376999910 +0200 -@@ -1274,14 +1274,19 @@ - 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); - --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; - } -@@ -3169,7 +3174,6 @@ - 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; -@@ -3345,15 +3349,18 @@ - 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); -+ } - - /* 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 -ru a/verify.c b/verify.c ---- a/src/verify.c 2016-03-02 18:27:51.000000000 +0100 -+++ b/src/verify.c 2017-04-24 09:33:17.380999961 +0200 -@@ -21,6 +21,7 @@ - /* Structure for caching DNSBL lookups */ - - typedef struct dnsbl_cache_block { -+ time_t expiry; - dns_address *rhs; - uschar *text; - int rc; -@@ -443,7 +444,7 @@ - - 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, -@@ -578,7 +579,7 @@ - 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") - ) -@@ -3584,21 +3585,37 @@ - - /* Look for this query in the cache. */ - --t = tree_search(dnsbl_cache, query); -+if ( (t = tree_search(dnsbl_cache, query)) -+ && (cb = t->data.ptr)->expiry > time(NULL) -+ ) -+ -+/* Previous lookup was cached */ -+ -+ { -+ HDEBUG(D_dnsbl) debug_printf("using result of previous DNS lookup\n"); -+ } - - /* If not cached from a previous lookup, we must do a DNS lookup, and - cache the result in permanent memory. */ - --if (t == NULL) -+else - { -+ uint ttl = 3600; -+ - store_pool = POOL_PERM; - -- /* Set up a tree entry to cache the lookup */ -+ if (t) -+ { -+ HDEBUG(D_dnsbl) debug_printf("cached data found but past valid time; "); -+ } - -- 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); -+ } - - /* Do the DNS loopup . */ - -@@ -3616,7 +3633,10 @@ - - 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. */ - - if (cb->rc == DNS_SUCCEED) - { -@@ -3634,6 +3654,7 @@ - *addrp = da; - while (da->next != NULL) da = da->next; - addrp = &(da->next); -+ if (ttl > rr->ttl) ttl = rr->ttl; - } - } - } -@@ -3645,17 +3666,10 @@ - if (cb->rhs == NULL) cb->rc = DNS_NODATA; - } - -+ cb->expiry = time(NULL)+ttl; - store_pool = old_pool; - } - --/* 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 -Only in a: version.sh