TLS: retire obsolete options gnutls_require_{kx,mac,proto)
[exim.git] / src / src / transports / smtp.c
index 30facda00e04b5bb8b48f86c874e9803d7dc8f06..3a887c1519927d6b64318712b17b09175bed6366 100644 (file)
@@ -2,7 +2,7 @@
 *     Exim - an Internet mail transport agent    *
 *************************************************/
 
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
 /* See the file NOTICE for conditions of use and distribution. */
 
 #include "../exim.h"
@@ -72,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) },
-#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,
@@ -257,9 +246,6 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   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 */
   US"system",          /* tls_verify_certificates */
   EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
@@ -411,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);
-
-#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
 }
 
 
@@ -596,7 +573,7 @@ if (*errno_value == ERRNO_WRITEINCOMPLETE)
   return FALSE;
   }
 
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
 /* Handle lack of advertised SMTPUTF8, for international message */
 if (*errno_value == ERRNO_UTF8_FWD)
   {
@@ -668,22 +645,22 @@ if (addr->message)
   }
 else
   {
-  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)
 {
-  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                            *
 *************************************************/
@@ -919,7 +896,7 @@ while (count-- > 0)
       addr->basic_errno = ERRNO_RCPT4XX;
       addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
       event_defer_errno = addr->more_errno;
       msg_event_raise(US"msg:rcpt:host:defer", addr);
 #endif
@@ -930,7 +907,7 @@ while (count-- > 0)
       if (host->next)
        log_write(0, LOG_MAIN, "H=%s [%s]: %s", host->name, host->address, addr->message);
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
       else
        msg_event_raise(US"msg:rcpt:defer", addr);
 #endif
@@ -1215,9 +1192,15 @@ return FALSE;
 
 
 #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
-tlsa_lookup(const 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];
@@ -1228,25 +1211,24 @@ const uschar * fullname = buffer;
 
 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)
-      return FAIL;
-    break;
-
   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
 
@@ -1311,7 +1293,6 @@ 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;
@@ -1330,6 +1311,49 @@ 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       *
 *************************************************/
@@ -1399,13 +1423,12 @@ BOOL completed_address = 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
-BOOL prdr_offered = FALSE;
 BOOL prdr_active;
 #endif
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
 BOOL utf8_needed = FALSE;
-BOOL utf8_offered = FALSE;
 #endif
 BOOL dsn_all_lasthop = TRUE;
 #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
