Logging: avoid claiming a host was used for an addr, when conn refused under TFO
[exim.git] / src / src / transports / smtp.c
index 9ee1304bc3f9bbf862788e4c87cd386fbb354b1d..6ce4c9ded56fbda4efaa6830e36d50626a90d4a1 100644 (file)
@@ -90,7 +90,7 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, hosts_avoid_esmtp) },
   { "hosts_avoid_pipelining", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_avoid_pipelining) },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   { "hosts_avoid_tls",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_avoid_tls) },
 #endif
@@ -98,7 +98,7 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, hosts_max_try) },
   { "hosts_max_try_hardlimit", opt_int,
       (void *)offsetof(smtp_transport_options_block, hosts_max_try_hardlimit) },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   { "hosts_nopass_tls",     opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_nopass_tls) },
   { "hosts_noproxy_tls",    opt_stringptr,
@@ -112,13 +112,13 @@ optionlist smtp_transport_options[] = {
 #endif
   { "hosts_randomize",      opt_bool,
       (void *)offsetof(smtp_transport_options_block, hosts_randomize) },
-#if defined(SUPPORT_TLS) && !defined(DISABLE_OCSP)
+#if !defined(DISABLE_TLS) && !defined(DISABLE_OCSP)
   { "hosts_request_ocsp",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_request_ocsp) },
 #endif
   { "hosts_require_auth",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_require_auth) },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 # ifdef SUPPORT_DANE
   { "hosts_require_dane",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_require_dane) },
@@ -134,7 +134,7 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, hosts_try_auth) },
   { "hosts_try_chunking",   opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_try_chunking) },
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
+#ifdef SUPPORT_DANE
   { "hosts_try_dane",       opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_try_dane) },
 #endif
@@ -144,7 +144,7 @@ optionlist smtp_transport_options[] = {
   { "hosts_try_prdr",       opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_try_prdr) },
 #endif
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   { "hosts_verify_avoid_tls", opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, hosts_verify_avoid_tls) },
 #endif
@@ -172,7 +172,7 @@ optionlist smtp_transport_options[] = {
   { "socks_proxy",          opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, socks_proxy) },
 #endif
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   { "tls_certificate",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_certificate) },
   { "tls_crl",              opt_stringptr,
@@ -183,6 +183,10 @@ optionlist smtp_transport_options[] = {
       (void *)offsetof(smtp_transport_options_block, tls_privatekey) },
   { "tls_require_ciphers",  opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_require_ciphers) },
