TLS: retire obsolete options gnutls_require_{kx,mac,proto)
[exim.git] / src / src / transports / smtp.c
index 12ae6e14d469df3c3969804a6ad1e317fb4f3f18..3a887c1519927d6b64318712b17b09175bed6366 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2014 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 #include "../exim.h"
 /* See the file NOTICE for conditions of use and distribution. */
 
 #include "../exim.h"
@@ -19,6 +19,11 @@ before the lower case letters). Some live in the transport_instance block so as
 to be publicly visible; these are flagged with opt_public. */
 
 optionlist smtp_transport_options[] = {
 to be publicly visible; these are flagged with opt_public. */
 
 optionlist smtp_transport_options[] = {
+  { "*expand_multi_domain",             opt_stringptr | opt_hidden | opt_public,
+      (void *)offsetof(transport_instance, expand_multi_domain) },
+  { "*expand_retry_include_ip_address", opt_stringptr | opt_hidden,
+       (void *)(offsetof(smtp_transport_options_block, expand_retry_include_ip_address)) },
+
   { "address_retry_include_sender", opt_bool,
       (void *)offsetof(smtp_transport_options_block, address_retry_include_sender) },
   { "allow_localhost",      opt_bool,
   { "address_retry_include_sender", opt_bool,
       (void *)offsetof(smtp_transport_options_block, address_retry_include_sender) },
   { "allow_localhost",      opt_bool,
@@ -56,9 +61,9 @@ optionlist smtp_transport_options[] = {
   { "dns_search_parents",   opt_bool,
       (void *)offsetof(smtp_transport_options_block, dns_search_parents) },
   { "dnssec_request_domains", opt_stringptr,
   { "dns_search_parents",   opt_bool,
       (void *)offsetof(smtp_transport_options_block, dns_search_parents) },
   { "dnssec_request_domains", opt_stringptr,
-      (void *)offsetof(smtp_transport_options_block, dnssec_request_domains) },
+      (void *)offsetof(smtp_transport_options_block, dnssec.request) },
   { "dnssec_require_domains", opt_stringptr,
   { "dnssec_require_domains", opt_stringptr,
-      (void *)offsetof(smtp_transport_options_block, dnssec_require_domains) },
+      (void *)offsetof(smtp_transport_options_block, dnssec.require) },
   { "dscp",                 opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dscp) },
   { "fallback_hosts",       opt_stringptr,
   { "dscp",                 opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, dscp) },
   { "fallback_hosts",       opt_stringptr,
@@ -67,17 +72,6 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, final_timeout) },
   { "gethostbyname",        opt_bool,
       (void *)offsetof(smtp_transport_options_block, gethostbyname) },
       (void *)offsetof(smtp_transport_options_block, final_timeout) },
   { "gethostbyname",        opt_bool,
       (void *)offsetof(smtp_transport_options_block, gethostbyname) },
-#ifdef SUPPORT_TLS
-  /* These are no longer honoured, as of Exim 4.80; for now, we silently
-  ignore; 4.83 will warn, and a later-still release will remove
-  these options, so that using them becomes an error. */
-  { "gnutls_require_kx",    opt_stringptr,
-      (void *)offsetof(smtp_transport_options_block, gnutls_require_kx) },
-  { "gnutls_require_mac",   opt_stringptr,
-      (void *)offsetof(smtp_transport_options_block, gnutls_require_mac) },
-  { "gnutls_require_protocols", opt_stringptr,
-      (void *)offsetof(smtp_transport_options_block, gnutls_require_proto) },
-#endif
   { "helo_data",            opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, helo_data) },
   { "hosts",                opt_stringptr,
   { "helo_data",            opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, helo_data) },
   { "hosts",                opt_stringptr,
@@ -142,18 +136,22 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, lmtp_ignore_quota) },
   { "max_rcpt",             opt_int | opt_public,
       (void *)offsetof(transport_instance, max_addresses) },
       (void *)offsetof(smtp_transport_options_block, lmtp_ignore_quota) },
   { "max_rcpt",             opt_int | opt_public,
       (void *)offsetof(transport_instance, max_addresses) },
-  { "multi_domain",         opt_bool | opt_public,
+  { "multi_domain",         opt_expand_bool | opt_public,
       (void *)offsetof(transport_instance, multi_domain) },
   { "port",                 opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, port) },
   { "protocol",             opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, protocol) },
       (void *)offsetof(transport_instance, multi_domain) },
   { "port",                 opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, port) },
   { "protocol",             opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, protocol) },
-  { "retry_include_ip_address", opt_bool,
+  { "retry_include_ip_address", opt_expand_bool,
       (void *)offsetof(smtp_transport_options_block, retry_include_ip_address) },
   { "serialize_hosts",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, serialize_hosts) },
   { "size_addition",        opt_int,
       (void *)offsetof(smtp_transport_options_block, size_addition) }
       (void *)offsetof(smtp_transport_options_block, retry_include_ip_address) },
   { "serialize_hosts",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, serialize_hosts) },
   { "size_addition",        opt_int,
       (void *)offsetof(smtp_transport_options_block, size_addition) }
+#ifdef SUPPORT_SOCKS
+ ,{ "socks_proxy",          opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, socks_proxy) }
+#endif
 #ifdef SUPPORT_TLS
  ,{ "tls_certificate",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_certificate) },
 #ifdef SUPPORT_TLS
  ,{ "tls_certificate",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_certificate) },
@@ -171,10 +169,8 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
   { "tls_try_verify_hosts", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_try_verify_hosts) },
       (void *)offsetof(smtp_transport_options_block, tls_tempfail_tryclear) },
   { "tls_try_verify_hosts", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_try_verify_hosts) },
-#ifdef EXPERIMENTAL_CERTNAMES
   { "tls_verify_cert_hostnames", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block,tls_verify_cert_hostnames)},
   { "tls_verify_cert_hostnames", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block,tls_verify_cert_hostnames)},
-#endif
   { "tls_verify_certificates", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
   { "tls_verify_hosts",     opt_stringptr,
   { "tls_verify_certificates", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_verify_certificates) },
   { "tls_verify_hosts",     opt_stringptr,
@@ -209,7 +205,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   NULL,                /* hosts_require_dane */
 #endif
 #ifndef DISABLE_PRDR
   NULL,                /* hosts_require_dane */
 #endif
 #ifndef DISABLE_PRDR
-  NULL,                /* hosts_try_prdr */
+  US"*",                /* hosts_try_prdr */
 #endif
 #ifndef DISABLE_OCSP
   US"*",               /* hosts_request_ocsp (except under DANE; tls_client_start()) */
 #endif
 #ifndef DISABLE_OCSP
   US"*",               /* hosts_request_ocsp (except under DANE; tls_client_start()) */
@@ -217,7 +213,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
 #endif
   NULL,                /* hosts_require_tls */
   NULL,                /* hosts_avoid_tls */
 #endif
   NULL,                /* hosts_require_tls */
   NULL,                /* hosts_avoid_tls */
-  US"*",               /* hosts_verify_avoid_tls */
+  NULL,                /* hosts_verify_avoid_tls */
   NULL,                /* hosts_avoid_pipelining */
   NULL,                /* hosts_avoid_esmtp */
   NULL,                /* hosts_nopass_tls */
   NULL,                /* hosts_avoid_pipelining */
   NULL,                /* hosts_avoid_esmtp */
   NULL,                /* hosts_nopass_tls */
@@ -234,32 +230,30 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   FALSE,               /* gethostbyname */
   TRUE,                /* dns_qualify_single */
   FALSE,               /* dns_search_parents */
   FALSE,               /* gethostbyname */
   TRUE,                /* dns_qualify_single */
   FALSE,               /* dns_search_parents */
-  NULL,                /* dnssec_request_domains */
-  NULL,                /* dnssec_require_domains */
+  { NULL, NULL },      /* dnssec_domains {request,require} */
   TRUE,                /* delay_after_cutoff */
   FALSE,               /* hosts_override */
   FALSE,               /* hosts_randomize */
   TRUE,                /* keepalive */
   FALSE,               /* lmtp_ignore_quota */
   TRUE,                /* delay_after_cutoff */
   FALSE,               /* hosts_override */
   FALSE,               /* hosts_randomize */
   TRUE,                /* keepalive */
   FALSE,               /* lmtp_ignore_quota */
+  NULL,                       /* expand_retry_include_ip_address */
   TRUE                 /* retry_include_ip_address */
   TRUE                 /* retry_include_ip_address */
+#ifdef SUPPORT_SOCKS
+ ,NULL                 /* socks_proxy */
+#endif
 #ifdef SUPPORT_TLS
  ,NULL,                /* tls_certificate */
   NULL,                /* tls_crl */
   NULL,                /* tls_privatekey */
   NULL,                /* tls_require_ciphers */
 #ifdef SUPPORT_TLS
  ,NULL,                /* tls_certificate */
   NULL,                /* tls_crl */
   NULL,                /* tls_privatekey */
   NULL,                /* tls_require_ciphers */
-  NULL,                /* gnutls_require_kx */
-  NULL,                /* gnutls_require_mac */
-  NULL,                /* gnutls_require_proto */
   NULL,                /* tls_sni */
   NULL,                /* tls_sni */
-  NULL,                /* tls_verify_certificates */
+  US"system",          /* tls_verify_certificates */
   EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
                        /* tls_dh_min_bits */
   TRUE,                /* tls_tempfail_tryclear */
   NULL,                /* tls_verify_hosts */
   EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
                        /* tls_dh_min_bits */
   TRUE,                /* tls_tempfail_tryclear */
   NULL,                /* tls_verify_hosts */
-  NULL                 /* tls_try_verify_hosts */
-# ifdef EXPERIMENTAL_CERTNAMES
- ,NULL                 /* tls_verify_cert_hostnames */
-# endif
+  US"*",               /* tls_try_verify_hosts */
+  US"*"                /* tls_verify_cert_hostnames */
 #endif
 #ifndef DISABLE_DKIM
  ,NULL,                /* dkim_canon */
 #endif
 #ifndef DISABLE_DKIM
  ,NULL,                /* dkim_canon */
@@ -271,14 +265,12 @@ smtp_transport_options_block smtp_transport_option_defaults = {
 #endif
 };
 
 #endif
 };
 
-#ifdef EXPERIMENTAL_DSN
 /* some DSN flags for use later */
 
 static int     rf_list[] = {rf_notify_never, rf_notify_success,
                             rf_notify_failure, rf_notify_delay };
 
 /* some DSN flags for use later */
 
 static int     rf_list[] = {rf_notify_never, rf_notify_success,
                             rf_notify_failure, rf_notify_delay };
 
-static uschar *rf_names[] = { "NEVER", "SUCCESS", "FAILURE", "DELAY" };
-#endif
+static uschar *rf_names[] = { US"NEVER", US"SUCCESS", US"FAILURE", US"DELAY" };
 
 
 
 
 
 
@@ -405,15 +397,6 @@ if (ob->hosts_override && ob->hosts != NULL) tblock->overrides_hosts = TRUE;
 for them, but do not do any lookups at this time. */
 
 host_build_hostlist(&(ob->fallback_hostlist), ob->fallback_hosts, FALSE);
 for them, but do not do any lookups at this time. */
 
 host_build_hostlist(&(ob->fallback_hostlist), ob->fallback_hosts, FALSE);
-
-#ifdef SUPPORT_TLS
-if (  ob->gnutls_require_kx
-   || ob->gnutls_require_mac
-   || ob->gnutls_require_proto)
-  log_write(0, LOG_MAIN, "WARNING: smtp transport options"
-    " gnutls_require_kx, gnutls_require_mac and gnutls_require_protocols"
-    " are obsolete\n");
-#endif
 }
 
 
 }
 
 
