Constification
[users/heiko/exim.git] / src / src / transports / smtp.c
index b95913c59e5375c0cd8c13744da9fee1f60c523f..b2adeb555a260c5cdf92f41813adb0a905d9bc7b 100644 (file)
@@ -163,13 +163,13 @@ optionlist smtp_transport_options[] = {
   { "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, size_addition) },
 #ifdef SUPPORT_SOCKS
,{ "socks_proxy",          opt_stringptr,
-      (void *)offsetof(smtp_transport_options_block, socks_proxy) }
 { "socks_proxy",          opt_stringptr,
+      (void *)offsetof(smtp_transport_options_block, socks_proxy) },
 #endif
 #ifdef SUPPORT_TLS
,{ "tls_certificate",      opt_stringptr,
 { "tls_certificate",      opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_certificate) },
   { "tls_crl",              opt_stringptr,
       (void *)offsetof(smtp_transport_options_block, tls_crl) },
@@ -795,8 +795,9 @@ responses before returning, except after I/O errors and timeouts. */
 
 if (sx->pending_MAIL)
   {
+  DEBUG(D_transport) debug_printf("%s expect mail\n", __FUNCTION__);
   count--;
-  if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+  if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
                          '2', ob->command_timeout))
     {
     DEBUG(D_transport) debug_printf("bad response for MAIL\n");
@@ -812,7 +813,7 @@ if (sx->pending_MAIL)
         }
       while (count-- > 0)
         {
-        if (!smtp_read_response(&sx->inblock, flushbuffer, sizeof(flushbuffer),
+        if (!smtp_read_response(sx, flushbuffer, sizeof(flushbuffer),
                    '2', ob->command_timeout)
             && (errno != 0 || flushbuffer[0] == 0))
           break;
@@ -846,7 +847,8 @@ while (count-- > 0)
   /* The address was accepted */
   addr->host_used = sx->host;
 
-  if (smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+  DEBUG(D_transport) debug_printf("%s expect rcpt\n", __FUNCTION__);
+  if (smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
                          '2', ob->command_timeout))
     {
     yield |= 1;
@@ -963,25 +965,28 @@ if (addr) sx->sync_addr = addr->next;
 /* Handle a response to DATA. If we have not had any good recipients, either
 previously or in this block, the response is ignored. */
 
-if (pending_DATA != 0 &&
-    !smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
-                       '3', ob->command_timeout))
+if (pending_DATA != 0)
   {
-  int code;
-  uschar *msg;
-  BOOL pass_message;
-  if (pending_DATA > 0 || (yield & 1) != 0)
+  DEBUG(D_transport) debug_printf("%s expect data\n", __FUNCTION__);
+  if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
+                       '3', ob->command_timeout))
     {
-    if (errno == 0 && sx->buffer[0] == '4')
+    int code;
+    uschar *msg;
+    BOOL pass_message;
+    if (pending_DATA > 0 || (yield & 1) != 0)
       {
-      errno = ERRNO_DATA4XX;
-      sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
+      if (errno == 0 && sx->buffer[0] == '4')
+       {
+       errno = ERRNO_DATA4XX;
+       sx->first_addr->more_errno |= ((sx->buffer[1] - '0')*10 + sx->buffer[2] - '0') << 8;
+       }
+      return -3;
       }
-    return -3;
+    (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message);
+    DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining "
+      "is in use and there were no good recipients\n", msg);
     }
-  (void)check_response(sx->host, &errno, 0, sx->buffer, &code, &msg, &pass_message);
-  DEBUG(D_transport) debug_printf("%s\nerror for DATA ignored: pipelining "
-    "is in use and there were no good recipients\n", msg);
   }
 
 /* All responses read and handled; MAIL (if present) received 2xx and DATA (if
@@ -996,11 +1001,8 @@ return yield;
 /* Do the client side of smtp-level authentication */
 /*
 Arguments:
+  sx           smtp connection
   buffer       EHLO response from server (gets overwritten)
-  addrlist      chain of potential addresses to deliver
-  host          host to deliver to
-  ob           transport options
-  ibp, obp     comms channel control blocks
 
 Returns:
   OK                   Success, or failed (but not required): global "smtp_authenticated" set
@@ -1011,23 +1013,22 @@ Returns:
   FAIL                 - response
 */
 
-int
-smtp_auth(uschar *buffer, unsigned bufsize, address_item *addrlist, host_item *host,
-    smtp_transport_options_block *ob, BOOL is_esmtp,
-    smtp_inblock *ibp, smtp_outblock *obp)
+static int
+smtp_auth(smtp_context * sx, uschar * buffer, unsigned bufsize)
 {
+smtp_transport_options_block * ob = sx->ob;
 int require_auth;
 uschar *fail_reason = US"server did not advertise AUTH support";
 
-smtp_authenticated = FALSE;
+f.smtp_authenticated = FALSE;
 client_authenticator = client_authenticated_id = client_authenticated_sender = NULL;
-require_auth = verify_check_given_host(&ob->hosts_require_auth, host);
+require_auth = verify_check_given_host(CUSS &ob->hosts_require_auth, sx->host);
 
-if (is_esmtp && !regex_AUTH) regex_AUTH =
+if (sx->esmtp && !regex_AUTH) regex_AUTH =
     regex_must_compile(US"\\n250[\\s\\-]AUTH\\s+([\\-\\w\\s]+)(?:\\n|$)",
          FALSE, TRUE);
 
-if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
+if (sx->esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
   {
   uschar *names = string_copyn(expand_nstring[1], expand_nlength[1]);
   expand_nmax = -1;                          /* reset */
@@ -1036,7 +1037,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
   regex match above. */
 
   if (require_auth == OK ||
-      verify_check_given_host(&ob->hosts_try_auth, host) == OK)
+      verify_check_given_host(CUSS &ob->hosts_try_auth, sx->host) == OK)
     {
     auth_instance *au;
     fail_reason = US"no common mechanisms were found";
@@ -1049,7 +1050,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
     If one is found, attempt to authenticate by calling its client function.
     */
 
-    for (au = auths; !smtp_authenticated && au; au = au->next)
+    for (au = auths; !f.smtp_authenticated && au; au = au->next)
       {
       uschar *p = names;
       if (!au->client ||
@@ -1084,10 +1085,10 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
        that reflections don't show it. */
 
        fail_reason = US"authentication attempt(s) failed";
-       obp->authenticating = TRUE;
-       rc = (au->info->clientcode)(au, ibp, obp,
+       sx->outblock.authenticating = TRUE;
+       rc = (au->info->clientcode)(au, sx,
          ob->command_timeout, buffer, bufsize);
-       obp->authenticating = FALSE;
+       sx->outblock.authenticating = FALSE;
        DEBUG(D_transport) debug_printf("%s authenticator yielded %d\n",
          au->name, rc);
 
@@ -1099,7 +1100,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
        switch(rc)
          {
          case OK:
-         smtp_authenticated = TRUE;   /* stops the outer loop */
+         f.smtp_authenticated = TRUE;   /* stops the outer loop */
          client_authenticator = au->name;
          if (au->set_client_id != NULL)
            client_authenticated_id = expand_string(au->set_client_id);
@@ -1115,7 +1116,7 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
          case FAIL:
          if (errno != 0 || buffer[0] != '5') return FAIL;
          log_write(0, LOG_MAIN, "%s authenticator failed H=%s [%s] %s",
-           au->name, host->name, host->address, buffer);
+           au->name, sx->host->name, sx->host->address, buffer);
          break;
 
          /* Failure by some other means. In effect, the authenticator
@@ -1127,14 +1128,14 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
          case CANCELLED:
          if (*buffer != 0)
            log_write(0, LOG_MAIN, "%s authenticator cancelled "
-             "authentication H=%s [%s] %s", au->name, host->name,
-             host->address, buffer);
+             "authentication H=%s [%s] %s", au->name, sx->host->name,
+             sx->host->address, buffer);
          break;
 
          /* Internal problem, message in buffer. */
 
          case ERROR:
-         set_errno_nohost(addrlist, ERRNO_AUTHPROB, string_copy(buffer),
+         set_errno_nohost(sx->addrlist, ERRNO_AUTHPROB, string_copy(buffer),
                    DEFER, FALSE);
          return ERROR;
          }
@@ -1147,9 +1148,9 @@ if (is_esmtp && regex_match_and_setup(regex_AUTH, buffer, 0, -1))
 
 /* If we haven't authenticated, but are required to, give up. */
 
-if (require_auth == OK && !smtp_authenticated)
+if (require_auth == OK && !f.smtp_authenticated)
   {
-  set_errno_nohost(addrlist, ERRNO_AUTHFAIL,
+  set_errno_nohost(sx->addrlist, ERRNO_AUTHFAIL,
     string_sprintf("authentication required but %s", fail_reason), DEFER,
     FALSE);
   return DEFER;
@@ -1166,7 +1167,7 @@ Arguments
   addrlist      chain of potential addresses to deliver
   ob           transport options
 
-Globals                smtp_authenticated
+Globals                f.smtp_authenticated
                client_authenticated_sender
 Return True on error, otherwise buffer has (possibly empty) terminated string
 */
@@ -1178,7 +1179,7 @@ smtp_mail_auth_str(uschar *buffer, unsigned bufsize, address_item *addrlist,
 uschar *local_authenticated_sender = authenticated_sender;
 
 #ifdef notdef
-  debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, smtp_authenticated?"Y":"N");
+  debug_printf("smtp_mail_auth_str: as<%s> os<%s> SA<%s>\n", authenticated_sender, ob->authenticated_sender, f.smtp_authenticated?"Y":"N");
 #endif
 
 if (ob->authenticated_sender != NULL)
@@ -1186,7 +1187,7 @@ if (ob->authenticated_sender != NULL)
   uschar *new = expand_string(ob->authenticated_sender);
   if (new == NULL)
     {
-    if (!expand_string_forcedfail)
+    if (!f.expand_string_forcedfail)
       {
       uschar *message = string_sprintf("failed to expand "
         "authenticated_sender: %s", expand_string_message);
@@ -1199,7 +1200,7 @@ if (ob->authenticated_sender != NULL)
 
 /* Add the authenticated sender address if present */
 
-if ((smtp_authenticated || ob->authenticated_sender_force) &&
+if ((f.smtp_authenticated || ob->authenticated_sender_force) &&
     local_authenticated_sender != NULL)
   {
   string_format(buffer, bufsize, " AUTH=%s",
@@ -1246,7 +1247,29 @@ switch (rc)
     return DEFER; /* just defer this TLS'd conn */
 
   case DNS_SUCCEED:
-    if (sec) return OK;
+    if (sec)
+      {
+      DEBUG(D_transport)
+       {
+       dns_scan dnss;
+       dns_record * rr;
+       for (rr = dns_next_rr(dnsa, &dnss, RESET_ANSWERS); rr;
+            rr = dns_next_rr(dnsa, &dnss, RESET_NEXT)) if (rr->type == T_TLSA)
+         {
+         uint16_t payload_length = rr->size - 3;
+         uschar s[MAX_TLSA_EXPANDED_SIZE], * sp = s, * p = US rr->data;
+
+         sp += sprintf(CS sp, "%d ", *p++); /* usage */
+         sp += sprintf(CS sp, "%d ", *p++); /* selector */
+         sp += sprintf(CS sp, "%d ", *p++); /* matchtype */
+         while (payload_length-- > 0 && sp-s < (MAX_TLSA_EXPANDED_SIZE - 4))
+           sp += sprintf(CS sp, "%02x", *p++);
+
+         debug_printf(" %s\n", s);
+         }
+       }
+      return OK;
+      }
     log_write(0, LOG_MAIN,
       "DANE error: TLSA lookup for %s not DNSSEC", host->name);
     /*FALLTRHOUGH*/
@@ -1431,7 +1454,7 @@ there may be more writes (like, the chunk data) done soon. */
 
 if (chunk_size > 0)
   {
-  if((cmd_count = smtp_write_command(&sx->outblock,
+  if((cmd_count = smtp_write_command(sx,
              flags & tc_reap_prev ? SCMD_FLUSH : SCMD_MORE,
              "BDAT %u%s\r\n", chunk_size, flags & tc_chunk_last ? " LAST" : "")
      ) < 0) return ERROR;
@@ -1474,7 +1497,7 @@ if (sx->pending_BDAT)
   {
   DEBUG(D_transport) debug_printf("look for one response for BDAT\n");
 
-  if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
+  if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
        ob->command_timeout))
     {
     if (errno == 0 && sx->buffer[0] == '4')
@@ -1546,7 +1569,7 @@ sx->dsn_all_lasthop = TRUE;
 #if defined(SUPPORT_TLS) && defined(SUPPORT_DANE)
 sx->dane = FALSE;
 sx->dane_required =
-  verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK;
+  verify_check_given_host(CUSS &sx->ob->hosts_require_dane, sx->host) == OK;
 #endif
 
 if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
@@ -1627,7 +1650,7 @@ if (!continue_hostname)
     if (sx->host->dnssec == DS_YES)
       {
       if(  sx->dane_required
-       || verify_check_given_host(&sx->ob->hosts_try_dane, sx->host) == OK
+       || verify_check_given_host(CUSS &sx->ob->hosts_try_dane, sx->host) == OK
        )
        switch (rc = tlsa_lookup(sx->host, &tlsa_dnsa, sx->dane_required))
          {
@@ -1724,7 +1747,7 @@ if (!continue_hostname)
 #ifdef TCP_QUICKACK
     (void) setsockopt(sx->cctx.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
 #endif
-    good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+    good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
       '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
     sx->smtp_greeting = string_copy(sx->buffer);
@@ -1796,7 +1819,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. */
 
-  sx->esmtp = verify_check_given_host(&sx->ob->hosts_avoid_esmtp, sx->host) != OK;
+  sx->esmtp = verify_check_given_host(CUSS &sx->ob->hosts_avoid_esmtp, sx->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
@@ -1814,11 +1837,11 @@ goto SEND_QUIT;
 
   if (sx->esmtp)
     {
-    if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "%s %s\r\n",
+    if (smtp_write_command(sx, SCMD_FLUSH, "%s %s\r\n",
          sx->lmtp ? "LHLO" : "EHLO", sx->helo_data) < 0)
       goto SEND_FAILED;
     sx->esmtp_sent = TRUE;
-    if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
+    if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
            sx->ob->command_timeout))
       {
       if (errno != 0 || sx->buffer[0] == 0 || sx->lmtp)
@@ -1847,10 +1870,9 @@ goto SEND_QUIT;
     if (sx->esmtp_sent && (n = Ustrlen(sx->buffer)) < sizeof(sx->buffer)/2)
       { rsp = sx->buffer + n + 1; n = sizeof(sx->buffer) - n; }
 
-    if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "HELO %s\r\n", sx->helo_data) < 0)
+    if (smtp_write_command(sx, SCMD_FLUSH, "HELO %s\r\n", sx->helo_data) < 0)
       goto SEND_FAILED;
-    good_response = smtp_read_response(&sx->inblock, rsp, n,
-      '2', sx->ob->command_timeout);
+    good_response = smtp_read_response(sx, rsp, n, '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
     sx->helo_response = string_copy(rsp);
 #endif
@@ -1925,7 +1947,7 @@ else
      )
     {
     sx->peer_offered = smtp_peer_options;
-    pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
+    sx->pipelining_used = pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
     HDEBUG(D_transport) debug_printf("continued connection, %s TLS\n",
       continue_proxy_cipher ? "proxied" : "verify conn with");
     return OK;
@@ -1944,13 +1966,13 @@ for error analysis. */
 #ifdef SUPPORT_TLS
 if (  smtp_peer_options & OPTION_TLS
    && !suppress_tls
-   && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK
+   && verify_check_given_host(CUSS &sx->ob->hosts_avoid_tls, sx->host) != OK
    && (  !sx->verify
-      || verify_check_given_host(&sx->ob->hosts_verify_avoid_tls, sx->host) != OK
+      || verify_check_given_host(CUSS &sx->ob->hosts_verify_avoid_tls, sx->host) != OK
    )  )
   {
   uschar buffer2[4096];
-  if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "STARTTLS\r\n") < 0)
+  if (smtp_write_command(sx, SCMD_FLUSH, "STARTTLS\r\n") < 0)
     goto SEND_FAILED;
 
   /* If there is an I/O error, transmission of this message is deferred. If
@@ -1960,7 +1982,7 @@ if (  smtp_peer_options & OPTION_TLS
   STARTTLS, we carry on. This means we will try to send the message in clear,
   unless the host is in hosts_require_tls (tested below). */
 
-  if (!smtp_read_response(&sx->inblock, buffer2, sizeof(buffer2), '2',
+  if (!smtp_read_response(sx, buffer2, sizeof(buffer2), '2',
       sx->ob->command_timeout))
     {
     if (  errno != 0
@@ -2055,7 +2077,7 @@ if (tls_out.active.sock >= 0)
   /* For SMTPS we need to wait for the initial OK response. */
   if (sx->smtps)
     {
-    good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+    good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
       '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
     sx->smtp_greeting = string_copy(sx->buffer);
@@ -2072,10 +2094,10 @@ if (tls_out.active.sock >= 0)
       debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
     }
 
-  if (smtp_write_command(&sx->outblock, SCMD_FLUSH, "%s %s\r\n",
+  if (smtp_write_command(sx, SCMD_FLUSH, "%s %s\r\n",
         sx->lmtp ? "LHLO" : greeting_cmd, sx->helo_data) < 0)
     goto SEND_FAILED;
-  good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+  good_response = smtp_read_response(sx, sx->buffer, sizeof(sx->buffer),
     '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
   sx->helo_response = string_copy(sx->buffer);
@@ -2094,7 +2116,7 @@ else if (  sx->smtps
 # ifdef EXPERIMENTAL_REQUIRETLS
        || tls_requiretls & REQUIRETLS_MSG
 # endif
-       || verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
+       || verify_check_given_host(CUSS &sx->ob->hosts_require_tls, sx->host) == OK
        )
   {
   errno =
@@ -2162,14 +2184,14 @@ if (continue_hostname == NULL
     the current host matches hosts_avoid_pipelining, don't do it. */
 
     if (  sx->peer_offered & OPTION_PIPE
-       && verify_check_given_host(&sx->ob->hosts_avoid_pipelining, sx->host) != OK)
+       && verify_check_given_host(CUSS &sx->ob->hosts_avoid_pipelining, sx->host) != OK)
       smtp_peer_options |= OPTION_PIPE;
 
     DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
       smtp_peer_options & OPTION_PIPE ? "" : "not ");
 
     if (  sx->peer_offered & OPTION_CHUNKING
-       && verify_check_given_host(&sx->ob->hosts_try_chunking, sx->host) != OK)
+       && verify_check_given_host(CUSS &sx->ob->hosts_try_chunking, sx->host) != OK)
       sx->peer_offered &= ~OPTION_CHUNKING;
 
     if (sx->peer_offered & OPTION_CHUNKING)
@@ -2177,7 +2199,7 @@ if (continue_hostname == NULL
 
 #ifndef DISABLE_PRDR
     if (  sx->peer_offered & OPTION_PRDR
-       && verify_check_given_host(&sx->ob->hosts_try_prdr, sx->host) != OK)
+       && verify_check_given_host(CUSS &sx->ob->hosts_try_prdr, sx->host) != OK)
       sx->peer_offered &= ~OPTION_PRDR;
 
     if (sx->peer_offered & OPTION_PRDR)
@@ -2204,8 +2226,7 @@ if (continue_hostname == NULL
     the business. The host name and address must be available when the
     authenticator's client driver is running. */
 
-    switch (yield = smtp_auth(sx->buffer, sizeof(sx->buffer), sx->addrlist, sx->host,
-                             sx->ob, sx->esmtp, &sx->inblock, &sx->outblock))
+    switch (yield = smtp_auth(sx, sx->buffer, sizeof(sx->buffer)))
       {
       default:         goto SEND_QUIT;
       case OK:         break;
@@ -2214,7 +2235,7 @@ if (continue_hostname == NULL
       }
     }
   }
-pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
+sx->pipelining_used = pipelining_active = !!(smtp_peer_options & OPTION_PIPE);
 
 /* The setting up of the SMTP call is now complete. Any subsequent errors are
 message-specific. */
@@ -2368,7 +2389,7 @@ FAILED:
 SEND_QUIT:
 
 if (sx->send_quit)
-  (void)smtp_write_command(&sx->outblock, SCMD_FLUSH, "QUIT\r\n");
+  (void)smtp_write_command(sx, SCMD_FLUSH, "QUIT\r\n");
 
 #ifdef SUPPORT_TLS
 if (sx->cctx.tls_ctx)
@@ -2606,7 +2627,7 @@ sx->pending_MAIL = TRUE;     /* The block starts with MAIL */
     }
 #endif
 
-  rc = smtp_write_command(&sx->outblock, pipelining_active ? SCMD_BUFFER : SCMD_FLUSH,
+  rc = smtp_write_command(sx, pipelining_active ? SCMD_BUFFER : SCMD_FLUSH,
          "MAIL FROM:<%s>%s\r\n", s, sx->buffer);
   }
 
@@ -2618,7 +2639,7 @@ switch(rc)
     return -5;
 
   case +1:                /* Cmd was sent */
-    if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
+    if (!smtp_read_response(sx, sx->buffer, sizeof(sx->buffer), '2',
        sx->ob->command_timeout))
       {
       if (errno == 0 && sx->buffer[0] == '4')
@@ -2657,7 +2678,7 @@ for (addr = sx->first_addr, address_count = 0;
   BOOL no_flush;
   uschar * rcpt_addr;
 
-  if (tcp_out_fastopen && !tcp_out_fastopen_logged)
+  if (tcp_out_fastopen && !f.tcp_out_fastopen_logged)
     {
     setflag(addr, af_tcp_fastopen_conn);
     if (tcp_out_fastopen > 1) setflag(addr, af_tcp_fastopen);
@@ -2690,7 +2711,7 @@ for (addr = sx->first_addr, address_count = 0;
     }
 #endif
 
-  count = smtp_write_command(&sx->outblock, no_flush ? SCMD_BUFFER : SCMD_FLUSH,
+  count = smtp_write_command(sx, no_flush ? SCMD_BUFFER : SCMD_FLUSH,
     "RCPT TO:<%s>%s%s\r\n", rcpt_addr, sx->igquotstr, sx->buffer);
 
   if (count < 0) return -5;
@@ -2716,7 +2737,7 @@ for (addr = sx->first_addr, address_count = 0;
     }
   }      /* Loop for next address */
 
-tcp_out_fastopen_logged = TRUE;
+f.tcp_out_fastopen_logged = TRUE;
 sx->next_addr = addr;
 return 0;
 }
@@ -2756,7 +2777,7 @@ if ((rc = fork()))
   _exit(rc < 0 ? EXIT_FAILURE : EXIT_SUCCESS);
   }
 
-if (running_in_test_harness) millisleep(100); /* let parent debug out */
+if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
 set_process_info("proxying TLS connection for continued transport");
 FD_ZERO(&rfds);
 FD_SET(tls_out.active.sock, &rfds);
@@ -2830,7 +2851,7 @@ for (fd_bits = 3; fd_bits; )
   }
 
 done:
-  if (running_in_test_harness) millisleep(100);        /* let logging complete */
+  if (f.running_in_test_harness) millisleep(100);      /* let logging complete */
   exim_exit(0, US"TLS proxy");
 }
 #endif
@@ -3028,7 +3049,7 @@ to send is. */
 if (  !(sx.peer_offered & OPTION_CHUNKING)
    && (sx.ok || (pipelining_active && !mua_wrapper)))
   {
-  int count = smtp_write_command(&sx.outblock, SCMD_FLUSH, "DATA\r\n");
+  int count = smtp_write_command(&sx, SCMD_FLUSH, "DATA\r\n");
 
   if (count < 0) goto SEND_FAILED;
   switch(sync_responses(&sx, count, sx.ok ? +1 : -1))
@@ -3122,7 +3143,7 @@ else
       {
       if (!(sx.ob->dkim.arc_signspec = s = expand_string(s)))
        {
-       if (!expand_string_forcedfail)
+       if (!f.expand_string_forcedfail)
          {
          message = US"failed to expand arc_sign";
          sx.ok = FALSE;
@@ -3191,7 +3212,7 @@ else
    * with per non-PRDR. */
   if(sx.prdr_active)
     {
-    sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '3',
+    sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '3',
       sx.ob->final_timeout);
     if (!sx.ok && errno == 0) switch(sx.buffer[0])
       {
@@ -3212,7 +3233,7 @@ else
 
   if (!sx.lmtp)
     {
-    sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
+    sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
       sx.ob->final_timeout);
     if (!sx.ok && errno == 0 && sx.buffer[0] == '4')
       {
@@ -3276,7 +3297,7 @@ else
       if (sx.lmtp)
 #endif
         {
-        if (!smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
+        if (!smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
             sx.ob->final_timeout))
           {
           if (errno != 0 || sx.buffer[0] == 0) goto RESPONSE_FAILED;
@@ -3320,6 +3341,8 @@ else
       addr->host_used = host;
       addr->special_action = flag;
       addr->message = conf;
+
+      if (sx.pipelining_used) setflag(addr, af_pipelining);
 #ifndef DISABLE_PRDR
       if (sx.prdr_active) setflag(addr, af_prdr_used);
 #endif
@@ -3356,7 +3379,7 @@ else
        /* PRDR - get the final, overall response.  For any non-success
        upgrade all the address statuses. */
 
-        sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
+        sx.ok = smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer), '2',
           sx.ob->final_timeout);
         if (!sx.ok)
          {
@@ -3564,7 +3587,7 @@ hosts_nopass_tls. */
 DEBUG(D_transport)
   debug_printf("ok=%d send_quit=%d send_rset=%d continue_more=%d "
     "yield=%d first_address is %sNULL\n", sx.ok, sx.send_quit,
-    sx.send_rset, continue_more, yield, sx.first_addr ? "not " : "");
+    sx.send_rset, f.continue_more, yield, sx.first_addr ? "not " : "");
 
 if (sx.completed_addr && sx.ok && sx.send_quit)
   {
@@ -3575,11 +3598,11 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
   t_compare.current_sender_address = sender_address;
 
   if (  sx.first_addr != NULL
-     || continue_more
+     || f.continue_more
      || (
 #ifdef SUPPORT_TLS
           (  tls_out.active.sock < 0  &&  !continue_proxy_cipher
-           || verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK
+           || verify_check_given_host(CUSS &sx.ob->hosts_nopass_tls, host) != OK
           )
         &&
 #endif
@@ -3592,13 +3615,13 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
     BOOL pass_message;
 
     if (sx.send_rset)
-      if (! (sx.ok = smtp_write_command(&sx.outblock, SCMD_FLUSH, "RSET\r\n") >= 0))
+      if (! (sx.ok = smtp_write_command(&sx, SCMD_FLUSH, "RSET\r\n") >= 0))
         {
         msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
           host->address, strerror(errno));
         sx.send_quit = FALSE;
         }
-      else if (! (sx.ok = smtp_read_response(&sx.inblock, sx.buffer,
+      else if (! (sx.ok = smtp_read_response(&sx, sx.buffer,
                  sizeof(sx.buffer), '2', sx.ob->command_timeout)))
         {
         int code;
@@ -3615,7 +3638,9 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
 
     if (sx.ok)
       {
+#ifdef SUPPORT_TLS
       int pfd[2];
+#endif
       int socket_fd = sx.cctx.sock;
 
 
@@ -3632,8 +3657,8 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
 
 #ifdef SUPPORT_TLS
       if (tls_out.active.sock >= 0)
-       if (  continue_more
-          || verify_check_given_host(&sx.ob->hosts_noproxy_tls, host) == OK)
+       if (  f.continue_more
+          || verify_check_given_host(CUSS &sx.ob->hosts_noproxy_tls, host) == OK)
          {
          /* Before passing the socket on, or returning to caller with it still
          open, we must shut down TLS.  Not all MTAs allow for the continuation
@@ -3645,12 +3670,12 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
          sx.cctx.tls_ctx = NULL;
          smtp_peer_options = smtp_peer_options_wrap;
          sx.ok = !sx.smtps
-           && smtp_write_command(&sx.outblock, SCMD_FLUSH,
-                                     "EHLO %s\r\n", sx.helo_data) >= 0
-           && smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer),
+           && smtp_write_command(&sx, SCMD_FLUSH, "EHLO %s\r\n", sx.helo_data)
+               >= 0
+           && smtp_read_response(&sx, sx.buffer, sizeof(sx.buffer),
                                      '2', sx.ob->command_timeout);
 
-         if (sx.ok && continue_more)
+         if (sx.ok && f.continue_more)
            return yield;               /* More addresses for another run */
          }
        else
@@ -3670,7 +3695,7 @@ if (sx.completed_addr && sx.ok && sx.send_quit)
          }
       else
 #endif
-       if (continue_more)
+       if (f.continue_more)
          return yield;                 /* More addresses for another run */
 
       /* If the socket is successfully passed, we mustn't send QUIT (or
@@ -3695,7 +3720,7 @@ propagate it from the initial
          int pid = fork();
          if (pid == 0)         /* child; fork again to disconnect totally */
            {
-           if (running_in_test_harness) millisleep(100); /* let parent debug out */
+           if (f.running_in_test_harness) millisleep(100); /* let parent debug out */
            /* does not return */
            smtp_proxy_tls(sx.cctx.tls_ctx, sx.buffer, sizeof(sx.buffer), pfd,
                            sx.ob->command_timeout);
@@ -3750,7 +3775,7 @@ This change is being made on 31-Jul-98. After over a year of trouble-free
 operation, the old commented-out code was removed on 17-Sep-99. */
 
 SEND_QUIT:
-if (sx.send_quit) (void)smtp_write_command(&sx.outblock, SCMD_FLUSH, "QUIT\r\n");
+if (sx.send_quit) (void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n");
 
 END_OFF:
 
@@ -3814,8 +3839,7 @@ smtp_transport_closedown(transport_instance *tblock)
 smtp_transport_options_block *ob =
   (smtp_transport_options_block *)tblock->options_block;
 client_conn_ctx cctx;
-smtp_inblock inblock;
-smtp_outblock outblock;
+smtp_context sx;
 uschar buffer[256];
 uschar inbuffer[4096];
 uschar outbuffer[16];
@@ -3824,22 +3848,21 @@ uschar outbuffer[16];
 cctx.sock = fileno(stdin);
 cctx.tls_ctx = cctx.sock == tls_out.active.sock ? tls_out.active.tls_ctx : NULL;
 
-inblock.cctx = &cctx;
-inblock.buffer = inbuffer;
-inblock.buffersize = sizeof(inbuffer);
-inblock.ptr = inbuffer;
-inblock.ptrend = inbuffer;
-
-outblock.cctx = &cctx;
-outblock.buffersize = sizeof(outbuffer);
-outblock.buffer = outbuffer;
-outblock.ptr = outbuffer;
-outblock.cmd_count = 0;
-outblock.authenticating = FALSE;
-
-(void)smtp_write_command(&outblock, SCMD_FLUSH, "QUIT\r\n");
-(void)smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
-  ob->command_timeout);
+sx.inblock.cctx = &cctx;
+sx.inblock.buffer = inbuffer;
+sx.inblock.buffersize = sizeof(inbuffer);
+sx.inblock.ptr = inbuffer;
+sx.inblock.ptrend = inbuffer;
+
+sx.outblock.cctx = &cctx;
+sx.outblock.buffersize = sizeof(outbuffer);
+sx.outblock.buffer = outbuffer;
+sx.outblock.ptr = outbuffer;
+sx.outblock.cmd_count = 0;
+sx.outblock.authenticating = FALSE;
+
+(void)smtp_write_command(&sx, SCMD_FLUSH, "QUIT\r\n");
+(void)smtp_read_response(&sx, buffer, sizeof(buffer), '2', ob->command_timeout);
 (void)close(cctx.sock);
 }
 
@@ -3993,7 +4016,7 @@ if (!hostlist || (ob->hosts_override && ob->hosts))
         {
         addrlist->message = string_sprintf("failed to expand list of hosts "
           "\"%s\" in %s transport: %s", s, tblock->name, expand_string_message);
-        addrlist->transport_return = search_find_defer ? DEFER : PANIC;
+        addrlist->transport_return = f.search_find_defer ? DEFER : PANIC;
         return FALSE;     /* Only top address has status */
         }
       DEBUG(D_transport) debug_printf("expanded list of hosts \"%s\" to "
@@ -4281,7 +4304,7 @@ retry_non_continued:
     were not in it. We don't want to hold up all SMTP deliveries! Except when
     doing a two-stage queue run, don't do this if forcing. */
 
-    if ((!deliver_force || queue_2stage) && (queue_smtp ||
+    if ((!f.deliver_force || f.queue_2stage) && (f.queue_smtp ||
         match_isinlist(addrlist->domain,
          (const uschar **)&queue_smtp_domains, 0,
           &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK))
@@ -4412,7 +4435,7 @@ retry_non_continued:
     sending the message down a pre-existing connection. */
 
     if (  !continue_hostname
-       && verify_check_given_host(&ob->serialize_hosts, host) == OK)
+       && verify_check_given_host(CUSS &ob->serialize_hosts, host) == OK)
       {
       serialize_key = string_sprintf("host-serialize-%s", host->name);
       if (!enq_start(serialize_key, 1))
@@ -4445,7 +4468,7 @@ retry_non_continued:
     /* This is not for real; don't do the delivery. If there are
     any remaining hosts, list them. */
 
-    if (dont_deliver)
+    if (f.dont_deliver)
       {
       host_item *host2;
       set_errno_nohost(addrlist, 0, NULL, OK, FALSE);
@@ -4553,7 +4576,7 @@ retry_non_continued:
       if (  rc == DEFER
         && first_addr->basic_errno == ERRNO_TLSFAILURE
         && ob->tls_tempfail_tryclear
-        && verify_check_given_host(&ob->hosts_require_tls, host) != OK
+        && verify_check_given_host(CUSS &ob->hosts_require_tls, host) != OK
         )
         {
         log_write(0, LOG_MAIN,
@@ -4816,7 +4839,7 @@ for (addr = addrlist; addr; addr = addr->next)
       setflag(addr, af_retry_skipped);
       }
 
-  if (queue_smtp)    /* no deliveries attempted */
+  if (f.queue_smtp)    /* no deliveries attempted */
     {
     addr->transport_return = DEFER;
     addr->basic_errno = 0;