+# ifdef EXPERIMENTAL_TLS_RESUME
+  { "tls_resumption_hosts", opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, tls_resumption_hosts) },
+# endif
   { "tls_sni",              opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_sni) },
   { "tls_tempfail_tryclear", opt_bool,
@@ -236,7 +240,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .hosts_require_auth =                NULL,
   .hosts_try_chunking =                US"*",
 #ifdef SUPPORT_DANE
-  .hosts_try_dane =            NULL,
+  .hosts_try_dane =            US"*",
   .hosts_require_dane =                NULL,
   .dane_require_tls_ciphers =  NULL,
 #endif
@@ -256,9 +260,9 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .hosts_pipe_connect =                NULL,
 #endif
   .hosts_avoid_esmtp =         NULL,
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   .hosts_nopass_tls =          NULL,
-  .hosts_noproxy_tls =         US"*",
+  .hosts_noproxy_tls =         NULL,
 #endif
   .command_timeout =           5*60,
   .connect_timeout =           5*60,
@@ -284,7 +288,7 @@ smtp_transport_options_block smtp_transport_option_defaults = {
 #ifdef SUPPORT_SOCKS
   .socks_proxy =               NULL,
 #endif
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   .tls_certificate =           NULL,
   .tls_crl =                   NULL,
   .tls_privatekey =            NULL,
@@ -293,6 +297,9 @@ smtp_transport_options_block smtp_transport_option_defaults = {
   .tls_verify_certificates =   US"system",
   .tls_dh_min_bits =           EXIM_CLIENT_DH_DEFAULT_MIN_BITS,
   .tls_tempfail_tryclear =     TRUE,
+# ifdef EXPERIMENTAL_TLS_RESUME
+  .tls_resumption_hosts =      NULL,
+# endif
   .tls_verify_hosts =          NULL,
   .tls_try_verify_hosts =      US"*",
   .tls_verify_cert_hostnames = US"*",
@@ -583,6 +590,10 @@ switch(*errno_value)
        transport_count);
     return FALSE;
 
+  case ECONNREFUSED:           /* First-read error on a TFO conn */
+    if (verify_mode) *message = US strerror(*errno_value);
+    return FALSE;              /* nonverify, do not set message */
+
   case ERRNO_SMTPFORMAT:       /* Handle malformed SMTP response */
     s = string_printing(buffer);
     while (isspace(*s)) s++;
@@ -593,6 +604,11 @@ switch(*errno_value)
          pl, smtp_command, s);
     return FALSE;
 
+  case ERRNO_TLSFAILURE:       /* Handle bad first read; can happen with
+                               GnuTLS and TLS1.3 */
+    *message = US"bad first read from TLS conn";
+    return TRUE;
+
   case ERRNO_FILTER_FAIL:      /* Handle a failed filter process error;
                          can't send QUIT as we mustn't end the DATA. */
     *message = string_sprintf("transport filter process failed (%d)%s",
@@ -820,7 +836,7 @@ write_ehlo_cache_entry(const smtp_context * sx)
 {
 open_db dbblock, * dbm_file;
 
-if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
   {
   uschar * ehlo_resp_key = ehlo_cache_key(sx);
   dbdata_ehlo_resp er = { .data = sx->ehlo_resp };
@@ -840,7 +856,7 @@ invalidate_ehlo_cache_entry(smtp_context * sx)
 open_db dbblock, * dbm_file;
 
 if (  sx->early_pipe_active
-   && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+   && (dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
   {
   uschar * ehlo_resp_key = ehlo_cache_key(sx);
   dbfn_delete(dbm_file, ehlo_resp_key);
@@ -854,7 +870,7 @@ read_ehlo_cache_entry(smtp_context * sx)
 open_db dbblock;
 open_db * dbm_file;
 
-if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE)))
+if (!(dbm_file = dbfn_open(US"misc", O_RDONLY, &dbblock, FALSE, TRUE)))
   { DEBUG(D_transport) debug_printf("ehlo-cache: no misc DB\n"); }
 else
   {
@@ -867,7 +883,7 @@ else
     {
     DEBUG(D_transport) debug_printf("ehlo-resp record too old\n");
     dbfn_close(dbm_file);
-    if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE)))
+    if ((dbm_file = dbfn_open(US"misc", O_RDWR, &dbblock, TRUE, TRUE)))
       dbfn_delete(dbm_file, ehlo_resp_key);
     }
   else
@@ -941,6 +957,7 @@ Arguments:
 
 Return:
  OK    all well
+ DEFER error on first read of TLS'd conn
  FAIL  SMTP error in response
 */
 int
@@ -948,6 +965,7 @@ smtp_reap_early_pipe(smtp_context * sx, int * countp)
 {
 BOOL pending_BANNER = sx->pending_BANNER;
 BOOL pending_EHLO = sx->pending_EHLO;
+int rc = FAIL;
 
 sx->pending_BANNER = FALSE;    /* clear early to avoid recursion */
 sx->pending_EHLO = FALSE;
@@ -959,6 +977,7 @@ if (pending_BANNER)
   if (!smtp_reap_banner(sx))
     {
     DEBUG(D_transport) debug_printf("bad banner\n");
+    if (tls_out.active.sock >= 0) rc = DEFER;
     goto fail;
     }
   }
@@ -973,6 +992,7 @@ if (pending_EHLO)
   if (!smtp_reap_ehlo(sx))
     {
     DEBUG(D_transport) debug_printf("bad response for EHLO\n");
+    if (tls_out.active.sock >= 0) rc = DEFER;
     goto fail;
     }
 
@@ -983,7 +1003,7 @@ if (pending_EHLO)
       ? &sx->ehlo_resp.cleartext_auths : &sx->ehlo_resp.crypted_auths;
 
   peer_offered = ehlo_response(sx->buffer,
-         (tls_out.active.sock < 0 ?  OPTION_TLS : OPTION_REQUIRETLS)
+         (tls_out.active.sock < 0 ?  OPTION_TLS : 0)
        | OPTION_CHUNKING | OPTION_PRDR | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
        | OPTION_UTF8 | OPTION_EARLY_PIPE
        );
@@ -1010,7 +1030,7 @@ return OK;
 fail:
   invalidate_ehlo_cache_entry(sx);
   (void) smtp_discard_responses(sx, sx->conn_args.ob, *countp);
-  return FAIL;
+  return rc;
 }
 #endif
 
@@ -1055,6 +1075,7 @@ Returns:      3 if at least one address had 2xx and one had 5xx
              -2 I/O or other non-response error for RCPT
              -3 DATA or MAIL failed - errno and buffer set
             -4 banner or EHLO failed (early-pipelining)
+            -5 banner or EHLO failed (early-pipelining, TLS)
 */
 
 static int
@@ -1065,8 +1086,9 @@ smtp_transport_options_block * ob = sx->conn_args.ob;
 int yield = 0;
 
 #ifdef EXPERIMENTAL_PIPE_CONNECT
-if (smtp_reap_early_pipe(sx, &count) != OK)
-  return -4;
+int rc;
+if ((rc = smtp_reap_early_pipe(sx, &count)) != OK)
+  return rc == FAIL ? -4 : -5;
 #endif
 
 /* Handle the response for a MAIL command. On error, reinstate the original
@@ -1082,6 +1104,8 @@ if (sx->pending_MAIL)
     {
     DEBUG(D_transport) debug_printf("bad response for MAIL\n");
     Ustrcpy(big_buffer, mail_command);  /* Fits, because it came from there! */
+    if (errno == ERRNO_TLSFAILURE)
+      return -5;
     if (errno == 0 && sx->buffer[0] != 0)
       {
       int save_errno = 0;
@@ -1140,6 +1164,11 @@ while (count-- > 0)
       }
     }
 