@@ -434,6 +417,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
   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
 
 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
@@ -444,7 +429,11 @@ Returns:       nothing
 
 static void
 set_errno(address_item *addrlist, int errno_value, uschar *msg, int rc,
 
 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;
 {
 address_item *addr;
 int orvalue = 0;
@@ -453,22 +442,43 @@ if (errno_value == ERRNO_CONNECTTIMEOUT)
   errno_value = ETIMEDOUT;
   orvalue = RTEF_CTOUT;
   }
   errno_value = ETIMEDOUT;
   orvalue = RTEF_CTOUT;
   }
-for (addr = addrlist; addr != NULL; addr = addr->next)
-  {
-  if (addr->transport_return < PENDING) continue;
-  addr->basic_errno = errno_value;
-  addr->more_errno |= orvalue;
-  if (msg != NULL)
+for (addr = addrlist; addr; addr = addr->next)
+  if (addr->transport_return >= PENDING)
     {
     {
-    addr->message = msg;
-    if (pass_message) setflag(addr, af_pass_message);
+    addr->basic_errno = errno_value;
+    addr->more_errno |= orvalue;
+    if (msg != NULL)
+      {
+      addr->message = msg;
+      if (pass_message) setflag(addr, af_pass_message);
+      }
+    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
+      }
     }
     }
-  addr->transport_return = rc;
-  if (host)
-    addr->host_used = host;
-  }
 }
 
 }
 
+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
+         );
+}
 
 
 /*************************************************
 
 
 /*************************************************
@@ -523,7 +533,7 @@ if (*errno_value == ETIMEDOUT)
 
 if (*errno_value == ERRNO_SMTPFORMAT)
   {
 
 if (*errno_value == ERRNO_SMTPFORMAT)
   {
-  uschar *malfresp = string_printing(buffer);
+  const uschar *malfresp = string_printing(buffer);
   while (isspace(*malfresp)) malfresp++;
   *message = *malfresp == 0
     ? string_sprintf("Malformed SMTP reply (an empty line) "
   while (isspace(*malfresp)) malfresp++;
   *message = *malfresp == 0
     ? string_sprintf("Malformed SMTP reply (an empty line) "
@@ -563,11 +573,21 @@ if (*errno_value == ERRNO_WRITEINCOMPLETE)
   return FALSE;
   }
 
   return FALSE;
   }
 
+#ifdef SUPPORT_I18N
+/* Handle lack of advertised SMTPUTF8, for international message */
+if (*errno_value == ERRNO_UTF8_FWD)
+  {
+  *message = US string_sprintf("utf8 support required but not offered for forwarding");
+  DEBUG(D_deliver|D_transport) debug_printf("%s\n", *message);
+  return TRUE;
+  }
+#endif
+
 /* Handle error responses from the remote mailer. */
 
 if (buffer[0] != 0)
   {
 /* Handle error responses from the remote mailer. */
 
 if (buffer[0] != 0)
   {
-  uschar *s = string_printing(buffer);
+  const uschar *s = string_printing(buffer);
   *message = US string_sprintf("SMTP error from remote mail server after %s%s: "
     "%s", pl, smtp_command, s);
   *pass_message = TRUE;
   *message = US string_sprintf("SMTP error from remote mail server after %s%s: "
     "%s", pl, smtp_command, s);
   *pass_message = TRUE;
@@ -612,6 +632,9 @@ write_logs(address_item *addr, host_item *host)
 {
 uschar * message = string_sprintf("H=%s [%s]", host->name, host->address);
 
 {
 uschar * message = string_sprintf("H=%s [%s]", host->name, host->address);
 
+if (LOGGING(outgoing_port))
+  message = string_sprintf("%s:%d", message,
+             host->port == PORT_NONE ? 25 : host->port);
 if (addr->message)
   {
   message = string_sprintf("%s: %s", message, addr->message);
 if (addr->message)
   {
   message = string_sprintf("%s: %s", message, addr->message);
@@ -622,25 +645,22 @@ if (addr->message)
   }
 else
   {
   }
 else
   {
-  if (log_extra_selector & LX_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));
-  deliver_msglog("%s %s %s\n", tod_stamp(tod_log), message,
-               strerror(addr->basic_errno));
+  const uschar * s = exim_errstr(addr->basic_errno);
+  log_write(0, LOG_MAIN, "%s %s", message, s);
+  deliver_msglog("%s %s %s\n", tod_stamp(tod_log), message, s);
   }
 }
 
 static void
 msglog_line(host_item * host, uschar * message)
 {
   }
 }
 
 static void
 msglog_line(host_item * host, uschar * message)
 {
-  deliver_msglog("%s H=%s [%s] %s\n", tod_stamp(tod_log),
-    host->name, host->address, message);
+deliver_msglog("%s H=%s [%s] %s\n", tod_stamp(tod_log),
+  host->name, host->address, message);
 }
 
 
 
 }
 
 
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
 /*************************************************
 *   Post-defer action                            *
 *************************************************/
 /*************************************************
 *   Post-defer action                            *
 *************************************************/
@@ -659,7 +679,7 @@ static void
 deferred_event_raise(address_item *addr, host_item *host)
 {
 uschar * action = addr->transport->event_action;
 deferred_event_raise(address_item *addr, host_item *host)
 {
 uschar * action = addr->transport->event_action;
-uschar * save_domain;
+const uschar * save_domain;
 uschar * save_local;
 
 if (!action)
 uschar * save_local;
 
 if (!action)
@@ -693,8 +713,6 @@ router_name = transport_name = NULL;
 }
 #endif
 
 }
 #endif
 
-
-
 /*************************************************
 *           Synchronize SMTP responses           *
 *************************************************/
 /*************************************************
 *           Synchronize SMTP responses           *
 *************************************************/
@@ -831,10 +849,9 @@ while (count-- > 0)
 
   else if (errno == ETIMEDOUT)
     {
 
   else if (errno == ETIMEDOUT)
     {
-    int save_errno = errno;
     uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
                          transport_rcpt_address(addr, include_affixes));
     uschar *message = string_sprintf("SMTP timeout after RCPT TO:<%s>",
                          transport_rcpt_address(addr, include_affixes));
-    set_errno(addrlist, save_errno, 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;
     retry_add_item(addr, addr->address_retry_key, 0);
     update_waiting = FALSE;
     return -1;
@@ -879,12 +896,22 @@ while (count-- > 0)
       addr->basic_errno = ERRNO_RCPT4XX;
       addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
 
       addr->basic_errno = ERRNO_RCPT4XX;
       addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
 
+#ifndef DISABLE_EVENT
+      event_defer_errno = addr->more_errno;
+      msg_event_raise(US"msg:rcpt:host:defer", addr);
+#endif
+
       /* Log temporary errors if there are more hosts to be tried.
       If not, log this last one in the == line. */
 
       if (host->next)
        log_write(0, LOG_MAIN, "H=%s [%s]: %s", host->name, host->address, addr->message);
 
       /* Log temporary errors if there are more hosts to be tried.
       If not, log this last one in the == line. */
 
       if (host->next)
        log_write(0, LOG_MAIN, "H=%s [%s]: %s", host->name, host->address, addr->message);
 
+#ifndef DISABLE_EVENT
+      else
+       msg_event_raise(US"msg:rcpt:defer", addr);
+#endif
+
       /* Do not put this message on the list of those waiting for specific
       hosts, as otherwise it is likely to be tried too often. */
 
       /* Do not put this message on the list of those waiting for specific
       hosts, as otherwise it is likely to be tried too often. */
 
@@ -970,8 +997,7 @@ uschar *fail_reason = US"server did not advertise AUTH support";
 
 smtp_authenticated = FALSE;
 client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
 
 smtp_authenticated = FALSE;
 client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
-require_auth = verify_check_this_host(&(ob->hosts_require_auth), NULL,
-  host->name, host->address, NULL);
+require_auth = verify_check_given_host(&ob->hosts_require_auth, host);
 
 if (is_esmtp && !regex_AUTH) regex_AUTH =
     regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
 
 if (is_esmtp && !regex_AUTH) regex_AUTH =
     regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
@@ -986,8 +1012,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
   regex match above. */
 
   if (require_auth == OK ||
   regex match above. */
 
   if (require_auth == OK ||
-      verify_check_this_host(&(ob->hosts_try_auth), NULL, host->name,
-       host->address, NULL) == OK)
+      verify_check_given_host(&ob->hosts_try_auth, host) == OK)
     {
     auth_instance *au;
     fail_reason = US"no common mechanisms were found";
     {
     auth_instance *au;
     fail_reason = US"no common mechanisms were found";
@@ -1085,7 +1110,8 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
          /* Internal problem, message in buffer. */
 
          case ERROR:
          /* Internal problem, message in buffer. */
 
          case ERROR:
-         set_errno(addrlist, 0, string_copy(buffer), DEFER, FALSE, NULL);
+         set_errno_nohost(addrlist, ERRNO_AUTHPROB, string_copy(buffer),
+                   DEFER, FALSE);
          return ERROR;
          }
 
          return ERROR;
          }
 
@@ -1099,9 +1125,9 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
 
 if (require_auth == OK && !smtp_authenticated)
   {
 
 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,
     string_sprintf("authentication required but %s", fail_reason), DEFER,
-    FALSE, NULL);
+    FALSE);
   return DEFER;
   }
 
   return DEFER;
   }
 
@@ -1140,7 +1166,7 @@ if (ob->authenticated_sender != NULL)
       {
       uschar *message = string_sprintf("failed to expand "
         "authenticated_sender: %s", expand_string_message);
       {
       uschar *message = string_sprintf("failed to expand "
         "authenticated_sender: %s", expand_string_message);
-      set_errno(addrlist, 0, message, DEFER, FALSE, NULL);
+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
       return TRUE;
       }
     }
       return TRUE;
       }
     }
@@ -1166,45 +1192,168 @@ return FALSE;
 
 
 #ifdef EXPERIMENTAL_DANE
 
 
 #ifdef EXPERIMENTAL_DANE
+/* Lookup TLSA record for host/port.
+Return:  OK            success with dnssec; DANE mode
+         DEFER         Do not use this host now, may retry later
+        FAIL_FORCED    No TLSA record; DANE not usable
+        FAIL           Do not use this connection
+*/
+
 int
 int