@@ -1503,26 +1526,26 @@ if (continue_hostname == NULL)
 
     if (host->dnssec == DS_YES)
       {
-      if(  (  dane_required
-          || verify_check_given_host(&ob->hosts_try_dane, host) == OK
-          )
-       && (rc = tlsa_lookup(host, &tlsa_dnsa, dane_required, &dane)) != OK
-       && dane_required        /* do not error on only dane-requested */
+      if(  dane_required
+       || verify_check_given_host(&ob->hosts_try_dane, host) == OK
        )
-       {
-       set_errno_nohost(addrlist, ERRNO_DNSDEFER,
-         string_sprintf("DANE error: tlsa lookup %s",
-           rc == DEFER ? "DEFER" : "FAIL"),
-         rc, FALSE);
-       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)
       {
       set_errno_nohost(addrlist, ERRNO_DNSDEFER,
        string_sprintf("DANE error: %s lookup not DNSSEC", host->name),
        FAIL, FALSE);
-      return  FAIL;
+      return FAIL;
       }
 
     if (dane)
@@ -1535,7 +1558,7 @@ if (continue_hostname == NULL)
   delayed till here so that $sending_interface and $sending_port are set. */
 
   helo_data = expand_string(ob->helo_data);
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
   if (helo_data)
     {
     uschar * errstr = NULL;
@@ -1562,7 +1585,7 @@ if (continue_hostname == NULL)
 #endif
     if (!good_response) goto RESPONSE_FAILED;
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
       {
       uschar * s;
       lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
@@ -1684,43 +1707,21 @@ goto SEND_QUIT;
     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
-  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_given_host(&ob->hosts_try_prdr, host) == OK;
-
-  if (prdr_offered)
-    {DEBUG(D_transport) debug_printf("PRDR usable\n");}
-#endif
-
-#ifdef EXPERIMENTAL_INTERNATIONAL
-  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");
-
-    utf8_offered = esmtp
-      && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0,
-                   PCRE_EOPT, NULL, 0) >= 0;
-    }
+  tls_offered = !!(peer_offered & PEER_OFFERED_TLS);
 #endif
   }
 
@@ -1770,8 +1771,10 @@ if (  tls_offered
   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;
@@ -1796,13 +1799,11 @@ if (  tls_offered
     if (rc != OK)
       {
 # ifdef EXPERIMENTAL_DANE
-      if (rc == DEFER && dane && !dane_required)
+      if (rc == DEFER && dane)
        {
-       log_write(0, LOG_MAIN, "DANE attempt failed;"
-         " trying CA-root TLS to %s [%s] (not in hosts_require_dane)",
+       log_write(0, LOG_MAIN,
+         "DANE attempt failed; no TLS connection to %s [%s]",
          host->name, host->address);
-       dane = FALSE;
-       goto TLS_NEGOTIATE;
        }
 # endif
 
@@ -1914,54 +1915,53 @@ if (continue_hostname == NULL
 #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. */
 
-  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. */
 
-  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. */
 
-  smtp_use_pipelining = esmtp
-    && verify_check_given_host(&ob->hosts_avoid_pipelining, host) != 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",
-    smtp_use_pipelining? "" : "not ");
+    smtp_use_pipelining ? "" : "not ");
 
 #ifndef DISABLE_PRDR
-  prdr_offered = esmtp
-    && pcre_exec(regex_PRDR, NULL, CS buffer, Ustrlen(CS buffer), 0,
-                 PCRE_EOPT, NULL, 0) >= 0
-    && verify_check_given_host(&ob->hosts_try_prdr, host) == 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
 
-#ifdef EXPERIMENTAL_INTERNATIONAL
-  if (addrlist->prop.utf8_msg)
-    utf8_offered = esmtp
-      && pcre_exec(regex_UTF8, NULL, CS buffer, Ustrlen(buffer), 0,
-                   PCRE_EOPT, NULL, 0) >= 0;
-#endif
-
   /* Note if the server supports DSN */
-  smtp_use_dsn = esmtp
-    && pcre_exec(regex_DSN, NULL, CS buffer, Ustrlen(CS buffer), 0,
-                 PCRE_EOPT, NULL, 0) >= 0;
-  DEBUG(D_transport) debug_printf("use_dsn=%d\n", smtp_use_dsn);
+  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
@@ -1983,9 +1983,17 @@ message-specific. */
 
 setting_up = FALSE;
 
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#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 && !utf8_offered)
+if (utf8_needed && !(peer_offered & PEER_OFFERED_UTF8))
   {
   errno = ERRNO_UTF8_FWD;
   goto RESPONSE_FAILED;
@@ -2051,8 +2059,7 @@ if (smtp_use_size)
 
 #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)
       {
@@ -2065,11 +2072,13 @@ if (prdr_offered)
          }
       break;
       }
-  }
 #endif
 
-#ifdef EXPERIMENTAL_INTERNATIONAL
-if (addrlist->prop.utf8_msg && !addrlist->prop.utf8_downcvt && utf8_offered)
+#ifdef SUPPORT_I18N
+if (  addrlist->prop.utf8_msg
+   && !addrlist->prop.utf8_downcvt
+   && peer_offered & PEER_OFFERED_UTF8
+   )
   sprintf(CS p, " SMTPUTF8"), p += 9;
 #endif
 
@@ -2128,14 +2137,16 @@ pending_MAIL = TRUE;     /* The block starts with MAIL */
 
   {
   uschar * s = return_path;
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#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 || !utf8_offered))
+  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)
       {
@@ -2239,7 +2250,7 @@ for (addr = first_addr;
 
   rcpt_addr = transport_rcpt_address(addr, tblock->rcpt_include_affixes);
 
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
   {
   uschar * dummy_errstr;
   if (  testflag(addrlist, af_utf8_downcvt)
@@ -2458,7 +2469,7 @@ if (!ok) ok = TRUE; else
     /* Set up confirmation if needed - applies only to SMTP */
 
     if (
-#ifndef EXPERIMENTAL_EVENT
+#ifdef DISABLE_EVENT
           LOGGING(smtp_confirmation) &&
 #endif
           !lmtp
@@ -2679,7 +2690,7 @@ if (!ok)
 
     switch(save_errno)
       {
-#ifdef EXPERIMENTAL_INTERNATIONAL
+#ifdef SUPPORT_I18N
       case ERRNO_UTF8_FWD:
         code = '5';
       /*FALLTHROUGH*/
@@ -2928,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. */
 
+HDEBUG(D_transport|D_acl|D_v) debug_printf("  SMTP(close)>>\n");
 (void)close(inblock.sock);
 
-#ifdef EXPERIMENTAL_EVENT
+#ifndef DISABLE_EVENT
 (void) event_raise(tblock->event_action, US"tcp:close", NULL);
 #endif
 
@@ -3663,7 +3675,7 @@ for (cutoff_retry = 0; expired &&
                          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
@@ -3691,7 +3703,7 @@ for (cutoff_retry = 0; expired &&
           &message_defer, TRUE);
         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
@@ -3886,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. */
 
-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
@@ -3895,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. */
 
-  if (host != NULL)
-    {
+  if (host)
     if (total_hosts_tried >= ob->hosts_max_try_hardlimit)
       {
       DEBUG(D_transport)
@@ -3909,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);
       }
-    }
 
   if (queue_smtp)    /* no deliveries attempted */
     {
@@ -3918,44 +3928,49 @@ for (addr = addrlist; addr != NULL; addr = addr->next)
     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;
-    if (continue_hostname != NULL)
-      {
+    if (continue_hostname)
       addr->message = US"no host found for existing SMTP connection";
-      }
     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 (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
       {
+      const char * s;
       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)
-        addr->message = US"all host address lookups failed permanently";
+        s = "all host address lookups%s failed permanently";
       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)
-        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)
-        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"");
       }
     }
   }