+  /* Error on first TLS read */
+
+  else if (errno == ERRNO_TLSFAILURE)
+    return -5;
+
   /* Timeout while reading the response */
 
   else if (errno == ETIMEDOUT)
@@ -1252,6 +1281,10 @@ if (pending_DATA != 0)
     int code;
     uschar *msg;
     BOOL pass_message;
+
+    if (errno == ERRNO_TLSFAILURE)     /* Error on first TLS read */
+      return -5;
+
     if (pending_DATA > 0 || (yield & 1) != 0)
       {
       if (errno == 0 && sx->buffer[0] == '4')
@@ -1405,8 +1438,6 @@ if (  sx->esmtp
   if (  require_auth == OK
      || verify_check_given_host(CUSS &ob->hosts_try_auth, host) == OK)
     {
-    auth_instance * au;
-
     DEBUG(D_transport) debug_printf("scanning authentication mechanisms\n");
     fail_reason = US"no common mechanisms were found";
 
@@ -1419,6 +1450,7 @@ if (  sx->esmtp
       client function.  We are limited to supporting up to 16 authenticator
       public-names by the number of bits in a short. */
 
+      auth_instance * au;
       uschar bitnum;
       int rc;
 
@@ -1659,7 +1691,7 @@ smtp_local_identity(uschar * sender, struct transport_instance * tblock)
 address_item * addr1;
 uschar * if1 = US"";
 uschar * helo1 = US"";
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 uschar * tlsc1 = US"";
 #endif
 uschar * save_sender_address = sender_address;
@@ -1677,7 +1709,7 @@ if (ob->interface)
 if (ob->helo_data)
   helo1 = expand_string(ob->helo_data);
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (ob->tls_certificate)
   tlsc1 = expand_string(ob->tls_certificate);
 local_identity = string_sprintf ("%s^%s^%s", if1, helo1, tlsc1);
@@ -1726,13 +1758,7 @@ size_t bsize = Ustrlen(buf);
 
 /* debug_printf("%s: check for 0x%04x\n", __FUNCTION__, checks); */
 
-#ifdef SUPPORT_TLS
-# ifdef EXPERIMENTAL_REQUIRETLS
-if (  checks & OPTION_REQUIRETLS
-   && pcre_exec(regex_REQUIRETLS, NULL, CS buf,bsize, 0, PCRE_EOPT, NULL,0) < 0)
-# endif
-  checks &= ~OPTION_REQUIRETLS;
-
+#ifndef DISABLE_TLS
 if (  checks & OPTION_TLS
    && pcre_exec(regex_STARTTLS, NULL, CS buf, bsize, 0, PCRE_EOPT, NULL, 0) < 0)
 #endif
@@ -1800,7 +1826,9 @@ Args:
    tc_chunk_last       add LAST option to SMTP BDAT command
    tc_reap_prev                reap response to previous SMTP commands
 
-Returns:       OK or ERROR
+Returns:
+  OK or ERROR
+  DEFER                        TLS error on first read (EHLO-resp); errno set
 */
 
 static int
@@ -1857,10 +1885,12 @@ if (flags & tc_reap_prev  &&  prev_cmd_count > 0)
     case 2: sx->completed_addr = TRUE; /* 5xx (only) => progress made */
     case 0: break;                     /* No 2xx or 5xx, but no probs */
 
-    case -1:                           /* Timeout on RCPT */
+    case -5: errno = ERRNO_TLSFAILURE;
+            return DEFER;
 #ifdef EXPERIMENTAL_PIPE_CONNECT
     case -4:                           /* non-2xx for pipelined banner or EHLO */
 #endif
+    case -1:                           /* Timeout on RCPT */
     default: return ERROR;             /* I/O error, or any MAIL/DATA error */
     }
   cmd_count = 1;
@@ -1923,14 +1953,14 @@ Returns:          OK    - the connection was made and the delivery attempted;
 int
 smtp_setup_conn(smtp_context * sx, BOOL suppress_tls)
 {
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
-dns_answer tlsa_dnsa;
-#endif
 smtp_transport_options_block * ob = sx->conn_args.tblock->options_block;
 BOOL pass_message = FALSE;
 uschar * message = NULL;
 int yield = OK;
 int rc;
+#ifndef DISABLE_TLS
+uschar * tls_errstr;
+#endif
 
 sx->conn_args.ob = ob;
 
@@ -1946,8 +1976,8 @@ sx->esmtp_sent = FALSE;
 sx->utf8_needed = FALSE;
 #endif
 sx->dsn_all_lasthop = TRUE;
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
-sx->dane = FALSE;
+#ifdef SUPPORT_DANE
+sx->conn_args.dane = FALSE;
 sx->dane_required =
   verify_check_given_host(CUSS &ob->hosts_require_dane, sx->conn_args.host) == OK;
 #endif
@@ -1993,10 +2023,13 @@ tls_out.cipher = NULL;  /* the one we may use for this transport */
 tls_out.ourcert = NULL;
 tls_out.peercert = NULL;
 tls_out.peerdn = NULL;
-#if defined(SUPPORT_TLS) && !defined(USE_GNUTLS)
+#ifdef USE_OPENSSL
 tls_out.sni = NULL;
 #endif
 tls_out.ocsp = OCSP_NOT_REQ;
+#ifdef EXPERIMENTAL_TLS_RESUME
+tls_out.resumption = 0;
+#endif
 
 /* Flip the legacy TLS-related variables over to the outbound set in case
 they're used in the context of the transport.  Don't bother resetting
@@ -2005,7 +2038,7 @@ For verify, unflipped once the callout is dealt with */
 
 tls_modify_variables(&tls_out);
 
-#ifndef SUPPORT_TLS
+#ifdef DISABLE_TLS
 if (sx->smtps)
   {
   set_errno_nohost(sx->addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
@@ -2027,7 +2060,7 @@ if (!continue_hostname)
 
   smtp_port_for_connect(sx->conn_args.host, sx->port);
 
-#if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
+#ifdef SUPPORT_DANE
     /* Do TLSA lookup for DANE */
     {
     tls_out.dane_verified = FALSE;
@@ -2038,9 +2071,9 @@ if (!continue_hostname)
       if(  sx->dane_required
        || verify_check_given_host(CUSS &ob->hosts_try_dane, sx->conn_args.host) == OK
        )
-       switch (rc = tlsa_lookup(sx->conn_args.host, &tlsa_dnsa, sx->dane_required))
+       switch (rc = tlsa_lookup(sx->conn_args.host, &sx->conn_args.tlsa_dnsa, sx->dane_required))
          {
-         case OK:              sx->dane = TRUE;
+         case OK:              sx->conn_args.dane = TRUE;
                                ob->tls_tempfail_tryclear = FALSE;
                                break;
          case FAIL_FORCED:     break;
@@ -2096,16 +2129,9 @@ if (!continue_hostname)
     {
     if ((sx->cctx.sock = smtp_connect(&sx->conn_args, NULL)) < 0)
       {
-      uschar * msg = NULL;
-      if (sx->verify)
-       {
-       msg = US strerror(errno);
-       HDEBUG(D_verify) debug_printf("connect: %s\n", msg);
-       }
       set_errno_nohost(sx->addrlist,
        errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
-       sx->verify ? string_sprintf("could not connect: %s", msg)
-              : NULL,
+       sx->verify ? US strerror(errno) : NULL,
        DEFER, FALSE);
       sx->send_quit = FALSE;
       return DEFER;
@@ -2233,7 +2259,7 @@ goto SEND_QUIT;
   /* 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
   and be usable.  I can see this coming back to bite us. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   if (sx->smtps)
     {
     smtp_peer_options |= OPTION_TLS;
@@ -2359,7 +2385,7 @@ goto SEND_QUIT;
 
   /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
     smtp_peer_options |= sx->peer_offered & OPTION_TLS;
 #endif
     }
@@ -2421,7 +2447,7 @@ negative, the original EHLO data is available for subsequent analysis, should
 the client not be required to use TLS. If the response is bad, copy the buffer
 for error analysis. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (  smtp_peer_options & OPTION_TLS
    && !suppress_tls
    && verify_check_given_host(CUSS &ob->hosts_avoid_tls, sx->conn_args.host) != OK
@@ -2471,27 +2497,22 @@ if (  smtp_peer_options & OPTION_TLS
   else
   TLS_NEGOTIATE:
     {
-    uschar * errstr;
-    sx->cctx.tls_ctx = tls_client_start(sx->cctx.sock, sx->conn_args.host,
-                           sx->addrlist, sx->conn_args.tblock,
-# ifdef SUPPORT_DANE
-                            sx->dane ? &tlsa_dnsa : NULL,
-# endif
-                            &tls_out, &errstr);
-
-    if (!sx->cctx.tls_ctx)
+    if (!tls_client_start(&sx->cctx, &sx->conn_args, sx->addrlist, &tls_out, &tls_errstr))
       {
       /* TLS negotiation failed; give an error. From outside, this function may
       be called again to try in clear on a new connection, if the options permit
       it for this host. */
-      DEBUG(D_tls) debug_printf("TLS session fail: %s\n", errstr);
+#ifdef USE_GNUTLS
+  GNUTLS_CONN_FAILED:
+#endif
+      DEBUG(D_tls) debug_printf("TLS session fail: %s\n", tls_errstr);
 
 # ifdef SUPPORT_DANE
-      if (sx->dane)
+      if (sx->conn_args.dane)
         {
        log_write(0, LOG_MAIN,
          "DANE attempt failed; TLS connection to %s [%s]: %s",
-         sx->conn_args.host->name, sx->conn_args.host->address, errstr);
+         sx->conn_args.host->name, sx->conn_args.host->address, tls_errstr);
 #  ifndef DISABLE_EVENT
        (void) event_raise(sx->conn_args.tblock->event_action,
          US"dane:fail", US"validation-failure");       /* could do with better detail */
@@ -2500,7 +2521,7 @@ if (  smtp_peer_options & OPTION_TLS
 # endif
 
       errno = ERRNO_TLSFAILURE;
-      message = string_sprintf("TLS session: %s", errstr);
+      message = string_sprintf("TLS session: %s", tls_errstr);
       sx->send_quit = FALSE;
       goto TLS_FAILED;
       }
@@ -2598,7 +2619,22 @@ if (tls_out.active.sock >= 0)
 #endif
     {
     if (!smtp_reap_ehlo(sx))
+#ifdef USE_GNUTLS
+      {
+      /* The GnuTLS layer in Exim only spots a server-rejection of a client
+      cert late, under TLS1.3 - which means here; the first time we try to
+      receive crypted data.  Treat it as if it was a connect-time failure.
+      See also the early-pipe equivalent... which will be hard; every call
+      to sync_responses will need to check the result.
+      It would be nicer to have GnuTLS check the cert during the handshake.
+      Can it do that, with all the flexibility we need? */
+
+      tls_errstr = US"error on first read";
+      goto GNUTLS_CONN_FAILED;
+      }
+#else
       goto RESPONSE_FAILED;
+#endif
     smtp_peer_options = 0;
     }
   }
@@ -2608,24 +2644,17 @@ have one. */
 
 else if (  sx->smtps
 # ifdef SUPPORT_DANE
-       || sx->dane
-# endif
-# ifdef EXPERIMENTAL_REQUIRETLS
-       || tls_requiretls & REQUIRETLS_MSG
+       || sx->conn_args.dane
 # endif
        || verify_check_given_host(CUSS &ob->hosts_require_tls, sx->conn_args.host) == OK
        )
   {
-  errno =
-# ifdef EXPERIMENTAL_REQUIRETLS
-      tls_requiretls & REQUIRETLS_MSG ? ERRNO_REQUIRETLS :
-# endif
-      ERRNO_TLSREQUIRED;
+  errno = ERRNO_TLSREQUIRED;
   message = string_sprintf("a TLS session is required, but %s",
     smtp_peer_options & OPTION_TLS
     ? "an attempt to start TLS failed" : "the server did not offer TLS support");
 # if defined(SUPPORT_DANE) && !defined(DISABLE_EVENT)
-  if (sx->dane)
+  if (sx->conn_args.dane)
     (void) event_raise(sx->conn_args.tblock->event_action, US"dane:fail",
       smtp_peer_options & OPTION_TLS
       ? US"validation-failure"         /* could do with better detail */
@@ -2633,7 +2662,7 @@ else if (  sx->smtps
 # endif
   goto TLS_FAILED;
   }
-#endif /*SUPPORT_TLS*/
+#endif /*DISABLE_TLS*/
 
 /* If TLS is active, we have just started it up and re-done the EHLO command,
 so its response needs to be analyzed. If TLS is not active and this is a
@@ -2641,7 +2670,7 @@ continued session down a previously-used socket, we haven't just done EHLO, so
 we skip this. */
 
 if (continue_hostname == NULL
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
     || tls_out.active.sock >= 0
 #endif
     )
@@ -2657,7 +2686,7 @@ if (continue_hostname == NULL
 #ifdef EXPERIMENTAL_PIPE_CONNECT
        | (sx->lmtp && ob->lmtp_ignore_quota ? OPTION_IGNQ : 0)
        | OPTION_DSN | OPTION_PIPE | OPTION_SIZE
-       | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8 | OPTION_REQUIRETLS
+       | OPTION_CHUNKING | OPTION_PRDR | OPTION_UTF8
        | (tls_out.active.sock >= 0 ? OPTION_EARLY_PIPE : 0) /* not for lmtp */
 
 #else
@@ -2673,9 +2702,6 @@ if (continue_hostname == NULL
        | OPTION_DSN
        | OPTION_PIPE
        | (ob->size_addition >= 0 ? OPTION_SIZE : 0)
-# if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-       | (tls_requiretls & REQUIRETLS_MSG ? OPTION_REQUIRETLS : 0)
-# endif
 #endif
       );
 #ifdef EXPERIMENTAL_PIPE_CONNECT
@@ -2726,16 +2752,6 @@ if (continue_hostname == NULL
     DEBUG(D_transport) debug_printf("%susing DSN\n",
                        sx->peer_offered & OPTION_DSN ? "" : "not ");
 
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-    if (sx->peer_offered & OPTION_REQUIRETLS)
-      {
-      smtp_peer_options |= OPTION_REQUIRETLS;
-      DEBUG(D_transport) debug_printf(
-       tls_requiretls & REQUIRETLS_MSG
-       ? "using REQUIRETLS\n" : "REQUIRETLS offered\n");
-      }
-#endif
-
 #ifdef EXPERIMENTAL_PIPE_CONNECT
     if (  sx->early_pipe_ok
        && !sx->early_pipe_active
@@ -2821,22 +2837,6 @@ if (sx->utf8_needed && !(sx->peer_offered & OPTION_UTF8))
   }
 #endif /*SUPPORT_I18N*/
 
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-  /*XXX should tls_requiretls actually be per-addr? */
-
-if (  tls_requiretls & REQUIRETLS_MSG
-   && !(sx->peer_offered & OPTION_REQUIRETLS)
-   )
-  {
-  sx->setting_up = TRUE;
-  errno = ERRNO_REQUIRETLS;
-  message = US"REQUIRETLS support is required from the server"
-    " but it was not offered";
-  DEBUG(D_transport) debug_printf("%s\n", message);
-  goto TLS_FAILED;
-  }
-#endif
-
 return OK;
 
 
@@ -2871,15 +2871,9 @@ return OK;
   in message and errno, and setting_up will always be true. Treat as
   a temporary error. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
   TLS_FAILED:
-# ifdef EXPERIMENTAL_REQUIRETLS
-    if (errno == ERRNO_REQUIRETLS)
-      code = '5', yield = FAIL;
-      /*XXX DSN will be labelled 500; prefer 530 5.7.4 */
-    else
-# endif
-      code = '4', yield = DEFER;
+    code = '4', yield = DEFER;
     goto FAILED;
 #endif
 
@@ -2907,7 +2901,8 @@ FAILED:
                        || errno == ERRNO_UTF8_FWD
 #endif
            ? FAIL : DEFER,
-           pass_message, sx->conn_args.host
+           pass_message,
+           errno == ECONNREFUSED ? NULL : sx->conn_args.host
 #ifdef EXPERIMENTAL_DSN_INFO
            , sx->smtp_greeting, sx->helo_response
 #endif
@@ -2920,7 +2915,7 @@ SEND_QUIT:
 if (sx->send_quit)
   (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n");
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 if (sx->cctx.tls_ctx)
   {
   tls_close(sx->cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
@@ -3017,11 +3012,6 @@ if (  sx->peer_offered & OPTION_UTF8
   Ustrcpy(p, " SMTPUTF8"), p += 9;
 #endif
 
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-if (tls_requiretls & REQUIRETLS_MSG)
-  Ustrcpy(p, " REQUIRETLS") , p += 11;
-#endif
-
 /* check if all addresses have DSN-lasthop flag; do not send RET and ENVID if so */
 for (sx->dsn_all_lasthop = TRUE, addr = addrlist, address_count = 0;
      addr && address_count < sx->max_rcpt;
@@ -3257,6 +3247,7 @@ for (addr = sx->first_addr, address_count = 0;
 
 #ifdef EXPERIMENTAL_PIPE_CONNECT
       case -4: return -1;                      /* non-2xx for pipelined banner or EHLO */
+      case -5: return -1;                      /* TLS first-read error */
 #endif
       }
     sx->pending_MAIL = FALSE;            /* Dealt with MAIL */
@@ -3268,7 +3259,7 @@ return 0;
 }
 
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 /*****************************************************
 * Proxy TLS connection for another transport process *
 ******************************************************/
@@ -3582,11 +3573,12 @@ if (  !(sx.peer_offered & OPTION_CHUNKING)
 
     case 1: sx.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
     if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
-    case 0: break;                       /* No 2xx or 5xx, but no probs */
+    case 0: break;                     /* No 2xx or 5xx, but no probs */
 
-    case -1: goto END_OFF;               /* Timeout on RCPT */
+    case -1: goto END_OFF;             /* Timeout on RCPT */
 
 #ifdef EXPERIMENTAL_PIPE_CONNECT
+    case -5:                           /* TLS first-read error */
     case -4:  HDEBUG(D_transport)
                debug_printf("failed reaping pipelined cmd responses\n");
 #endif
@@ -3723,19 +3715,20 @@ else
       {
       case 3: sx.ok = TRUE;            /* 2xx & 5xx => OK & progress made */
       case 2: sx.completed_addr = TRUE;    /* 5xx (only) => progress made */
-      break;
+             break;
 
-      case 1: sx.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
+      case 1: sx.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
       if (!sx.lmtp) sx.completed_addr = TRUE; /* can't tell about progress yet */
-      case 0: break;                       /* No 2xx or 5xx, but no probs */
+      case 0: break;                   /* No 2xx or 5xx, but no probs */
 
-      case -1: goto END_OFF;               /* Timeout on RCPT */
+      case -1: goto END_OFF;           /* Timeout on RCPT */
 
 #ifdef EXPERIMENTAL_PIPE_CONNECT
+      case -5:                         /* TLS first-read error */
       case -4:  HDEBUG(D_transport)
                  debug_printf("failed reaping pipelined cmd responses\n");
 #endif
-      default: goto RESPONSE_FAILED;       /* I/O error, or any MAIL/DATA error */
+      default: goto RESPONSE_FAILED;   /* I/O error, or any MAIL/DATA error */
       }
     }
 
@@ -4153,7 +4146,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
   if (  sx.first_addr != NULL
      || f.continue_more
      || (
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
           (  tls_out.active.sock < 0  &&  !continue_proxy_cipher
            || verify_check_given_host(CUSS &ob->hosts_nopass_tls, host) != OK
           )
@@ -4191,7 +4184,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
 
     if (sx.ok)
       {
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
       int pfd[2];
 #endif
       int socket_fd = sx.cctx.sock;
@@ -4208,7 +4201,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
       transport_pass_socket).  If the caller has more ready, just return with
       the connection still open. */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
       if (tls_out.active.sock >= 0)
        if (  f.continue_more
           || verify_check_given_host(CUSS &ob->hosts_noproxy_tls, host) == OK)
@@ -4267,7 +4260,7 @@ propagate it from the initial
        just passed the baton to.  Fork a child to to do it, and return to
        get logging done asap.  Which way to place the work makes assumptions
        about post-fork prioritisation which may not hold on all platforms. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
        if (tls_out.active.sock >= 0)
          {
          int pid = fork();
@@ -4335,7 +4328,7 @@ if (sx.send_quit) (void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n");
 
 END_OFF:
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
 tls_close(sx.cctx.tls_ctx, TLS_SHUTDOWN_NOWAIT);
 sx.cctx.tls_ctx = NULL;
 #endif
@@ -4454,7 +4447,7 @@ for (address_item * addr = addrlist; addr; addr = addr->next)
     addr->basic_errno = 0;
     addr->more_errno = (host->mx >= 0)? 'M' : 'A';
     addr->message = NULL;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
     addr->cipher = NULL;
     addr->ourcert = NULL;
     addr->peercert = NULL;
@@ -4531,12 +4524,6 @@ same one in order to be passed to a single transport - or if the transport has
 a host list with hosts_override set, use the host list supplied with the
 transport. It is an error for this not to exist. */
 
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
-if (tls_requiretls & REQUIRETLS_MSG)
-  ob->tls_tempfail_tryclear = FALSE;   /*XXX surely we should have a local for this
-                                       rather than modifying the transport? */
-#endif
-
 if (!hostlist || (ob->hosts_override && ob->hosts))
   {
   if (!ob->hosts)
@@ -5123,7 +5110,7 @@ retry_non_continued:
       session, so the in-clear transmission after those errors, if permitted,
       happens inside smtp_deliver().] */
 
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
       if (  rc == DEFER
         && first_addr->basic_errno == ERRNO_TLSFAILURE
         && ob->tls_tempfail_tryclear
@@ -5143,7 +5130,7 @@ retry_non_continued:
           deferred_event_raise(first_addr, host);
 # endif
         }
-#endif /*SUPPORT_TLS*/
+#endif /*DISABLE_TLS*/
       }
 
     /* Delivery attempt finished */
@@ -5316,7 +5303,7 @@ retry_non_continued:
     int fd = cutthrough.cctx.sock >= 0 ? cutthrough.cctx.sock : 0;
 
     DEBUG(D_transport) debug_printf("no hosts match already-open connection\n");
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
     /* A TLS conn could be open for a cutthrough, but not for a plain continued-
     transport */
 /*XXX doublecheck that! */