-tlsa_lookup(host_item * host, dns_answer * dnsa,
-  BOOL dane_required, BOOL * dane)
+tlsa_lookup(const host_item * host, dns_answer * dnsa, BOOL dane_required)
 {
 /* move this out to host.c given the similarity to dns_lookup() ? */
 uschar buffer[300];
 {
 /* move this out to host.c given the similarity to dns_lookup() ? */
 uschar buffer[300];
-uschar * fullname = buffer;
+const uschar * fullname = buffer;
 
 /* TLSA lookup string */
 (void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
 
 switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname))
   {
 
 /* TLSA lookup string */
 (void)sprintf(CS buffer, "_%d._tcp.%.256s", host->port, host->name);
 
 switch (dns_lookup(dnsa, buffer, T_TLSA, &fullname))
   {
-  case DNS_AGAIN:
-    return DEFER; /* just defer this TLS'd conn */
-
-  default:
-  case DNS_FAIL:
-    if (dane_required)
-      {
-      log_write(0, LOG_MAIN, "DANE error: TLSA lookup failed");
-      return FAIL;
-      }
-    break;
-
   case DNS_SUCCEED:
     if (!dns_is_secure(dnsa))
       {
       log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
       return DEFER;
       }
   case DNS_SUCCEED:
     if (!dns_is_secure(dnsa))
       {
       log_write(0, LOG_MAIN, "DANE error: TLSA lookup not DNSSEC");
       return DEFER;
       }
-    *dane = TRUE;
-    break;
+    return OK;
+
+  case DNS_AGAIN:
+    return DEFER; /* just defer this TLS'd conn */
+
+  case DNS_NOMATCH:
+    return dane_required ? FAIL : FAIL_FORCED;
+
+  default:
+  case DNS_FAIL:
+    return dane_required ? FAIL : DEFER;
   }
   }
-return OK;
 }
 #endif
 
 
 }
 #endif
 
 
+
+typedef struct smtp_compare_s
+{
+    uschar                          *current_sender_address;
+    struct transport_instance       *tblock;
+} smtp_compare_t;
+
+/*
+Create a unique string that identifies this message, it is based on
+sender_address, helo_data and tls_certificate if enabled.  */
+
+static uschar *
+smtp_local_identity(uschar * sender, struct transport_instance * tblock)
+{
+address_item * addr1;
+uschar * if1 = US"";
+uschar * helo1 = US"";
+#ifdef SUPPORT_TLS
+uschar * tlsc1 = US"";
+#endif
+uschar * save_sender_address = sender_address;
+uschar * local_identity = NULL;
+smtp_transport_options_block * ob =
+  (smtp_transport_options_block *)tblock->options_block;
+
+sender_address = sender;
+
+addr1 = deliver_make_addr (sender, TRUE);
+deliver_set_expansions(addr1);
+
+if (ob->interface)
+  if1 = expand_string(ob->interface);
+
+if (ob->helo_data)
+  helo1 = expand_string(ob->helo_data);
+
+#ifdef SUPPORT_TLS
+if (ob->tls_certificate)
+  tlsc1 = expand_string(ob->tls_certificate);
+local_identity = string_sprintf ("%s^%s^%s", if1, helo1, tlsc1);
+#else
+local_identity = string_sprintf ("%s^%s", if1, helo1);
+#endif
+
+deliver_set_expansions(NULL);
+sender_address = save_sender_address;
+
+return local_identity;
+}
+
+
+
+/* This routine is a callback that is called from transport_check_waiting.
+This function will evaluate the incoming message versus the previous
+message.  If the incoming message is using a different local identity then
+we will veto this new message.  */
+
+static BOOL
+smtp_are_same_identities(uschar * message_id, smtp_compare_t * s_compare)
+{
+uschar * message_local_identity,
+       * current_local_identity,
+       * new_sender_address;
+
+current_local_identity =
+  smtp_local_identity(s_compare->current_sender_address, s_compare->tblock);
+
+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;
+}
+
+
+
+uschar
+ehlo_response(uschar * buf, size_t bsize, uschar checks)
+{
+#ifdef SUPPORT_TLS
+if (checks & PEER_OFFERED_TLS)
+  if (pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+    checks &= ~PEER_OFFERED_TLS;
+#endif
+
+  if (  checks & PEER_OFFERED_IGNQ
+     && pcre_exec(regex_IGNOREQUOTA, NULL, CS buf, bsize, 0,
+                 PCRE_EOPT, NULL, 0) < 0)
+    checks &= ~PEER_OFFERED_IGNQ;
+
+#ifndef DISABLE_PRDR
+  if (  checks & PEER_OFFERED_PRDR
+     && pcre_exec(regex_PRDR, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+    checks &= ~PEER_OFFERED_PRDR;
+#endif
+
+#ifdef SUPPORT_I18N
+  if (  checks & PEER_OFFERED_UTF8
+     && pcre_exec(regex_UTF8, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+    checks &= ~PEER_OFFERED_UTF8;
+#endif
+
+  if (  checks & PEER_OFFERED_DSN
+     && pcre_exec(regex_DSN, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+    checks &= ~PEER_OFFERED_DSN;
+
+  if (  checks & PEER_OFFERED_PIPE
+     && pcre_exec(regex_PIPELINING, NULL, CS buf, bsize, 0,
+                 PCRE_EOPT, NULL, 0) < 0)
+    checks &= ~PEER_OFFERED_PIPE;
+
+  if (  checks & PEER_OFFERED_SIZE
+     && pcre_exec(regex_SIZE, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
+    checks &= ~PEER_OFFERED_SIZE;
+
+return checks;
+}
+
+
 /*************************************************
 *       Deliver address list to given host       *
 *************************************************/
 /*************************************************
 *       Deliver address list to given host       *
 *************************************************/
@@ -1231,8 +1380,6 @@ Arguments:
   port            default TCP/IP port to use, in host byte order
   interface       interface to bind to, or NULL
   tblock          transport instance block
   port            default TCP/IP port to use, in host byte order
   interface       interface to bind to, or NULL
   tblock          transport instance block
-  copy_host       TRUE if host set in addr->host_used must be copied, because
-                    it is specific to this call of the transport
   message_defer   set TRUE if yield is OK, but all addresses were deferred
                     because of a non-recipient, non-host failure, that is, a
                     4xx response to MAIL FROM, DATA, or ".". This is a defer
   message_defer   set TRUE if yield is OK, but all addresses were deferred
                     because of a non-recipient, non-host failure, that is, a
                     4xx response to MAIL FROM, DATA, or ".". This is a defer
@@ -1253,7 +1400,7 @@ Returns:          OK    - the connection was made and the delivery attempted;
 
 static int
 smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port,
 
 static int
 smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port,
-  uschar *interface, transport_instance *tblock, BOOL copy_host,
+  uschar *interface, transport_instance *tblock,
   BOOL *message_defer, BOOL suppress_tls)
 {
 address_item *addr;
   BOOL *message_defer, BOOL suppress_tls)
 {
 address_item *addr;
@@ -1276,22 +1423,30 @@ BOOL completed_address = FALSE;
 BOOL esmtp = TRUE;
 BOOL pending_MAIL;
 BOOL pass_message = FALSE;
 BOOL esmtp = TRUE;
 BOOL pending_MAIL;
 BOOL pass_message = FALSE;
+uschar peer_offered = 0;       /*XXX should this be handed on cf. tls_offered, smtp_use_dsn ? */
 #ifndef DISABLE_PRDR
 #ifndef DISABLE_PRDR
-BOOL prdr_offered = FALSE;
 BOOL prdr_active;
 #endif
 BOOL prdr_active;
 #endif
-#ifdef EXPERIMENTAL_DSN
-BOOL dsn_all_lasthop = TRUE;
+#ifdef SUPPORT_I18N
+BOOL utf8_needed = FALSE;
 #endif
 #endif
+BOOL dsn_all_lasthop = TRUE;
 #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
 BOOL dane = FALSE;
 #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
 BOOL dane = FALSE;
+BOOL dane_required = verify_check_given_host(&ob->hosts_require_dane, host) == OK;
 dns_answer tlsa_dnsa;
 #endif
 smtp_inblock inblock;
 smtp_outblock outblock;
 int max_rcpt = tblock->max_addresses;
 uschar *igquotstr = US"";
 dns_answer tlsa_dnsa;
 #endif
 smtp_inblock inblock;
 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 *helo_data = NULL;
+
 uschar *message = NULL;
 uschar new_message_id[MESSAGE_ID_LENGTH + 1];
 uschar *p;
 uschar *message = NULL;
 uschar new_message_id[MESSAGE_ID_LENGTH + 1];
 uschar *p;
@@ -1341,7 +1496,8 @@ tls_modify_variables(&tls_out);
 #ifndef SUPPORT_TLS
 if (smtps)
   {
 #ifndef SUPPORT_TLS
 if (smtps)
   {
-  set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE, NULL);
+  set_errno_nohost(addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
+           DEFER, FALSE);
   return ERROR;
   }
 #endif
   return ERROR;
   }
 #endif
@@ -1354,42 +1510,41 @@ if (continue_hostname == NULL)
   {
   /* This puts port into host->port */
   inblock.sock = outblock.sock =
   {
   /* This puts port into host->port */
   inblock.sock = outblock.sock =
-    smtp_connect(host, host_af, port, interface, ob->connect_timeout,
-                 ob->keepalive, ob->dscp
-#ifdef EXPERIMENTAL_EVENT
-                 , tblock->event_action
-#endif
-               );
+    smtp_connect(host, host_af, port, interface, ob->connect_timeout, tblock);
 
   if (inblock.sock < 0)
     {
 
   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;
     }
 
 #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
     {
     return DEFER;
     }
 
 #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
     {
-    BOOL dane_required;
-
     tls_out.dane_verified = FALSE;
     tls_out.tlsa_usage = 0;
 
     tls_out.dane_verified = FALSE;
     tls_out.tlsa_usage = 0;
 
-    dane_required = verify_check_this_host(&ob->hosts_require_dane, NULL,
-                             host->name, host->address, NULL) == OK;
-
     if (host->dnssec == DS_YES)
       {
       if(  dane_required
     if (host->dnssec == DS_YES)
       {
       if(  dane_required
-       || verify_check_this_host(&ob->hosts_try_dane, NULL,
-                             host->name, host->address, NULL) == OK
+       || verify_check_given_host(&ob->hosts_try_dane, host) == OK
        )
        )
-       if ((rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK)
-         return rc;
+       switch (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required))
+         {
+         case OK:              dane = TRUE; break;
+         case FAIL_FORCED:     break;
+         default:              set_errno_nohost(addrlist, ERRNO_DNSDEFER,
+                                 string_sprintf("DANE error: tlsa lookup %s",
+                                   rc == DEFER ? "DEFER" : "FAIL"),
+                                 rc, FALSE);
+                               return rc;
+         }
       }
     else if (dane_required)
       {
       }
     else if (dane_required)
       {
-      log_write(0, LOG_MAIN, "DANE error: %s lookup not DNSSEC", host->name);
+      set_errno_nohost(addrlist, ERRNO_DNSDEFER,
+       string_sprintf("DANE error: %s lookup not DNSSEC", host->name),
+       FAIL, FALSE);
       return FAIL;
       }
 
       return FAIL;
       }
 
@@ -1403,6 +1558,19 @@ if (continue_hostname == NULL)
   delayed till here so that $sending_interface and $sending_port are set. */
 
   helo_data = expand_string(ob->helo_data);
   delayed till here so that $sending_interface and $sending_port are set. */
 
   helo_data = expand_string(ob->helo_data);
+#ifdef SUPPORT_I18N
+  if (helo_data)
+    {
+    uschar * errstr = 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_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
+      yield = DEFER;
+      goto SEND_QUIT;
+      }
+    }
+#endif
 
   /* The first thing is to wait for an initial OK response. The dreaded "goto"
   is nevertheless a reasonably clean way of programming this kind of logic,
 
   /* The first thing is to wait for an initial OK response. The dreaded "goto"
   is nevertheless a reasonably clean way of programming this kind of logic,
@@ -1410,10 +1578,14 @@ if (continue_hostname == NULL)
 
   if (!smtps)
     {
 
   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
+#ifndef DISABLE_EVENT
       {
       uschar * s;
       lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
       {
       uschar * s;
       lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
@@ -1421,9 +1593,9 @@ if (continue_hostname == NULL)
       s = event_raise(tblock->event_action, US"smtp:connect", buffer);
       if (s)
        {
       s = event_raise(tblock->event_action, US"smtp:connect", buffer);
       if (s)
        {
-       set_errno(addrlist, 0,
+       set_errno_nohost(addrlist, ERRNO_EXPANDFAIL,
          string_sprintf("deferred by smtp:connect event expansion: %s", s),
          string_sprintf("deferred by smtp:connect event expansion: %s", s),
-         DEFER, FALSE, NULL);
+         DEFER, FALSE);
        yield = DEFER;
        goto SEND_QUIT;
        }
        yield = DEFER;
        goto SEND_QUIT;
        }
@@ -1437,7 +1609,7 @@ if (continue_hostname == NULL)
       {
       uschar *message = string_sprintf("failed to expand helo_data: %s",
         expand_string_message);
       {
       uschar *message = string_sprintf("failed to expand helo_data: %s",
         expand_string_message);
-      set_errno(addrlist, 0, message, DEFER, FALSE, NULL);
+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
       yield = DEFER;
       goto SEND_QUIT;
       }
       yield = DEFER;
       goto SEND_QUIT;
       }
@@ -1478,8 +1650,7 @@ goto SEND_QUIT;
   mailers use upper case for some reason (the RFC is quite clear about case
   independence) so, for peace of mind, I gave in. */
 
   mailers use upper case for some reason (the RFC is quite clear about case
   independence) so, for peace of mind, I gave in. */
 
-  esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL,
-     host->name, host->address, NULL) != OK;
+  esmtp = verify_check_given_host(&ob->hosts_avoid_esmtp, host) != OK;
 
   /* Alas; be careful, since this goto is not an error-out, so conceivably
   we might set data between here and the target which we assume to exist
 
   /* Alas; be careful, since this goto is not an error-out, so conceivably
   we might set data between here and the target which we assume to exist
@@ -1503,9 +1674,18 @@ goto SEND_QUIT;
     if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
            ob->command_timeout))
       {
     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;
       }
       esmtp = FALSE;
       }
+#ifdef EXPERIMENTAL_DSN_INFO
+    helo_response = string_copy(buffer);
+#endif
     }
   else
     {
     }
   else
     {
@@ -1515,36 +1695,33 @@ goto SEND_QUIT;
 
   if (!esmtp)
     {
 
   if (!esmtp)
     {
+    BOOL good_response;
+
     if (smtp_write_command(&outblock, FALSE, "HELO %s\r\n", helo_data) < 0)
       goto SEND_FAILED;
     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
-  lmtp_ignore_quota option was set. */
-
-  igquotstr = (lmtp && ob->lmtp_ignore_quota &&
-    pcre_exec(regex_IGNOREQUOTA, NULL, CS buffer, Ustrlen(CS buffer), 0,
-      PCRE_EOPT, NULL, 0) >= 0)? US" IGNOREQUOTA" : US"";
+  if (esmtp || lmtp)
+    peer_offered = ehlo_response(buffer, Ustrlen(buffer),
+      PEER_OFFERED_TLS
+      | 0      /* IGNQ checked later */
+      | 0      /* PRDR checked later */
+      | 0      /* UTF8 checked later */
+      | 0      /* DSN  checked later */
+      | 0      /* PIPE checked later */
+      | 0      /* SIZE checked later */
+      );
 
   /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
 
 #ifdef SUPPORT_TLS
 
   /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
 
 #ifdef SUPPORT_TLS
-  tls_offered = esmtp &&
-    pcre_exec(regex_STARTTLS, NULL, CS buffer, Ustrlen(buffer), 0,
-      PCRE_EOPT, NULL, 0) >= 0;
-#endif
-
-#ifndef DISABLE_PRDR
-  prdr_offered = esmtp &&
-    (pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(buffer), 0,
-      PCRE_EOPT, NULL, 0) >= 0) &&
-    (verify_check_this_host(&(ob->hosts_try_prdr), NULL, host->name,
-      host->address, NULL) == OK);
-
-  if (prdr_offered)
-    {DEBUG(D_transport) debug_printf("PRDR usable\n");}
+  tls_offered = !!(peer_offered & PEER_OFFERED_TLS);
 #endif
   }
 
 #endif
   }
 
@@ -1555,6 +1732,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. */
 
 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. */
+
 else
   {
   inblock.sock = outblock.sock = fileno(stdin);
 else
   {
   inblock.sock = outblock.sock = fileno(stdin);
@@ -1571,9 +1753,9 @@ the client not be required to use TLS. If the response is bad, copy the buffer
 for error analysis. */
 
 #ifdef SUPPORT_TLS
 for error analysis. */
 
 #ifdef SUPPORT_TLS
-if (tls_offered && !suppress_tls &&
-      verify_check_this_host(&(ob->hosts_avoid_tls), NULL, host->name,
-        host->address, NULL) != OK)
+if (  tls_offered
+   && !suppress_tls
+   && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK)
   {
   uschar buffer2[4096];
   if (smtp_write_command(&outblock, FALSE, "STARTTLS\r\n") < 0)
   {
   uschar buffer2[4096];
   if (smtp_write_command(&outblock, FALSE, "STARTTLS\r\n") < 0)
@@ -1589,8 +1771,10 @@ if (tls_offered && !suppress_tls &&
   if (!smtp_read_response(&inblock, buffer2, sizeof(buffer2), '2',
       ob->command_timeout))
     {
   if (!smtp_read_response(&inblock, buffer2, sizeof(buffer2), '2',
       ob->command_timeout))
     {
-    if (errno != 0 || buffer2[0] == 0 ||
-         (buffer2[0] == '4' && !ob->tls_tempfail_tryclear))
+    if (  errno != 0
+       || buffer2[0] == 0
+       || (buffer2[0] == '4' && !ob->tls_tempfail_tryclear)
+       )
       {
       Ustrncpy(buffer, buffer2, sizeof(buffer));
       goto RESPONSE_FAILED;
       {
       Ustrncpy(buffer, buffer2, sizeof(buffer));
       goto RESPONSE_FAILED;
@@ -1614,6 +1798,15 @@ if (tls_offered && !suppress_tls &&
 
     if (rc != OK)
       {
 
     if (rc != OK)
       {
+# ifdef EXPERIMENTAL_DANE
+      if (rc == DEFER && dane)
+       {
+       log_write(0, LOG_MAIN,
+         "DANE attempt failed; no TLS connection to %s [%s]",
+         host->name, host->address);
+       }
+# endif
+
       save_errno = ERRNO_TLSFAILURE;
       message = US"failure while setting up TLS session";
       send_quit = FALSE;
       save_errno = ERRNO_TLSFAILURE;
       message = US"failure while setting up TLS session";
       send_quit = FALSE;
@@ -1622,7 +1815,7 @@ if (tls_offered && !suppress_tls &&
 
     /* TLS session is set up */
 
 
     /* TLS session is set up */
 
-    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;
       if (addr->transport_return == PENDING_DEFER)
         {
         addr->cipher = tls_out.cipher;
@@ -1647,6 +1840,8 @@ start of the Exim process (in exim.c). */
 if (tls_out.active >= 0)
   {
   char *greeting_cmd;
 if (tls_out.active >= 0)
   {
   char *greeting_cmd;
+  BOOL good_response;
+
   if (helo_data == NULL)
     {
     helo_data = expand_string(ob->helo_data);
   if (helo_data == NULL)
     {
     helo_data = expand_string(ob->helo_data);
@@ -1654,7 +1849,7 @@ if (tls_out.active >= 0)
       {
       uschar *message = string_sprintf("failed to expand helo_data: %s",
         expand_string_message);
       {
       uschar *message = string_sprintf("failed to expand helo_data: %s",
         expand_string_message);
-      set_errno(addrlist, 0, message, DEFER, FALSE, NULL);
+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
       yield = DEFER;
       goto SEND_QUIT;
       }
       yield = DEFER;
       goto SEND_QUIT;
       }
@@ -1663,8 +1858,12 @@ if (tls_out.active >= 0)
   /* For SMTPS we need to wait for the initial OK response. */
   if (smtps)
     {
   /* 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)
     }
 
   if (esmtp)
@@ -1679,9 +1878,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_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
   }
 
 /* If the host is required to use a secure channel, ensure that we
@@ -1691,8 +1893,7 @@ else if (
 # ifdef EXPERIMENTAL_DANE
        dane ||
 # endif
 # ifdef EXPERIMENTAL_DANE
        dane ||
 # endif
-        verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
-            host->address, NULL) == OK
+        verify_check_given_host(&ob->hosts_require_tls, host) == OK
        )
   {
   save_errno = ERRNO_TLSREQUIRED;
        )
   {
   save_errno = ERRNO_TLSREQUIRED;
@@ -1714,50 +1915,53 @@ if (continue_hostname == NULL
 #endif
     )
   {
 #endif
     )
   {
+  if (esmtp || lmtp)
+    peer_offered = ehlo_response(buffer, Ustrlen(buffer),
+      0 /* no TLS */
+      | (lmtp && ob->lmtp_ignore_quota ? PEER_OFFERED_IGNQ : 0)
+      | PEER_OFFERED_PRDR
+#ifdef SUPPORT_I18N
+      | (addrlist->prop.utf8_msg ? PEER_OFFERED_UTF8 : 0)
+       /*XXX if we hand peercaps on to continued-conn processes,
+             must not depend on this addr */
+#endif
+      | PEER_OFFERED_DSN
+      | PEER_OFFERED_PIPE
+      | (ob->size_addition >= 0 ? PEER_OFFERED_SIZE : 0)
+      );
+
   /* Set for IGNOREQUOTA if the response to LHLO specifies support and the
   lmtp_ignore_quota option was set. */
 
   /* Set for IGNOREQUOTA if the response to LHLO specifies support and the
   lmtp_ignore_quota option was set. */
 
-  igquotstr = (lmtp && ob->lmtp_ignore_quota &&
-    pcre_exec(regex_IGNOREQUOTA, NULL, CS buffer, Ustrlen(CS buffer), 0,
-      PCRE_EOPT, NULL, 0) >= 0)? US" IGNOREQUOTA" : US"";
+  igquotstr = peer_offered & PEER_OFFERED_IGNQ ? US" IGNOREQUOTA" : US"";
 
   /* If the response to EHLO specified support for the SIZE parameter, note
   this, provided size_addition is non-negative. */
 
 
   /* If the response to EHLO specified support for the SIZE parameter, note
   this, provided size_addition is non-negative. */
 
-  smtp_use_size = esmtp && ob->size_addition >= 0 &&
-    pcre_exec(regex_SIZE, NULL, CS buffer, Ustrlen(CS buffer), 0,
-      PCRE_EOPT, NULL, 0) >= 0;
+  smtp_use_size = !!(peer_offered & PEER_OFFERED_SIZE);
 
   /* Note whether the server supports PIPELINING. If hosts_avoid_esmtp matched
   the current host, esmtp will be false, so PIPELINING can never be used. If
   the current host matches hosts_avoid_pipelining, don't do it. */
 
 
   /* Note whether the server supports PIPELINING. If hosts_avoid_esmtp matched
   the current host, esmtp will be false, so PIPELINING can never be used. If
   the current host matches hosts_avoid_pipelining, don't do it. */
 
-  smtp_use_pipelining = esmtp &&
-    verify_check_this_host(&(ob->hosts_avoid_pipelining), NULL, host->name,
-      host->address, NULL) != OK &&
-    pcre_exec(regex_PIPELINING, NULL, CS buffer, Ustrlen(CS buffer), 0,
-      PCRE_EOPT, NULL, 0) >= 0;
+  smtp_use_pipelining = peer_offered & PEER_OFFERED_PIPE
+    && verify_check_given_host(&ob->hosts_avoid_pipelining, host) != OK;
 
   DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
 
   DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
-    smtp_use_pipelining? "" : "not ");
+    smtp_use_pipelining ? "" : "not ");
 
 #ifndef DISABLE_PRDR
 
 #ifndef DISABLE_PRDR
-  prdr_offered = esmtp &&
-    pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(CS buffer), 0,
-      PCRE_EOPT, NULL, 0) >= 0 &&
-    verify_check_this_host(&(ob->hosts_try_prdr), NULL, host->name,
-      host->address, NULL) == OK;
+  if (  peer_offered & PEER_OFFERED_PRDR
+     && verify_check_given_host(&ob->hosts_try_prdr, host) != OK)
+    peer_offered &= ~PEER_OFFERED_PRDR;
 
 
-  if (prdr_offered)
+  if (peer_offered & PEER_OFFERED_PRDR)
     {DEBUG(D_transport) debug_printf("PRDR usable\n");}
 #endif
 
     {DEBUG(D_transport) debug_printf("PRDR usable\n");}
 #endif
 
-#ifdef EXPERIMENTAL_DSN
   /* Note if the server supports DSN */
   /* Note if the server supports DSN */
-  smtp_use_dsn = esmtp && pcre_exec(regex_DSN, NULL, CS buffer, (int)Ustrlen(CS buffer), 0,
-       PCRE_EOPT, NULL, 0) >= 0;
-  DEBUG(D_transport) debug_printf("use_dsn=%d\n", smtp_use_dsn);
-#endif
+  smtp_use_dsn = !!(peer_offered & PEER_OFFERED_DSN);
+  DEBUG(D_transport) debug_printf("%susing DSN\n", smtp_use_dsn ? "" : "not ");
 
   /* Note if the response to EHLO specifies support for the AUTH extension.
   If it has, check that this host is one we want to authenticate to, and do
 
   /* Note if the response to EHLO specifies support for the AUTH extension.
   If it has, check that this host is one we want to authenticate to, and do
@@ -1779,6 +1983,23 @@ message-specific. */
 
 setting_up = FALSE;
 
 
 setting_up = FALSE;
 
+#ifdef SUPPORT_I18N
+if (addrlist->prop.utf8_msg)
+  {
+  utf8_needed =  !addrlist->prop.utf8_downcvt
+             && !addrlist->prop.utf8_downcvt_maybe;
+  DEBUG(D_transport) if (!utf8_needed) debug_printf("utf8: %s downconvert\n",
+    addrlist->prop.utf8_downcvt ? "mandatory" : "optional");
+  }
+
+/* If this is an international message we need the host to speak SMTPUTF8 */
+if (utf8_needed && !(peer_offered & PEER_OFFERED_UTF8))
+  {
+  errno = ERRNO_UTF8_FWD;
+  goto RESPONSE_FAILED;
+  }
+#endif
+
 /* If there is a filter command specified for this transport, we can now
 set it up. This cannot be done until the identify of the host is known. */
 
 /* If there is a filter command specified for this transport, we can now
 set it up. This cannot be done until the identify of the host is known. */
 
@@ -1796,8 +2017,8 @@ if (tblock->filter_command != NULL)
 
   if (!rc)
     {
 
   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;
     }
     yield = ERROR;
     goto SEND_QUIT;
     }
@@ -1838,8 +2059,7 @@ if (smtp_use_size)
 
 #ifndef DISABLE_PRDR
 prdr_active = FALSE;
 
 #ifndef DISABLE_PRDR
 prdr_active = FALSE;
-if (prdr_offered)
-  {
+if (peer_offered & PEER_OFFERED_PRDR)
   for (addr = first_addr; addr; addr = addr->next)
     if (addr->transport_return == PENDING_DEFER)
       {
   for (addr = first_addr; addr; addr = addr->next)
     if (addr->transport_return == PENDING_DEFER)
       {
@@ -1852,31 +2072,39 @@ if (prdr_offered)
          }
       break;
       }
          }
       break;
       }
-  }
 #endif
 
 #endif
 
-#ifdef EXPERIMENTAL_DSN
+#ifdef SUPPORT_I18N
+if (  addrlist->prop.utf8_msg
+   && !addrlist->prop.utf8_downcvt
+   && peer_offered & PEER_OFFERED_UTF8
+   )
+  sprintf(CS p, " SMTPUTF8"), p += 9;
+#endif
+
 /* check if all addresses have lasthop flag */
 /* do not send RET and ENVID if true */
 /* check if all addresses have lasthop flag */
 /* do not send RET and ENVID if true */
-dsn_all_lasthop = TRUE;
-for (addr = first_addr;
+for (dsn_all_lasthop = TRUE, addr = first_addr;
      address_count < max_rcpt && addr != NULL;
      addr = addr->next)
   if ((addr->dsn_flags & rf_dsnlasthop) != 1)
      address_count < max_rcpt && addr != NULL;
      addr = addr->next)
   if ((addr->dsn_flags & rf_dsnlasthop) != 1)
+    {
     dsn_all_lasthop = FALSE;
     dsn_all_lasthop = FALSE;
+    break;
+    }
 
 /* Add any DSN flags to the mail command */
 
 
 /* Add any DSN flags to the mail command */
 
-if ((smtp_use_dsn) && (dsn_all_lasthop == FALSE))
+if (smtp_use_dsn && !dsn_all_lasthop)
   {
   if (dsn_ret == dsn_ret_hdrs)
     {
   {
   if (dsn_ret == dsn_ret_hdrs)
     {
-    strcpy(p, " RET=HDRS");
+    Ustrcpy(p, " RET=HDRS");
     while (*p) p++;
     }
   else if (dsn_ret == dsn_ret_full)
     {
     while (*p) p++;
     }
   else if (dsn_ret == dsn_ret_full)
     {
-    strcpy(p, " RET=FULL");
+    Ustrcpy(p, " RET=FULL");
     while (*p) p++;
     }
   if (dsn_envid != NULL)
     while (*p) p++;
     }
   if (dsn_envid != NULL)
@@ -1885,7 +2113,6 @@ if ((smtp_use_dsn) && (dsn_all_lasthop == FALSE))
     while (*p) p++;
     }
   }
     while (*p) p++;
     }
   }
-#endif
 
 /* If an authenticated_sender override has been specified for this transport
 instance, expand it. If the expansion is forced to fail, and there was already
 
 /* If an authenticated_sender override has been specified for this transport
 instance, expand it. If the expansion is forced to fail, and there was already
@@ -1908,28 +2135,53 @@ buffer. */
 
 pending_MAIL = TRUE;     /* The block starts with MAIL */
 
 
 pending_MAIL = TRUE;     /* The block starts with MAIL */
 
-rc = smtp_write_command(&outblock, smtp_use_pipelining,
-       "MAIL FROM:<%s>%s\r\n", return_path, buffer);
+  {
+  uschar * s = return_path;
+#ifdef SUPPORT_I18N
+  uschar * errstr = NULL;
+
+  /* If we must downconvert, do the from-address here.  Remember we had to
+  for the to-addresses (done below), and also (ugly) for re-doing when building
+  the delivery log line. */
+
+  if (  addrlist->prop.utf8_msg
+     && (addrlist->prop.utf8_downcvt || !(peer_offered & PEER_OFFERED_UTF8))
+     )
+    {
+    if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr)
+      {
+      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
+      yield = ERROR;
+      goto SEND_QUIT;
+      }
+    setflag(addrlist, af_utf8_downcvt);
+    }
+#endif
+
+  rc = smtp_write_command(&outblock, smtp_use_pipelining,
+         "MAIL FROM:<%s>%s\r\n", s, buffer);
+  }
+
 mail_command = string_copy(big_buffer);  /* Save for later error message */
 
 switch(rc)
   {
   case -1:                /* Transmission error */
 mail_command = string_copy(big_buffer);  /* Save for later error message */
 
 switch(rc)
   {
   case -1:                /* Transmission error */
-  goto SEND_FAILED;
+    goto SEND_FAILED;
 
   case +1:                /* Block was sent */
 
   case +1:                /* Block was sent */
-  if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
        ob->command_timeout))
        ob->command_timeout))
-    {
-    if (errno == 0 && buffer[0] == '4')
       {
       {
-      errno = ERRNO_MAIL4XX;
-      addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+      if (errno == 0 && buffer[0] == '4')
+       {
+       errno = ERRNO_MAIL4XX;
+       addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+       }
+      goto RESPONSE_FAILED;
       }
       }
-    goto RESPONSE_FAILED;
-    }
-  pending_MAIL = FALSE;
-  break;
+    pending_MAIL = FALSE;
+    break;
   }
 
 /* Pass over all the relevant recipient addresses for this host, which are the
   }
 
 /* Pass over all the relevant recipient addresses for this host, which are the
@@ -1951,17 +2203,15 @@ for (addr = first_addr;
   {
   int count;
   BOOL no_flush;
   {
   int count;
   BOOL no_flush;
+  uschar * rcpt_addr;
 
 
-#ifdef EXPERIMENTAL_DSN
   addr->dsn_aware = smtp_use_dsn ? dsn_support_yes : dsn_support_no;
   addr->dsn_aware = smtp_use_dsn ? dsn_support_yes : dsn_support_no;
-#endif
 
   if (addr->transport_return != PENDING_DEFER) continue;
 
   address_count++;
   no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next != NULL);
 
 
   if (addr->transport_return != PENDING_DEFER) continue;
 
   address_count++;
   no_flush = smtp_use_pipelining && (!mua_wrapper || addr->next != NULL);
 
-#ifdef EXPERIMENTAL_DSN
   /* Add any DSN flags to the rcpt command and add to the sent string */
 
   p = buffer;
   /* Add any DSN flags to the rcpt command and add to the sent string */
 
   p = buffer;
@@ -1973,14 +2223,14 @@ for (addr = first_addr;
       {
       int i;
       BOOL first = TRUE;
       {
       int i;
       BOOL first = TRUE;
-      strcpy(p, " NOTIFY=");
+      Ustrcpy(p, " NOTIFY=");
       while (*p) p++;
       for (i = 0; i < 4; i++)
         if ((addr->dsn_flags & rf_list[i]) != 0)
           {
           if (!first) *p++ = ',';
           first = FALSE;
       while (*p) p++;
       for (i = 0; i < 4; i++)
         if ((addr->dsn_flags & rf_list[i]) != 0)
           {
           if (!first) *p++ = ',';
           first = FALSE;
-          strcpy(p, rf_names[i]);
+          Ustrcpy(p, rf_names[i]);
           while (*p) p++;
           }
       }
           while (*p) p++;
           }
       }
@@ -1992,22 +2242,31 @@ for (addr = first_addr;
       while (*p) p++;
       }
     }
       while (*p) p++;
       }
     }
-#endif
-
 
   /* Now send the RCPT command, and process outstanding responses when
   necessary. After a timeout on RCPT, we just end the function, leaving the
   yield as OK, because this error can often mean that there is a problem with
   just one address, so we don't want to delay the host. */
 
 
   /* Now send the RCPT command, and process outstanding responses when
   necessary. After a timeout on RCPT, we just end the function, leaving the
   yield as OK, because this error can often mean that there is a problem with
   just one address, so we don't want to delay the host. */
 
-#ifdef EXPERIMENTAL_DSN
-  count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n",
-    transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr, buffer);
-#else
-  count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s\r\n",
-    transport_rcpt_address(addr, tblock->rcpt_include_affixes), igquotstr);
+  rcpt_addr = transport_rcpt_address(addr, tblock->rcpt_include_affixes);
+
+#ifdef SUPPORT_I18N
+  {
+  uschar * dummy_errstr;
+  if (  testflag(addrlist, af_utf8_downcvt)
+     && (rcpt_addr = string_address_utf8_to_alabel(rcpt_addr, &dummy_errstr),
+        dummy_errstr
+     )  )
+    {
+    errno = ERRNO_EXPANDFAIL;
+    goto SEND_FAILED;
+    }
+  }
 #endif
 
 #endif
 
+  count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n",
+    rcpt_addr, igquotstr, buffer);
+
   if (count < 0) goto SEND_FAILED;
   if (count > 0)
     {
   if (count < 0) goto SEND_FAILED;
   if (count > 0)
     {
@@ -2039,16 +2298,15 @@ RCPT. */
 if (mua_wrapper)
   {
   address_item *badaddr;
 if (mua_wrapper)
   {
   address_item *badaddr;
-  for (badaddr = first_addr; badaddr != NULL; badaddr = badaddr->next)
-    {
-    if (badaddr->transport_return != PENDING_OK) break;
-    }
-  if (badaddr != NULL)
-    {
-    set_errno(addrlist, 0, badaddr->message, FAIL,
-      testflag(badaddr, af_pass_message), NULL);
-    ok = FALSE;
-    }
+  for (badaddr = first_addr; badaddr; badaddr = badaddr->next)
+    if (badaddr->transport_return != PENDING_OK)
+      {
+      /*XXX could we find a better errno than 0 here? */
+      set_errno_nohost(addrlist, 0, badaddr->message, FAIL,
+       testflag(badaddr, af_pass_message));
+      ok = FALSE;
+      break;
+      }
   }
 
 /* If ok is TRUE, we know we have got at least one good recipient, and must now
   }
 
 /* If ok is TRUE, we know we have got at least one good recipient, and must now
@@ -2205,33 +2463,21 @@ if (!ok) ok = TRUE; else
     int flag = '=';
     int delivery_time = (int)(time(NULL) - start_delivery_time);
     int len;
     int flag = '=';
     int delivery_time = (int)(time(NULL) - start_delivery_time);
     int len;
-    host_item *thost;
     uschar *conf = NULL;
     send_rset = FALSE;
 
     uschar *conf = NULL;
     send_rset = FALSE;
 
-    /* Make a copy of the host if it is local to this invocation
-    of the transport. */
-
-    if (copy_host)
-      {
-      thost = store_get(sizeof(host_item));
-      *thost = *host;
-      thost->name = string_copy(host->name);
-      thost->address = string_copy(host->address);
-      }
-    else thost = host;
-
     /* Set up confirmation if needed - applies only to SMTP */
 
     if (
     /* Set up confirmation if needed - applies only to SMTP */
 
     if (
-#ifndef EXPERIMENTAL_EVENT
-          (log_extra_selector & LX_smtp_confirmation) != 0 &&
+#ifdef DISABLE_EVENT
+          LOGGING(smtp_confirmation) &&
 #endif
           !lmtp
        )
       {
 #endif
           !lmtp
        )
       {
-      uschar *s = string_printing(buffer);
-      conf = (s == buffer)? (uschar *)string_copy(s) : s;
+      const uschar *s = string_printing(buffer);
+      /* deconst cast ok here as string_printing was checked to have alloc'n'copied */
+      conf = (s == buffer)? (uschar *)string_copy(s) : US s;
       }
 
     /* Process all transported addresses - for LMTP or PRDR, read a status for
       }
 
     /* Process all transported addresses - for LMTP or PRDR, read a status for
@@ -2279,10 +2525,11 @@ if (!ok) ok = TRUE; else
           continue;
           }
         completed_address = TRUE;   /* NOW we can set this flag */
           continue;
           }
         completed_address = TRUE;   /* NOW we can set this flag */
-        if ((log_extra_selector & LX_smtp_confirmation) != 0)
+        if (LOGGING(smtp_confirmation))
           {
           {
-          uschar *s = string_printing(buffer);
-          conf = (s == buffer)? (uschar *)string_copy(s) : s;
+          const uschar *s = string_printing(buffer);
+         /* deconst cast ok here as string_printing was checked to have alloc'n'copied */
+          conf = (s == buffer)? (uschar *)string_copy(s) : US s;
           }
         }
 
           }
         }
 
@@ -2291,7 +2538,7 @@ if (!ok) ok = TRUE; else
 
       addr->transport_return = OK;
       addr->more_errno = delivery_time;
 
       addr->transport_return = OK;
       addr->more_errno = delivery_time;
-      addr->host_used = thost;
+      addr->host_used = host;
       addr->special_action = flag;
       addr->message = conf;
 #ifndef DISABLE_PRDR
       addr->special_action = flag;
       addr->message = conf;
 #ifndef DISABLE_PRDR
@@ -2313,7 +2560,7 @@ if (!ok) ok = TRUE; else
         else
           sprintf(CS buffer, "%.500s\n", addr->unique);
 
         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 "
         len = Ustrlen(CS buffer);
         if (write(journal_fd, buffer, len) != len)
           log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
@@ -2350,7 +2597,7 @@ if (!ok) ok = TRUE; else
           else
             sprintf(CS buffer, "%.500s\n", addr->unique);
 
           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 "
           len = Ustrlen(CS buffer);
           if (write(journal_fd, buffer, len) != len)
             log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
@@ -2380,22 +2627,27 @@ the problem is not related to this specific message. */
 
 if (!ok)
   {
 
 if (!ok)
   {
-  int code;
+  int code, set_rc;
+  uschar * set_message;
 
   RESPONSE_FAILED:
 
   RESPONSE_FAILED:
-  save_errno = errno;
-  message = NULL;
-  send_quit = check_response(host, &save_errno, addrlist->more_errno,
-    buffer, &code, &message, &pass_message);
-  goto FAILED;
+    {
+    save_errno = errno;
+    message = NULL;
+    send_quit = check_response(host, &save_errno, addrlist->more_errno,
+      buffer, &code, &message, &pass_message);
+    goto FAILED;
+    }
 
   SEND_FAILED:
 
   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;
+    }
 
   /* 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
 
   /* 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
@@ -2416,16 +2668,14 @@ if (!ok)
 
   FAILED:
   ok = FALSE;                /* For when reached by GOTO */
 
   FAILED:
   ok = FALSE;                /* For when reached by GOTO */
+  set_message = message;
 
   if (setting_up)
     {
     if (code == '5')
 
   if (setting_up)
     {
     if (code == '5')
-      set_errno(addrlist, save_errno, message, FAIL, pass_message, host);
+      set_rc = FAIL;
     else
     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
     }
 
   /* We want to handle timeouts after MAIL or "." and loss of connection after
@@ -2440,24 +2690,29 @@ if (!ok)
 
     switch(save_errno)
       {
 
     switch(save_errno)
       {
+#ifdef SUPPORT_I18N
+      case ERRNO_UTF8_FWD:
+        code = '5';
+      /*FALLTHROUGH*/
+#endif
       case 0:
       case ERRNO_MAIL4XX:
       case ERRNO_DATA4XX:
       case 0:
       case ERRNO_MAIL4XX:
       case ERRNO_DATA4XX:
-      message_error = TRUE;
-      break;
+       message_error = TRUE;
+       break;
 
       case ETIMEDOUT:
 
       case ETIMEDOUT:
-      message_error = Ustrncmp(smtp_command,"MAIL",4) == 0 ||
-                      Ustrncmp(smtp_command,"end ",4) == 0;
-      break;
+       message_error = Ustrncmp(smtp_command,"MAIL",4) == 0 ||
+                       Ustrncmp(smtp_command,"end ",4) == 0;
+       break;
 
       case ERRNO_SMTPCLOSED:
 
       case ERRNO_SMTPCLOSED:
-      message_error = Ustrncmp(smtp_command,"end ",4) == 0;
-      break;
+       message_error = Ustrncmp(smtp_command,"end ",4) == 0;
+       break;
 
       default:
 
       default:
-      message_error = FALSE;
-      break;
+       message_error = FALSE;
+       break;
       }
 
     /* Handle the cases that are treated as message errors. These are:
       }
 
     /* Handle the cases that are treated as message errors. These are:
@@ -2465,6 +2720,7 @@ if (!ok)
       (a) negative response or timeout after MAIL
       (b) negative response after DATA
       (c) negative response or timeout or dropped connection after "."
       (a) negative response or timeout after MAIL
       (b) negative response after DATA
       (c) negative response or timeout or dropped connection after "."
+      (d) utf8 support required and not offered
 
     It won't be a negative response or timeout after RCPT, as that is dealt
     with separately above. The action in all cases is to set an appropriate
 
     It won't be a negative response or timeout after RCPT, as that is dealt
     with separately above. The action in all cases is to set an appropriate
@@ -2478,14 +2734,15 @@ if (!ok)
     if (message_error)
       {
       if (mua_wrapper) code = '5';  /* Force hard failure in wrapper mode */
     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);
 
       /* If there's an errno, the message contains just the identity of
       the host. */
 
 
       /* If there's an errno, the message contains just the identity of
       the host. */
 
-      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);
         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);
@@ -2502,11 +2759,17 @@ if (!ok)
 
     else
       {
 
     else
       {
+      set_rc = DEFER;
       yield = (save_errno == ERRNO_CHHEADER_FAIL ||
                save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER;
       yield = (save_errno == ERRNO_CHHEADER_FAIL ||
                save_errno == ERRNO_FILTER_FAIL)? ERROR : DEFER;
-      set_errno(addrlist, save_errno, message, DEFER, pass_message, host);
       }
     }
       }
     }
+
+  set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host
+#ifdef EXPERIMENTAL_DSN_INFO
+           , smtp_greeting, helo_response
+#endif
+           );
   }
 
 
   }
 
 
@@ -2546,15 +2809,21 @@ DEBUG(D_transport)
 if (completed_address && ok && send_quit)
   {
   BOOL more;
 if (completed_address && ok && send_quit)
   {
   BOOL more;
-  if (first_addr != NULL || continue_more ||
-        (
-           (tls_out.active < 0 ||
-           verify_check_this_host(&(ob->hosts_nopass_tls), NULL, host->name,
-             host->address, NULL) != OK)
+  smtp_compare_t t_compare;
+
+  t_compare.tblock = tblock;
+  t_compare.current_sender_address = sender_address;
+
+  if (  first_addr != NULL
+     || continue_more
+     || (  (  tls_out.active < 0
+           || verify_check_given_host(&ob->hosts_nopass_tls, host) != OK
+          )
         &&
            transport_check_waiting(tblock->name, host->name,
         &&
            transport_check_waiting(tblock->name, host->name,
-             tblock->connection_max_messages, new_message_id, &more)
-        ))
+             tblock->connection_max_messages, new_message_id, &more,
+            (oicf)smtp_are_same_identities, (void*)&t_compare)
+     )  )
     {
     uschar *msg;
     BOOL pass_message;
     {
     uschar *msg;
     BOOL pass_message;
@@ -2613,6 +2882,9 @@ if (completed_address && ok && send_quit)
       /* If the socket is successfully passed, we musn't send QUIT (or
       indeed anything!) from here. */
 
       /* 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))
         {
       if (ok && transport_pass_socket(tblock->name, host->name, host->address,
             new_message_id, inblock.sock))
         {
@@ -2622,7 +2894,11 @@ if (completed_address && ok && send_quit)
 
     /* If RSET failed and there are addresses left, they get deferred. */
 
 
     /* If RSET failed and there are addresses left, they get deferred. */
 
-    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
+                 );
     }
   }
 
     }
   }
 
@@ -2663,9 +2939,10 @@ writing RSET might have failed, or there may be other addresses whose hosts are
 specified in the transports, and therefore not visible at top level, in which
 case continue_more won't get set. */
 
 specified in the transports, and therefore not visible at top level, in which
 case continue_more won't get set. */
 
+HDEBUG(D_transport|D_acl|D_v) debug_printf("  SMTP(close)>>\n");
 (void)close(inblock.sock);
 
 (void)close(inblock.sock);
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
 (void) event_raise(tblock->event_action, US"tcp:close", NULL);
 #endif
 
 (void) event_raise(tblock->event_action, US"tcp:close", NULL);
 #endif
 
@@ -2750,21 +3027,25 @@ prepare_addresses(address_item *addrlist, host_item *host)
 address_item *first_addr = NULL;
 address_item *addr;
 for (addr = addrlist; addr != NULL; addr = addr->next)
 address_item *first_addr = NULL;
 address_item *addr;
 for (addr = addrlist; addr != NULL; addr = addr->next)
-  {
-  if (addr->transport_return != DEFER) continue;
-  if (first_addr == NULL) first_addr = addr;
-  addr->transport_return = PENDING_DEFER;
-  addr->basic_errno = 0;
-  addr->more_errno = (host->mx >= 0)? 'M' : 'A';
-  addr->message = NULL;
+  if (addr->transport_return == DEFER)
+    {
+    if (first_addr == NULL) first_addr = addr;
+    addr->transport_return = PENDING_DEFER;
+    addr->basic_errno = 0;
+    addr->more_errno = (host->mx >= 0)? 'M' : 'A';
+    addr->message = NULL;
 #ifdef SUPPORT_TLS
 #ifdef SUPPORT_TLS
-  addr->cipher = NULL;
-  addr->ourcert = NULL;
-  addr->peercert = NULL;
-  addr->peerdn = NULL;
-  addr->ocsp = OCSP_NOT_REQ;
+    addr->cipher = NULL;
+    addr->ourcert = NULL;
+    addr->peercert = NULL;
+    addr->peerdn = NULL;
+    addr->ocsp = OCSP_NOT_REQ;
 #endif
 #endif
-  }
+#ifdef EXPERIMENTAL_DSN_INFO
+    addr->smtp_greeting = NULL;
+    addr->helo_response = NULL;
+#endif
+    }
 return first_addr;
 }
 
 return first_addr;
 }
 
@@ -2853,8 +3134,7 @@ if (hostlist == NULL || (ob->hosts_override && ob->hosts != NULL))
 
     if (Ustrchr(s, '$') != NULL)
       {
 
     if (Ustrchr(s, '$') != NULL)
       {
-      expanded_hosts = expand_string(s);
-      if (expanded_hosts == NULL)
+      if (!(expanded_hosts = expand_string(s)))
         {
         addrlist->message = string_sprintf("failed to expand list of hosts "
           "\"%s\" in %s transport: %s", s, tblock->name, expand_string_message);
         {
         addrlist->message = string_sprintf("failed to expand list of hosts "
           "\"%s\" in %s transport: %s", s, tblock->name, expand_string_message);
@@ -2882,13 +3162,14 @@ if (hostlist == NULL || (ob->hosts_override && ob->hosts != NULL))
     /* If there was no expansion of hosts, save the host list for
     next time. */
 
     /* If there was no expansion of hosts, save the host list for
     next time. */
 
-    if (expanded_hosts == NULL) ob->hostlist = hostlist;
+    if (!expanded_hosts) ob->hostlist = hostlist;
     }
 
   /* This is not the first time this transport has been run in this delivery;
   the host list was built previously. */
 
     }
 
   /* This is not the first time this transport has been run in this delivery;
   the host list was built previously. */
 
-  else hostlist = ob->hostlist;
+  else
+    hostlist = ob->hostlist;
   }
 
 /* The host list was supplied with the address. If hosts_randomize is set, we
   }
 
 /* The host list was supplied with the address. If hosts_randomize is set, we
@@ -2932,12 +3213,10 @@ else if (ob->hosts_randomize && hostlist->mx == MX_NONE && !continuing)
   hostlist = addrlist->host_list = newlist;
   }
 
   hostlist = addrlist->host_list = newlist;
   }
 
-
 /* Sort out the default port.  */
 
 if (!smtp_get_port(ob->port, addrlist, &port, tid)) return FALSE;
 
 /* Sort out the default port.  */
 
 if (!smtp_get_port(ob->port, addrlist, &port, tid)) return FALSE;
 
-
 /* For each host-plus-IP-address on the list:
 
 .  If this is a continued delivery and the host isn't the one with the
 /* For each host-plus-IP-address on the list:
 
 .  If this is a continued delivery and the host isn't the one with the
@@ -3002,7 +3281,6 @@ for (cutoff_retry = 0; expired &&
     BOOL serialized = FALSE;
     BOOL host_is_expired = FALSE;
     BOOL message_defer = FALSE;
     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;
     BOOL some_deferred = FALSE;
     address_item *first_addr = NULL;
     uschar *interface = NULL;
@@ -3034,7 +3312,6 @@ for (cutoff_retry = 0; expired &&
       {
       int new_port, flags;
       host_item *hh;
       {
       int new_port, flags;
       host_item *hh;
-      uschar *canonical_name;
 
       if (host->status >= hstatus_unusable)
         {
 
       if (host->status >= hstatus_unusable)
         {
@@ -3062,11 +3339,11 @@ for (cutoff_retry = 0; expired &&
       if (ob->dns_search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
 
       if (ob->gethostbyname || string_is_ip_address(host->name, NULL) != 0)
       if (ob->dns_search_parents) flags |= HOST_FIND_SEARCH_PARENTS;
 
       if (ob->gethostbyname || string_is_ip_address(host->name, NULL) != 0)
-        rc = host_find_byname(host, NULL, flags, &canonical_name, TRUE);
+        rc = host_find_byname(host, NULL, flags, NULL, TRUE);
       else
         rc = host_find_bydns(host, NULL, flags, NULL, NULL, NULL,
       else
         rc = host_find_bydns(host, NULL, flags, NULL, NULL, NULL,
-         ob->dnssec_request_domains, ob->dnssec_require_domains,
-          &canonical_name, NULL);
+         &ob->dnssec,          /* domains for request/require */
+          NULL, NULL);
 
       /* Update the host (and any additional blocks, resulting from
       multihoming) with a host-specific port, if any. */
 
       /* Update the host (and any additional blocks, resulting from
       multihoming) with a host-specific port, if any. */
@@ -3142,7 +3419,8 @@ for (cutoff_retry = 0; expired &&
     doing a two-stage queue run, don't do this if forcing. */
 
     if ((!deliver_force || queue_2stage) && (queue_smtp ||
     doing a two-stage queue run, don't do this if forcing. */
 
     if ((!deliver_force || queue_2stage) && (queue_smtp ||
-        match_isinlist(addrlist->domain, &queue_smtp_domains, 0,
+        match_isinlist(addrlist->domain,
+         (const uschar **)&queue_smtp_domains, 0,
           &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK))
       {
       expired = FALSE;
           &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK))
       {
       expired = FALSE;
@@ -3178,15 +3456,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
     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;
 
     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
 
     /* 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
@@ -3194,14 +3475,20 @@ for (cutoff_retry = 0; expired &&
 
     if (cutoff_retry == 0)
       {
 
     if (cutoff_retry == 0)
       {
+      BOOL incl_ip;
       /* Ensure the status of the address is set by checking retry data if
       /* Ensure the status of the address is set by checking retry data if
-      necessary. There maybe host-specific retry data (applicable to all
+      necessary. There may be host-specific retry data (applicable to all
       messages) and also data for retries of a specific message at this host.
       If either of these retry records are actually read, the keys used are
       returned to save recomputing them later. */
 
       messages) and also data for retries of a specific message at this host.
       If either of these retry records are actually read, the keys used are
       returned to save recomputing them later. */
 
+      if (exp_bool(addrlist, US"transport", tblock->name, D_transport,
+               US"retry_include_ip_address", ob->retry_include_ip_address,
+               ob->expand_retry_include_ip_address, &incl_ip) != OK)
+       continue;       /* with next host */
+
       host_is_expired = retry_check_address(addrlist->domain, host, pistring,
       host_is_expired = retry_check_address(addrlist->domain, host, pistring,
-        ob->retry_include_ip_address, &retry_host_key, &retry_message_key);
+        incl_ip, &retry_host_key, &retry_message_key);
 
       DEBUG(D_transport) debug_printf("%s [%s]%s status = %s\n", host->name,
         (host->address == NULL)? US"" : host->address, pistring,
 
       DEBUG(D_transport) debug_printf("%s [%s]%s status = %s\n", host->name,
         (host->address == NULL)? US"" : host->address, pistring,
@@ -3264,11 +3551,10 @@ for (cutoff_retry = 0; expired &&
     sending the message down a pre-existing connection. */
 
     if (!continuing &&
     sending the message down a pre-existing connection. */
 
     if (!continuing &&
-        verify_check_this_host(&(ob->serialize_hosts), NULL, host->name,
-          host->address, NULL) == OK)
+        verify_check_given_host(&ob->serialize_hosts, host) == OK)
       {
       serialize_key = string_sprintf("host-serialize-%s", host->name);
       {
       serialize_key = string_sprintf("host-serialize-%s", host->name);
-      if (!enq_start(serialize_key))
+      if (!enq_start(serialize_key, 1))
         {
         DEBUG(D_transport)
           debug_printf("skipping host %s because another Exim process "
         {
         DEBUG(D_transport)
           debug_printf("skipping host %s because another Exim process "
@@ -3302,7 +3588,7 @@ for (cutoff_retry = 0; expired &&
     if (dont_deliver)
       {
       host_item *host2;
     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;
       for (addr = addrlist; addr != NULL; addr = addr->next)
         {
         addr->host_used = host;
@@ -3335,27 +3621,40 @@ for (cutoff_retry = 0; expired &&
 
     else
       {
 
     else
       {
+      host_item * thost;
+      /* Make a copy of the host if it is local to this invocation
+       of the transport. */
+
+      if (expanded_hosts)
+       {
+       thost = store_get(sizeof(host_item));
+       *thost = *host;
+       thost->name = string_copy(host->name);
+       thost->address = string_copy(host->address);
+       }
+      else
+        thost = host;
+
       if (!host_is_expired && ++unexpired_hosts_tried >= ob->hosts_max_try)
         {
         host_item *h;
         DEBUG(D_transport)
           debug_printf("hosts_max_try limit reached with this host\n");
       if (!host_is_expired && ++unexpired_hosts_tried >= ob->hosts_max_try)
         {
         host_item *h;
         DEBUG(D_transport)
           debug_printf("hosts_max_try limit reached with this host\n");
-        for (h = host; h != NULL; h = h->next)
-          if (h->mx != host->mx) break;
-        if (h != NULL)
-          {
-          nexthost = h;
-          unexpired_hosts_tried--;
-          DEBUG(D_transport) debug_printf("however, a higher MX host exists "
-            "and will be tried\n");
-          }
+        for (h = host; h; h = h->next) if (h->mx != host->mx)
+         {
+         nexthost = h;
+         unexpired_hosts_tried--;
+         DEBUG(D_transport) debug_printf("however, a higher MX host exists "
+           "and will be tried\n");
+         break;
+         }
         }
 
       /* Attempt the delivery. */
 
       total_hosts_tried++;
         }
 
       /* Attempt the delivery. */
 
       total_hosts_tried++;
-      rc = smtp_deliver(addrlist, host, host_af, port, interface, tblock,
-        expanded_hosts != NULL, &message_defer, FALSE);
+      rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock,
+        &message_defer, FALSE);
 
       /* Yield is one of:
          OK     => connection made, each address contains its result;
 
       /* Yield is one of:
          OK     => connection made, each address contains its result;
@@ -3376,7 +3675,7 @@ for (cutoff_retry = 0; expired &&
                          first_addr->basic_errno != ERRNO_TLSFAILURE)
         write_logs(first_addr, host);
 
                          first_addr->basic_errno != ERRNO_TLSFAILURE)
         write_logs(first_addr, host);
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
       if (rc == DEFER)
         deferred_event_raise(first_addr, host);
 #endif
       if (rc == DEFER)
         deferred_event_raise(first_addr, host);
 #endif
@@ -3394,18 +3693,17 @@ for (cutoff_retry = 0; expired &&
       if (  rc == DEFER
         && first_addr->basic_errno == ERRNO_TLSFAILURE
         && ob->tls_tempfail_tryclear
       if (  rc == DEFER
         && first_addr->basic_errno == ERRNO_TLSFAILURE
         && ob->tls_tempfail_tryclear
-        && verify_check_this_host(&(ob->hosts_require_tls), NULL, host->name,
-             host->address, NULL) != OK
+        && verify_check_given_host(&ob->hosts_require_tls, host) != OK
         )
         {
         log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
           "to %s [%s] (not in hosts_require_tls)", host->name, host->address);
         first_addr = prepare_addresses(addrlist, host);
         )
         {
         log_write(0, LOG_MAIN, "TLS session failure: delivering unencrypted "
           "to %s [%s] (not in hosts_require_tls)", host->name, host->address);
         first_addr = prepare_addresses(addrlist, host);
-        rc = smtp_deliver(addrlist, host, host_af, port, interface, tblock,
-          expanded_hosts != NULL, &message_defer, TRUE);
+        rc = smtp_deliver(addrlist, thost, host_af, port, interface, tblock,
+          &message_defer, TRUE);
         if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
           write_logs(first_addr, host);
         if (rc == DEFER && first_addr->basic_errno != ERRNO_AUTHFAIL)
           write_logs(first_addr, host);
-# ifdef EXPERIMENTAL_EVENT
+# ifndef DISABLE_EVENT
         if (rc == DEFER)
           deferred_event_raise(first_addr, host);
 # endif
         if (rc == DEFER)
           deferred_event_raise(first_addr, host);
 # endif
@@ -3439,7 +3737,13 @@ for (cutoff_retry = 0; expired &&
       int delete_flag = (rc != DEFER)? rf_delete : 0;
       if (retry_host_key == NULL)
         {
       int delete_flag = (rc != DEFER)? rf_delete : 0;
       if (retry_host_key == NULL)
         {
-        retry_host_key = ob->retry_include_ip_address?
+       BOOL incl_ip;
+       if (exp_bool(addrlist, US"transport", tblock->name, D_transport,
+                 US"retry_include_ip_address", ob->retry_include_ip_address,
+                 ob->expand_retry_include_ip_address, &incl_ip) != OK)
+         incl_ip = TRUE;       /* error; use most-specific retry record */
+
+        retry_host_key = incl_ip ?
           string_sprintf("T:%S:%s%s", host->name, host->address, pistring) :
           string_sprintf("T:%S%s", host->name, pistring);
         }
           string_sprintf("T:%S:%s%s", host->name, host->address, pistring) :
           string_sprintf("T:%S%s", host->name, pistring);
         }
@@ -3481,7 +3785,13 @@ for (cutoff_retry = 0; expired &&
       int delete_flag = message_defer? 0 : rf_delete;
       if (retry_message_key == NULL)
         {
       int delete_flag = message_defer? 0 : rf_delete;
       if (retry_message_key == NULL)
         {
-        retry_message_key = ob->retry_include_ip_address?
+       BOOL incl_ip;
+       if (exp_bool(addrlist, US"transport", tblock->name, D_transport,
+                 US"retry_include_ip_address", ob->retry_include_ip_address,
+                 ob->expand_retry_include_ip_address, &incl_ip) != OK)
+         incl_ip = TRUE;       /* error; use most-specific retry record */
+
+        retry_message_key = incl_ip ?
           string_sprintf("T:%S:%s%s:%s", host->name, host->address, pistring,
             message_id) :
           string_sprintf("T:%S%s:%s", host->name, pistring, message_id);
           string_sprintf("T:%S:%s%s:%s", host->name, host->address, pistring,
             message_id) :
           string_sprintf("T:%S%s:%s", host->name, pistring, message_id);
@@ -3496,16 +3806,12 @@ for (cutoff_retry = 0; expired &&
     case, see if any of them are deferred. */
 
     if (rc == OK)
     case, see if any of them are deferred. */
 
     if (rc == OK)
-      {
-      for (addr = addrlist; addr != NULL; addr = addr->next)
-        {
+      for (addr = addrlist; addr; addr = addr->next)
         if (addr->transport_return == DEFER)
           {
           some_deferred = TRUE;
           break;
           }
         if (addr->transport_return == DEFER)
           {
           some_deferred = TRUE;
           break;
           }
-        }
-      }
 
     /* If no addresses deferred or the result was ERROR, return. We do this for
     ERROR because a failing filter set-up or add_headers expansion is likely to
 
     /* If no addresses deferred or the result was ERROR, return. We do this for
     ERROR because a failing filter set-up or add_headers expansion is likely to
@@ -3592,7 +3898,7 @@ If queue_smtp is set, or this transport was called to send a subsequent message
 down an existing TCP/IP connection, and something caused the host not to be
 found, we end up here, but can detect these cases and handle them specially. */
 
 down an existing TCP/IP connection, and something caused the host not to be
 found, we end up here, but can detect these cases and handle them specially. */
 
-for (addr = addrlist; addr != NULL; addr = addr->next)
+for (addr = addrlist; addr; addr = addr->next)
   {
   /* If host is not NULL, it means that we stopped processing the host list
   because of hosts_max_try or hosts_max_try_hardlimit. In the former case, this
   {
   /* If host is not NULL, it means that we stopped processing the host list
   because of hosts_max_try or hosts_max_try_hardlimit. In the former case, this
@@ -3601,8 +3907,7 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
   However, if we have hit hosts_max_try_hardlimit, we want to behave as if all
   hosts were tried. */
 
   However, if we have hit hosts_max_try_hardlimit, we want to behave as if all
   hosts were tried. */
 
-  if (host != NULL)
-    {
+  if (host)
     if (total_hosts_tried >= ob->hosts_max_try_hardlimit)
       {
       DEBUG(D_transport)
     if (total_hosts_tried >= ob->hosts_max_try_hardlimit)
       {
       DEBUG(D_transport)
@@ -3615,7 +3920,6 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
         debug_printf("hosts_max_try limit caused some hosts to be skipped\n");
       setflag(addr, af_retry_skipped);
       }
         debug_printf("hosts_max_try limit caused some hosts to be skipped\n");
       setflag(addr, af_retry_skipped);
       }
-    }
 
   if (queue_smtp)    /* no deliveries attempted */
     {
 
   if (queue_smtp)    /* no deliveries attempted */
     {
@@ -3624,44 +3928,49 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
     addr->message = US"SMTP delivery explicitly queued";
     }
 
     addr->message = US"SMTP delivery explicitly queued";
     }
 
-  else if (addr->transport_return == DEFER &&
-       (addr->basic_errno == ERRNO_UNKNOWNERROR || addr->basic_errno == 0) &&
-       addr->message == NULL)
+  else if (  addr->transport_return == DEFER
+         && (addr->basic_errno == ERRNO_UNKNOWNERROR || addr->basic_errno == 0)
+         && !addr->message
+         )
     {
     addr->basic_errno = ERRNO_HRETRY;
     {
     addr->basic_errno = ERRNO_HRETRY;
-    if (continue_hostname != NULL)
-      {
+    if (continue_hostname)
       addr->message = US"no host found for existing SMTP connection";
       addr->message = US"no host found for existing SMTP connection";
-      }
     else if (expired)
       {
       setflag(addr, af_pass_message);   /* This is not a security risk */
     else if (expired)
       {
       setflag(addr, af_pass_message);   /* This is not a security risk */
-      addr->message = (ob->delay_after_cutoff)?
-        US"retry time not reached for any host after a long failure period" :
-        US"all hosts have been failing for a long time and were last tried "
-          "after this message arrived";
+      addr->message = string_sprintf(
+       "all hosts%s have been failing for a long time %s",
+       addr->domain ? string_sprintf(" for '%s'", addr->domain) : US"",
+        ob->delay_after_cutoff
+       ? US"(and retry time not reached)"
+       : US"and were last tried after this message arrived");
 
       /* If we are already using fallback hosts, or there are no fallback hosts
       defined, convert the result to FAIL to cause a bounce. */
 
 
       /* If we are already using fallback hosts, or there are no fallback hosts
       defined, convert the result to FAIL to cause a bounce. */
 
-      if (addr->host_list == addr->fallback_hosts ||
-          addr->fallback_hosts == NULL)
+      if (addr->host_list == addr->fallback_hosts || !addr->fallback_hosts)
         addr->transport_return = FAIL;
       }
     else
       {
         addr->transport_return = FAIL;
       }
     else
       {
+      const char * s;
       if (hosts_retry == hosts_total)
       if (hosts_retry == hosts_total)
-        addr->message = US"retry time not reached for any host";
+        s = "retry time not reached for any host%s";
       else if (hosts_fail == hosts_total)
       else if (hosts_fail == hosts_total)
-        addr->message = US"all host address lookups failed permanently";
+        s = "all host address lookups%s failed permanently";
       else if (hosts_defer == hosts_total)
       else if (hosts_defer == hosts_total)
-        addr->message = US"all host address lookups failed temporarily";
+        s = "all host address lookups%s failed temporarily";
       else if (hosts_serial == hosts_total)
       else if (hosts_serial == hosts_total)
-        addr->message = US"connection limit reached for all hosts";
+        s = "connection limit reached for all hosts%s";
       else if (hosts_fail+hosts_defer == hosts_total)
       else if (hosts_fail+hosts_defer == hosts_total)
-        addr->message = US"all host address lookups failed";
-      else addr->message = US"some host address lookups failed and retry time "
-        "not reached for other hosts or connection limit reached";
+        s = "all host address lookups%s failed";
+      else
+        s = "some host address lookups failed and retry time "
+        "not reached for other hosts or connection limit reached%s";
+
+      addr->message = string_sprintf(s,
+       addr->domain ? string_sprintf(" for '%s'", addr->domain) : US"");
       }
     }
   }
       }
     }
   }