Split out smtp_setup_conn() from smtp_deliver, in the transport
authorJeremy Harris <jgh146exb@wizmail.org>
Mon, 19 Dec 2016 13:10:29 +0000 (13:10 +0000)
committerJeremy Harris <jgh146exb@wizmail.org>
Sat, 21 Jan 2017 21:49:50 +0000 (21:49 +0000)
src/src/dns.c
src/src/transports/smtp.c
test/log/0211
test/mail/0211.CALLER
test/scripts/0000-Basic/0211
test/stdout/0211

index bc9d895..815a978 100644 (file)
@@ -349,8 +349,8 @@ trace = trace;
 
 if (reset != RESET_NEXT)
   {
-  TRACE debug_printf("%s: reset\n", __FUNCTION__);
   dnss->rrcount = ntohs(h->qdcount);
+  TRACE debug_printf("%s: reset (Q rrcount %d)\n", __FUNCTION__, dnss->rrcount);
   dnss->aptr = dnsa->answer + sizeof(HEADER);
 
   /* Skip over questions; failure to expand the name just gives up */
@@ -369,6 +369,7 @@ if (reset != RESET_NEXT)
   /* Get the number of answer records. */
 
   dnss->rrcount = ntohs(h->ancount);
+  TRACE debug_printf("%s: reset (A rrcount %d)\n", __FUNCTION__, dnss->rrcount);
 
   /* Skip over answers if we want to look at the authority section. Also skip
   the NS records (i.e. authority section) if wanting to look at the additional
@@ -378,6 +379,7 @@ if (reset != RESET_NEXT)
     {
     TRACE debug_printf("%s: additional\n", __FUNCTION__);
     dnss->rrcount += ntohs(h->nscount);
+    TRACE debug_printf("%s: reset (NS rrcount %d)\n", __FUNCTION__, dnss->rrcount);
     }
 
   if (reset == RESET_AUTHORITY || reset == RESET_ADDITIONAL)
@@ -400,6 +402,8 @@ if (reset != RESET_NEXT)
       }
     dnss->rrcount = reset == RESET_AUTHORITY
       ? ntohs(h->nscount) : ntohs(h->arcount);
+    TRACE debug_printf("%s: reset (%s rrcount %d)\n", __FUNCTION__,
+      reset == RESET_AUTHORITY ? "NS" : "AR", dnss->rrcount);
     }
   TRACE debug_printf("%s: %d RRs to read\n", __FUNCTION__, dnss->rrcount);
   }
@@ -443,8 +447,8 @@ for convenience so that the scans can use nice-looking for loops. */
 return &dnss->srr;
 
 null_return:
-  TRACE debug_printf("%s: terminate (%d RRs left). Last op: %s\n",
-    __FUNCTION__, dnss->rrcount, trace);
+  TRACE debug_printf("%s: terminate (%d RRs left). Last op: %s; errno %d %s\n",
+    __FUNCTION__, dnss->rrcount, trace, errno, strerror(errno));
   dnss->rrcount = 0;
   return NULL;
 }
@@ -727,7 +731,7 @@ if (check_dns_names_pattern[0] != 0 && type != T_PTR && type != T_TXT)
     }
 
   if (pcre_exec(regex_check_dns_names, NULL, CCS checkname, Ustrlen(checkname),
-      0, PCRE_EOPT, ovector, sizeof(ovector)/sizeof(int)) < 0)
+      0, PCRE_EOPT, ovector, nelem(ovector)) < 0)
     {
     DEBUG(D_dns)
       debug_printf("DNS name syntax check failed: %s (%s)\n", name,
@@ -756,14 +760,14 @@ if ((type == T_A || type == T_AAAA) && string_is_ip_address(name, NULL) != 0)
 domains, and interfaces to a fake nameserver for certain special zones. */
 
 dnsa->answerlen = running_in_test_harness
-  ? fakens_search(name, type, dnsa->answer, MAXPACKET)
-  : res_search(CCS name, C_IN, type, dnsa->answer, MAXPACKET);
+  ? fakens_search(name, type, dnsa->answer, sizeof(dnsa->answer))
+  : res_search(CCS name, C_IN, type, dnsa->answer, sizeof(dnsa->answer));
 
-if (dnsa->answerlen > MAXPACKET)
+if (dnsa->answerlen > (int) sizeof(dnsa->answer))
   {
   DEBUG(D_dns) debug_printf("DNS lookup of %s (%s) resulted in overlong packet (size %d), truncating to %d.\n",
-    name, dns_text_type(type), dnsa->answerlen, MAXPACKET);
-  dnsa->answerlen = MAXPACKET;
+    name, dns_text_type(type), dnsa->answerlen, sizeof(dnsa->answer));
+  dnsa->answerlen = sizeof(dnsa->answer);
   }
 
 if (dnsa->answerlen < 0) switch (h_errno)
index 8708271..eadcd9f 100644 (file)
@@ -582,7 +582,7 @@ if (*errno_value == ERRNO_WRITEINCOMPLETE)
 /* 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");
+  *message = US"utf8 support required but not offered for forwarding";
   DEBUG(D_deliver|D_transport) debug_printf("%s\n", *message);
   return TRUE;
   }
@@ -1247,9 +1247,10 @@ typedef struct smtp_compare_s
     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.  */
+
+/* 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)
@@ -1467,65 +1468,16 @@ return OK;
 
 
 /*************************************************
-*       Deliver address list to given host       *
+*       Make connection for given message        *
 *************************************************/
 
-/* If continue_hostname is not null, we get here only when continuing to
-deliver down an existing channel. The channel was passed as the standard
-input. TLS is never active on a passed channel; the previous process always
-closes it down before passing the connection on.
-
-Otherwise, we have to make a connection to the remote host, and do the
-initial protocol exchange.
-
-When running as an MUA wrapper, if the sender or any recipient is rejected,
-temporarily or permanently, we force failure for all recipients.
-
-Arguments:
-  addrlist        chain of potential addresses to deliver; only those whose
-                  transport_return field is set to PENDING_DEFER are currently
-                  being processed; others should be skipped - they have either
-                  been delivered to an earlier host or IP address, or been
-                  failed by one of them.
-  host            host to deliver to
-  host_af         AF_INET or AF_INET6
-  port            default TCP/IP port to use, in host byte order
-  interface       interface to bind to, or NULL
-  tblock          transport instance block
-  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
-                    that is specific to the message.
-  suppress_tls    if TRUE, don't attempt a TLS connection - this is set for
-                    a second attempt after TLS initialization fails
-
-Returns:          OK    - the connection was made and the delivery attempted;
-                          the result for each address is in its data block.
-                  DEFER - the connection could not be made, or something failed
-                          while setting up the SMTP session, or there was a
-                          non-message-specific error, such as a timeout.
-                  ERROR - a filter command is specified for this transport,
-                          and there was a problem setting it up; OR helo_data
-                          or add_headers or authenticated_sender is specified
-                          for this transport, and the string failed to expand
-*/
+typedef struct {
+  address_item *       addrlist;
+  host_item *          host;
+  int                  host_af;
+  int                  port;
+  uschar *             interface;
 
-static int
-smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port,
-  uschar *interface, transport_instance *tblock,
-  BOOL *message_defer, BOOL suppress_tls)
-{
-address_item *addr;
-address_item *sync_addr;
-address_item *first_addr = addrlist;
-int yield = OK;
-int address_count;
-int save_errno;
-int rc;
-time_t start_delivery_time = time(NULL);
-smtp_transport_options_block *ob =
-  (smtp_transport_options_block *)(tblock->options_block);
-struct lflags {
   BOOL lmtp:1;
   BOOL smtps:1;
   BOOL ok:1;
@@ -1546,70 +1498,108 @@ struct lflags {
   BOOL dane:1;
   BOOL dane_required:1;
 #endif
-} lflags;
 
-BOOL pass_message = FALSE;
-BOOL completed_address = FALSE;
-uschar peer_offered = 0;
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
-dns_answer tlsa_dnsa;
-#endif
-smtp_inblock inblock;
-smtp_outblock outblock;
-int max_rcpt = tblock->max_addresses;
-uschar *igquotstr = US"";
+  int          max_rcpt;
 
+  uschar       peer_offered;
+  uschar *     igquotstr;
+  uschar *     helo_data;
 #ifdef EXPERIMENTAL_DSN_INFO
-uschar *smtp_greeting = NULL;
-uschar *helo_response = NULL;
+  uschar *     smtp_greeting;
+  uschar *     helo_response;
 #endif
-uschar *helo_data = NULL;
 
-uschar *message = NULL;
-uschar new_message_id[MESSAGE_ID_LENGTH + 1];
-uschar *p;
-uschar buffer[DELIVER_BUFFER_SIZE];
-uschar inbuffer[4096];
-uschar outbuffer[4096];
+  smtp_inblock  inblock;
+  smtp_outblock outblock;
+  uschar       buffer[DELIVER_BUFFER_SIZE];
+  uschar       inbuffer[4096];
+  uschar       outbuffer[4096];
 
-suppress_tls = suppress_tls;  /* stop compiler warning when no TLS support */
+  transport_instance *                 tblock;
+  smtp_transport_options_block *       ob;
+} smtp_context;
 
-lflags.lmtp = strcmpic(ob->protocol, US"lmtp") == 0;
-lflags.smtps = strcmpic(ob->protocol, US"smtps") == 0;
-lflags.ok = FALSE;
-lflags.send_rset = TRUE;
-lflags.send_quit = TRUE;
-lflags.setting_up = TRUE;
-lflags.esmtp = TRUE;
-lflags.esmtp_sent = FALSE;
+/*
+Arguments:
+  ctx            connection context
+  message_defer   return 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
+                    that is specific to the message.
+  suppress_tls    if TRUE, don't attempt a TLS connection - this is set for
+                    a second attempt after TLS initialization fails
+  verify         TRUE if connection is for a verify callout, FALSE for
+                 a delivery attempt
+
+Returns:          OK    - the connection was made and the delivery attempted;
+                          fd is set in the conn context, tls_out set up.
+                  DEFER - the connection could not be made, or something failed
+                          while setting up the SMTP session, or there was a
+                          non-message-specific error, such as a timeout.
+                  ERROR - helo_data or add_headers or authenticated_sender is
+                         specified for this transport, and the string failed
+                         to expand
+*/
+int
+smtp_setup_conn(smtp_context * sx, BOOL * message_defer, BOOL suppress_tls,
+       BOOL verify)
+{
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+dns_answer tlsa_dnsa;
+#endif
+BOOL pass_message = FALSE;
+
+uschar * message = NULL;
+int save_errno;
+int yield = OK;
+int rc;
+
+sx->ob = (smtp_transport_options_block *)(sx->tblock->options_block);
+
+sx->lmtp = strcmpic(sx->ob->protocol, US"lmtp") == 0;
+sx->smtps = strcmpic(sx->ob->protocol, US"smtps") == 0;
+sx->ok = FALSE;
+sx->send_rset = TRUE;
+sx->send_quit = TRUE;
+sx->setting_up = TRUE;
+sx->esmtp = TRUE;
+sx->esmtp_sent = FALSE;
 #ifdef SUPPORT_I18N
-lflags.utf8_needed = FALSE;
+sx->utf8_needed = FALSE;
 #endif
-lflags.dsn_all_lasthop = TRUE;
+sx->dsn_all_lasthop = TRUE;
 #if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
-lflags.dane = FALSE;
-lflags.dane_required = verify_check_given_host(&ob->hosts_require_dane, host) == OK;
+sx->dane = FALSE;
+sx->dane_required = verify_check_given_host(&sx->ob->hosts_require_dane, sx->host) == OK;
+#endif
+
+if ((sx->max_rcpt = sx->tblock->max_addresses) == 0) sx->max_rcpt = 999999;
+sx->helo_data = NULL;
+sx->peer_offered = 0;
+sx->igquotstr = US"";
+#ifdef EXPERIMENTAL_DSN_INFO
+sx->smtp_greeting = NULL;
+sx->helo_response = NULL;
 #endif
 
 *message_defer = FALSE;
 smtp_command = US"initial connection";
-buffer[0] = '\0';
-if (max_rcpt == 0) max_rcpt = 999999;
+sx->buffer[0] = '\0';
 
 /* Set up the buffer for reading SMTP response packets. */
 
-inblock.buffer = inbuffer;
-inblock.buffersize = sizeof(inbuffer);
-inblock.ptr = inbuffer;
-inblock.ptrend = inbuffer;
+sx->inblock.buffer = sx->inbuffer;
+sx->inblock.buffersize = sizeof(sx->inbuffer);
+sx->inblock.ptr = sx->inbuffer;
+sx->inblock.ptrend = sx->inbuffer;
 
 /* Set up the buffer for holding SMTP commands while pipelining */
 
-outblock.buffer = outbuffer;
-outblock.buffersize = sizeof(outbuffer);
-outblock.ptr = outbuffer;
-outblock.cmd_count = 0;
-outblock.authenticating = FALSE;
+sx->outblock.buffer = sx->outbuffer;
+sx->outblock.buffersize = sizeof(sx->outbuffer);
+sx->outblock.ptr = sx->outbuffer;
+sx->outblock.cmd_count = 0;
+sx->outblock.authenticating = FALSE;
 
 /* Reset the parameters of a TLS session. */
 
@@ -1630,9 +1620,9 @@ afterward as we're in a subprocess. */
 tls_modify_variables(&tls_out);
 
 #ifndef SUPPORT_TLS
-if (lflags.smtps)
+if (sx->smtps)
   {
-  set_errno_nohost(addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
+  set_errno_nohost(sx->addrlist, ERRNO_TLSFAILURE, US"TLS support not available",
            DEFER, FALSE);
   return ERROR;
   }
@@ -1645,12 +1635,13 @@ specially so they can be identified for retries. */
 if (continue_hostname == NULL)
   {
   /* This puts port into host->port */
-  inblock.sock = outblock.sock =
-    smtp_connect(host, host_af, port, interface, ob->connect_timeout, tblock);
+  sx->inblock.sock = sx->outblock.sock =
+    smtp_connect(sx->host, sx->host_af, sx->port, sx->interface,
+                 sx->ob->connect_timeout, sx->tblock);
 
-  if (inblock.sock < 0)
+  if (sx->inblock.sock < 0)
     {
-    set_errno_nohost(addrlist, errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
+    set_errno_nohost(sx->addrlist, errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
       NULL, DEFER, FALSE);
     return DEFER;
     }
@@ -1660,32 +1651,32 @@ if (continue_hostname == NULL)
     tls_out.dane_verified = FALSE;
     tls_out.tlsa_usage = 0;
 
-    if (host->dnssec == DS_YES)
+    if (sx->host->dnssec == DS_YES)
       {
-      if(  lflags.dane_required
-       || verify_check_given_host(&ob->hosts_try_dane, host) == OK
+      if(  sx->dane_required
+       || verify_check_given_host(&sx->ob->hosts_try_dane, sx->host) == OK
        )
-       switch (rc = tlsa_lookup(host, &tlsa_dnsa, lflags.dane_required))
+       switch (rc = tlsa_lookup(sx->host, &tlsa_dnsa, sx->dane_required))
          {
-         case OK:              lflags.dane = TRUE; break;
+         case OK:              sx->dane = TRUE; break;
          case FAIL_FORCED:     break;
-         default:              set_errno_nohost(addrlist, ERRNO_DNSDEFER,
+         default:              set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
                                  string_sprintf("DANE error: tlsa lookup %s",
                                    rc == DEFER ? "DEFER" : "FAIL"),
                                  rc, FALSE);
                                return rc;
          }
       }
-    else if (lflags.dane_required)
+    else if (sx->dane_required)
       {
-      set_errno_nohost(addrlist, ERRNO_DNSDEFER,
-       string_sprintf("DANE error: %s lookup not DNSSEC", host->name),
+      set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
+       string_sprintf("DANE error: %s lookup not DNSSEC", sx->host->name),
        FAIL, FALSE);
       return FAIL;
       }
 
-    if (lflags.dane)
-      ob->tls_tempfail_tryclear = FALSE;
+    if (sx->dane)
+      sx->ob->tls_tempfail_tryclear = FALSE;
     }
 #endif /*DANE*/
 
@@ -1693,15 +1684,15 @@ if (continue_hostname == NULL)
   sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is
   delayed till here so that $sending_interface and $sending_port are set. */
 
-  helo_data = expand_string(ob->helo_data);
+  sx->helo_data = expand_string(sx->ob->helo_data);
 #ifdef SUPPORT_I18N
-  if (helo_data)
+  if (sx->helo_data)
     {
     uschar * errstr = NULL;
-    if ((helo_data = string_domain_utf8_to_alabel(helo_data, &errstr)), errstr)
+    if ((sx->helo_data = string_domain_utf8_to_alabel(sx->helo_data, &errstr)), errstr)
       {
       errstr = string_sprintf("failed to expand helo_data: %s", errstr);
-      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
+      set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, errstr, DEFER, FALSE);
       yield = DEFER;
       goto SEND_QUIT;
       }
@@ -1712,29 +1703,29 @@ if (continue_hostname == NULL)
   is nevertheless a reasonably clean way of programming this kind of logic,
   where you want to escape on any error. */
 
-  if (!lflags.smtps)
+  if (!sx->smtps)
     {
     BOOL good_response;
 
 #ifdef TCP_QUICKACK
-    (void) setsockopt(inblock.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
+    (void) setsockopt(sx->inblock.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
 #endif
-    good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
-      '2', ob->command_timeout);
+    good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+      '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
-    smtp_greeting = string_copy(buffer);
+    sx->smtp_greeting = string_copy(sx->buffer);
 #endif
     if (!good_response) goto RESPONSE_FAILED;
 
 #ifndef DISABLE_EVENT
       {
       uschar * s;
-      lookup_dnssec_authenticated = host->dnssec==DS_YES ? US"yes"
-       : host->dnssec==DS_NO ? US"no" : NULL;
-      s = event_raise(tblock->event_action, US"smtp:connect", buffer);
+      lookup_dnssec_authenticated = sx->host->dnssec==DS_YES ? US"yes"
+       : sx->host->dnssec==DS_NO ? US"no" : NULL;
+      s = event_raise(sx->tblock->event_action, US"smtp:connect", sx->buffer);
       if (s)
        {
-       set_errno_nohost(addrlist, ERRNO_EXPANDFAIL,
+       set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL,
          string_sprintf("deferred by smtp:connect event expansion: %s", s),
          DEFER, FALSE);
        yield = DEFER;
@@ -1746,18 +1737,18 @@ if (continue_hostname == NULL)
     /* Now check if the helo_data expansion went well, and sign off cleanly if
     it didn't. */
 
-    if (!helo_data)
+    if (!sx->helo_data)
       {
-      uschar *message = string_sprintf("failed to expand helo_data: %s",
+      message = string_sprintf("failed to expand helo_data: %s",
         expand_string_message);
-      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+      set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
       yield = DEFER;
       goto SEND_QUIT;
       }
     }
 
 /** Debugging without sending a message
-addrlist->transport_return = DEFER;
+sx->addrlist->transport_return = DEFER;
 goto SEND_QUIT;
 **/
 
@@ -1791,96 +1782,95 @@ 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. */
 
-  lflags.esmtp = verify_check_given_host(&ob->hosts_avoid_esmtp, host) != OK;
+  sx->esmtp = verify_check_given_host(&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
   and be usable.  I can see this coming back to bite us. */
 #ifdef SUPPORT_TLS
-  if (lflags.smtps)
+  if (sx->smtps)
     {
     smtp_peer_options |= PEER_OFFERED_TLS;
     suppress_tls = FALSE;
-    ob->tls_tempfail_tryclear = FALSE;
+    sx->ob->tls_tempfail_tryclear = FALSE;
     smtp_command = US"SSL-on-connect";
     goto TLS_NEGOTIATE;
     }
 #endif
 
-  if (lflags.esmtp)
+  if (sx->esmtp)
     {
-    if (smtp_write_command(&outblock, FALSE, "%s %s\r\n",
-         lflags.lmtp ? "LHLO" : "EHLO", helo_data) < 0)
+    if (smtp_write_command(&sx->outblock, FALSE, "%s %s\r\n",
+         sx->lmtp ? "LHLO" : "EHLO", sx->helo_data) < 0)
       goto SEND_FAILED;
-    lflags.esmtp_sent = TRUE;
-    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
-           ob->command_timeout))
+    sx->esmtp_sent = TRUE;
+    if (!smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer), '2',
+           sx->ob->command_timeout))
       {
-      if (errno != 0 || buffer[0] == 0 || lflags.lmtp)
+      if (errno != 0 || sx->buffer[0] == 0 || sx->lmtp)
        {
 #ifdef EXPERIMENTAL_DSN_INFO
-       helo_response = string_copy(buffer);
+       sx->helo_response = string_copy(sx->buffer);
 #endif
        goto RESPONSE_FAILED;
        }
-      lflags.esmtp = FALSE;
+      sx->esmtp = FALSE;
       }
 #ifdef EXPERIMENTAL_DSN_INFO
-    helo_response = string_copy(buffer);
+    sx->helo_response = string_copy(sx->buffer);
 #endif
     }
   else
     DEBUG(D_transport)
       debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
 
-  if (!lflags.esmtp)
+  if (!sx->esmtp)
     {
     BOOL good_response;
-    int n = sizeof(buffer);
-    uschar * rsp = buffer;
+    int n = sizeof(sx->buffer);
+    uschar * rsp = sx->buffer;
 
-    if (lflags.esmtp_sent && (n = Ustrlen(buffer)) < sizeof(buffer)/2)
-      { rsp = buffer + n + 1; n = sizeof(buffer) - n; }
+    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(&outblock, FALSE, "HELO %s\r\n", helo_data) < 0)
+    if (smtp_write_command(&sx->outblock, FALSE, "HELO %s\r\n", sx->helo_data) < 0)
       goto SEND_FAILED;
-    good_response = smtp_read_response(&inblock, rsp, n,
-      '2', ob->command_timeout);
+    good_response = smtp_read_response(&sx->inblock, rsp, n,
+      '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
-    helo_response = string_copy(rsp);
+    sx->helo_response = string_copy(rsp);
 #endif
     if (!good_response)
       {
       /* Handle special logging for a closed connection after HELO
       when had previously sent EHLO */
 
-      if (rsp != buffer && rsp[0] == 0 && (errno == 0 || errno == ECONNRESET))
+      if (rsp != sx->buffer && rsp[0] == 0 && (errno == 0 || errno == ECONNRESET))
        {
-       message = NULL;
-       lflags.send_quit = FALSE;
+       sx->send_quit = FALSE;
        save_errno = ERRNO_SMTPCLOSED;
        message = string_sprintf("Remote host closed connection "
              "in response to %s (EHLO response was: %s)",
-             smtp_command, buffer);
+             smtp_command, sx->buffer);
        goto FAILED;
        }
-      Ustrncpy(buffer, rsp, sizeof(buffer)/2);
+      Ustrncpy(sx->buffer, rsp, sizeof(sx->buffer)/2);
       goto RESPONSE_FAILED;
       }
     }
 
-  peer_offered = smtp_peer_options = 0;
+  sx->peer_offered = smtp_peer_options = 0;
 
-  if (lflags.esmtp || lflags.lmtp)
+  if (sx->esmtp || sx->lmtp)
     {
-    peer_offered = ehlo_response(buffer, Ustrlen(buffer),
+    sx->peer_offered = ehlo_response(sx->buffer, Ustrlen(sx->buffer),
       PEER_OFFERED_TLS /* others checked later */
       );
 
   /* Set tls_offered if the response to EHLO specifies support for STARTTLS. */
 
 #ifdef SUPPORT_TLS
-    smtp_peer_options |= peer_offered & PEER_OFFERED_TLS;
+    smtp_peer_options |= sx->peer_offered & PEER_OFFERED_TLS;
 #endif
     }
   }
@@ -1899,9 +1889,9 @@ separate - we could match up by host ip+port as a bodge. */
 
 else
   {
-  inblock.sock = outblock.sock = fileno(stdin);
+  sx->inblock.sock = sx->outblock.sock = fileno(stdin);
   smtp_command = big_buffer;
-  host->port = port;    /* Record the port that was used */
+  sx->host->port = sx->port;    /* Record the port that was used */
   }
 
 /* If TLS is available on this connection, whether continued or not, attempt to
@@ -1915,10 +1905,10 @@ for error analysis. */
 #ifdef SUPPORT_TLS
 if (  smtp_peer_options & PEER_OFFERED_TLS
    && !suppress_tls
-   && verify_check_given_host(&ob->hosts_avoid_tls, host) != OK)
+   && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK)
   {
   uschar buffer2[4096];
-  if (smtp_write_command(&outblock, FALSE, "STARTTLS\r\n") < 0)
+  if (smtp_write_command(&sx->outblock, FALSE, "STARTTLS\r\n") < 0)
     goto SEND_FAILED;
 
   /* If there is an I/O error, transmission of this message is deferred. If
@@ -1928,15 +1918,15 @@ if (  smtp_peer_options & PEER_OFFERED_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(&inblock, buffer2, sizeof(buffer2), '2',
-      ob->command_timeout))
+  if (!smtp_read_response(&sx->inblock, buffer2, sizeof(buffer2), '2',
+      sx->ob->command_timeout))
     {
     if (  errno != 0
        || buffer2[0] == 0
-       || (buffer2[0] == '4' && !ob->tls_tempfail_tryclear)
+       || (buffer2[0] == '4' && !sx->ob->tls_tempfail_tryclear)
        )
       {
-      Ustrncpy(buffer, buffer2, sizeof(buffer));
+      Ustrncpy(sx->buffer, buffer2, sizeof(sx->buffer));
       goto RESPONSE_FAILED;
       }
     }
@@ -1946,9 +1936,10 @@ if (  smtp_peer_options & PEER_OFFERED_TLS
   else
   TLS_NEGOTIATE:
     {
-    int rc = tls_client_start(inblock.sock, host, addrlist, tblock
+    address_item * addr;
+    int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock
 # ifdef EXPERIMENTAL_DANE
-                            , lflags.dane ? &tlsa_dnsa : NULL
+                            , sx->dane ? &tlsa_dnsa : NULL
 # endif
                             );
 
@@ -1959,21 +1950,21 @@ if (  smtp_peer_options & PEER_OFFERED_TLS
     if (rc != OK)
       {
 # ifdef EXPERIMENTAL_DANE
-      if (lflags.dane) log_write(0, LOG_MAIN,
+      if (sx->dane) log_write(0, LOG_MAIN,
          "DANE attempt failed; no TLS connection to %s [%s]",
-         host->name, host->address);
+         sx->host->name, sx->host->address);
 # endif
 
       save_errno = ERRNO_TLSFAILURE;
       message = US"failure while setting up TLS session";
-      lflags.send_quit = FALSE;
+      sx->send_quit = FALSE;
       goto TLS_FAILED;
       }
 
     /* TLS session is set up */
 
     smtp_peer_options_wrap = smtp_peer_options;
-    for (addr = addrlist; addr; addr = addr->next)
+    for (addr = sx->addrlist; addr; addr = addr->next)
       if (addr->transport_return == PENDING_DEFER)
         {
         addr->cipher = tls_out.cipher;
@@ -2000,31 +1991,27 @@ if (tls_out.active >= 0)
   char *greeting_cmd;
   BOOL good_response;
 
-  if (helo_data == NULL)
+  if (!sx->helo_data && !(sx->helo_data = expand_string(sx->ob->helo_data)))
     {
-    helo_data = expand_string(ob->helo_data);
-    if (helo_data == NULL)
-      {
-      uschar *message = string_sprintf("failed to expand helo_data: %s",
-        expand_string_message);
-      set_errno_nohost(addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
-      yield = DEFER;
-      goto SEND_QUIT;
-      }
+    uschar *message = string_sprintf("failed to expand helo_data: %s",
+      expand_string_message);
+    set_errno_nohost(sx->addrlist, ERRNO_EXPANDFAIL, message, DEFER, FALSE);
+    yield = DEFER;
+    goto SEND_QUIT;
     }
 
   /* For SMTPS we need to wait for the initial OK response. */
-  if (lflags.smtps)
+  if (sx->smtps)
     {
-    good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
-      '2', ob->command_timeout);
+    good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+      '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
-    smtp_greeting = string_copy(buffer);
+    sx->smtp_greeting = string_copy(sx->buffer);
 #endif
     if (!good_response) goto RESPONSE_FAILED;
     }
 
-  if (lflags.esmtp)
+  if (sx->esmtp)
     greeting_cmd = "EHLO";
   else
     {
@@ -2033,13 +2020,13 @@ if (tls_out.active >= 0)
       debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
     }
 
-  if (smtp_write_command(&outblock, FALSE, "%s %s\r\n",
-        lflags.lmtp ? "LHLO" : greeting_cmd, helo_data) < 0)
+  if (smtp_write_command(&sx->outblock, FALSE, "%s %s\r\n",
+        sx->lmtp ? "LHLO" : greeting_cmd, sx->helo_data) < 0)
     goto SEND_FAILED;
-  good_response = smtp_read_response(&inblock, buffer, sizeof(buffer),
-    '2', ob->command_timeout);
+  good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+    '2', sx->ob->command_timeout);
 #ifdef EXPERIMENTAL_DSN_INFO
-  helo_response = string_copy(buffer);
+  sx->helo_response = string_copy(sx->buffer);
 #endif
   if (!good_response) goto RESPONSE_FAILED;
   smtp_peer_options = 0;
@@ -2048,11 +2035,11 @@ if (tls_out.active >= 0)
 /* If the host is required to use a secure channel, ensure that we
 have one. */
 
-else if (  lflags.smtps
+else if (  sx->smtps
 # ifdef EXPERIMENTAL_DANE
-       || lflags.dane
+       || sx->dane
 # endif
-        || verify_check_given_host(&ob->hosts_require_tls, host) == OK
+       || verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
        )
   {
   save_errno = ERRNO_TLSREQUIRED;
@@ -2074,72 +2061,72 @@ if (continue_hostname == NULL
 #endif
     )
   {
-  if (lflags.esmtp || lflags.lmtp)
+  if (sx->esmtp || sx->lmtp)
     {
-    peer_offered = ehlo_response(buffer, Ustrlen(buffer),
-      0 /* no TLS */
-      | (lflags.lmtp && ob->lmtp_ignore_quota ? PEER_OFFERED_IGNQ : 0)
-      | PEER_OFFERED_CHUNKING
-      | PEER_OFFERED_PRDR
+    sx->peer_offered = ehlo_response(sx->buffer, Ustrlen(sx->buffer),
+       0 /* no TLS */
+       | (sx->lmtp && sx->ob->lmtp_ignore_quota ? PEER_OFFERED_IGNQ : 0)
+       | PEER_OFFERED_CHUNKING
+       | 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 */
+       | (sx->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)
+       | PEER_OFFERED_DSN
+       | PEER_OFFERED_PIPE
+       | (sx->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 = peer_offered & PEER_OFFERED_IGNQ ? US" IGNOREQUOTA" : US"";
+    sx->igquotstr = sx->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_peer_options |= peer_offered & PEER_OFFERED_SIZE;
+    smtp_peer_options |= sx->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. */
 
-    if (  peer_offered & PEER_OFFERED_PIPE
-       && verify_check_given_host(&ob->hosts_avoid_pipelining, host) != OK)
+    if (  sx->peer_offered & PEER_OFFERED_PIPE
+       && verify_check_given_host(&sx->ob->hosts_avoid_pipelining, sx->host) != OK)
       smtp_peer_options |= PEER_OFFERED_PIPE;
 
     DEBUG(D_transport) debug_printf("%susing PIPELINING\n",
       smtp_peer_options & PEER_OFFERED_PIPE ? "" : "not ");
 
-    if (  peer_offered & PEER_OFFERED_CHUNKING
-       && verify_check_given_host(&ob->hosts_try_chunking, host) != OK)
-      peer_offered &= ~PEER_OFFERED_CHUNKING;
+    if (  sx->peer_offered & PEER_OFFERED_CHUNKING
+       && verify_check_given_host(&sx->ob->hosts_try_chunking, sx->host) != OK)
+      sx->peer_offered &= ~PEER_OFFERED_CHUNKING;
 
-    if (peer_offered & PEER_OFFERED_CHUNKING)
+    if (sx->peer_offered & PEER_OFFERED_CHUNKING)
       {DEBUG(D_transport) debug_printf("CHUNKING usable\n");}
 
 #ifndef DISABLE_PRDR
-    if (  peer_offered & PEER_OFFERED_PRDR
-       && verify_check_given_host(&ob->hosts_try_prdr, host) != OK)
-      peer_offered &= ~PEER_OFFERED_PRDR;
+    if (  sx->peer_offered & PEER_OFFERED_PRDR
+       && verify_check_given_host(&sx->ob->hosts_try_prdr, sx->host) != OK)
+      sx->peer_offered &= ~PEER_OFFERED_PRDR;
 
-    if (peer_offered & PEER_OFFERED_PRDR)
+    if (sx->peer_offered & PEER_OFFERED_PRDR)
       {DEBUG(D_transport) debug_printf("PRDR usable\n");}
 #endif
 
     /* Note if the server supports DSN */
-    smtp_peer_options |= peer_offered & PEER_OFFERED_DSN;
+    smtp_peer_options |= sx->peer_offered & PEER_OFFERED_DSN;
     DEBUG(D_transport) debug_printf("%susing DSN\n",
-                       peer_offered & PEER_OFFERED_DSN ? "" : "not ");
+                       sx->peer_offered & PEER_OFFERED_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
     the business. The host name and address must be available when the
     authenticator's client driver is running. */
 
-    switch (yield = smtp_auth(buffer, sizeof(buffer), addrlist, host,
-                             ob, lflags.esmtp, &inblock, &outblock))
+    switch (yield = smtp_auth(sx->buffer, sizeof(sx->buffer), sx->addrlist, sx->host,
+                             sx->ob, sx->esmtp, &sx->inblock, &sx->outblock))
       {
       default:         goto SEND_QUIT;
       case OK:         break;
@@ -2153,26 +2140,208 @@ pipelining_active = !!(smtp_peer_options & PEER_OFFERED_PIPE);
 /* The setting up of the SMTP call is now complete. Any subsequent errors are
 message-specific. */
 
-lflags.setting_up = FALSE;
+sx->setting_up = FALSE;
 
 #ifdef SUPPORT_I18N
-if (addrlist->prop.utf8_msg)
+if (sx->addrlist->prop.utf8_msg)
   {
-  lflags.utf8_needed = !addrlist->prop.utf8_downcvt
-                   && !addrlist->prop.utf8_downcvt_maybe;
-  DEBUG(D_transport) if (!lflags.utf8_needed)
+  sx->utf8_needed = !sx->addrlist->prop.utf8_downcvt
+                   && !sx->addrlist->prop.utf8_downcvt_maybe;
+  DEBUG(D_transport) if (!sx->utf8_needed)
     debug_printf("utf8: %s downconvert\n",
-      addrlist->prop.utf8_downcvt ? "mandatory" : "optional");
+      sx->addrlist->prop.utf8_downcvt ? "mandatory" : "optional");
   }
 
 /* If this is an international message we need the host to speak SMTPUTF8 */
-if (lflags.utf8_needed && !(peer_offered & PEER_OFFERED_UTF8))
+if (sx->utf8_needed && !(sx->peer_offered & PEER_OFFERED_UTF8))
   {
   errno = ERRNO_UTF8_FWD;
   goto RESPONSE_FAILED;
   }
 #endif
 
+return OK;
+
+
+  {
+  int code;
+  uschar * set_message;
+
+  RESPONSE_FAILED:
+    {
+    save_errno = errno;
+    message = NULL;
+    sx->send_quit = check_response(sx->host, &save_errno, sx->addrlist->more_errno,
+      sx->buffer, &code, &message, &pass_message);
+    goto FAILED;
+    }
+
+  SEND_FAILED:
+    {
+    save_errno = errno;
+    code = '4';
+    message = US string_sprintf("send() to %s [%s] failed: %s",
+      sx->host->name, sx->host->address, strerror(save_errno));
+    sx->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
+  in message and save_errno, and setting_up will always be true. Treat as
+  a temporary error. */
+
+#ifdef SUPPORT_TLS
+  TLS_FAILED:
+  code = '4';
+#endif
+
+  /* The failure happened while setting up the call; see if the failure was
+  a 5xx response (this will either be on connection, or following HELO - a 5xx
+  after EHLO causes it to try HELO). If so, fail all addresses, as this host is
+  never going to accept them. For other errors during setting up (timeouts or
+  whatever), defer all addresses, and yield DEFER, so that the host is not
+  tried again for a while. */
+
+  FAILED:
+  sx->ok = FALSE;                /* For when reached by GOTO */
+  set_message = message;
+
+  yield = code == '5'
+#ifdef SUPPORT_I18N
+         || errno == ERRNO_UTF8_FWD
+#endif
+    ? FAIL : DEFER;
+
+  set_errno(sx->addrlist, save_errno, set_message, yield, pass_message, sx->host
+#ifdef EXPERIMENTAL_DSN_INFO
+           , sx->smtp_greeting, sx->helo_response
+#endif
+           );
+  }
+
+
+SEND_QUIT:
+
+if (sx->send_quit)
+  (void)smtp_write_command(&sx->outblock, FALSE, "QUIT\r\n");
+
+/*END_OFF:*/
+
+#ifdef SUPPORT_TLS
+tls_close(FALSE, TRUE);
+#endif
+
+/* Close the socket, and return the appropriate value, first setting
+works because the NULL setting is passed back to the calling process, and
+remote_max_parallel is forced to 1 when delivering over an existing connection,
+
+If all went well and continue_more is set, we shouldn't actually get here if
+there are further addresses, as the return above will be taken. However,
+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");
+if (sx->send_quit)
+  {
+  shutdown(sx->outblock.sock, SHUT_WR);
+  if (fcntl(sx->inblock.sock, F_SETFL, O_NONBLOCK) == 0)
+    for (rc = 16; read(sx->inblock.sock, sx->inbuffer, sizeof(sx->inbuffer)) > 0 && rc > 0;)
+      rc--;                            /* drain socket */
+  }
+(void)close(sx->inblock.sock);
+
+#ifndef DISABLE_EVENT
+(void) event_raise(sx->tblock->event_action, US"tcp:close", NULL);
+#endif
+
+continue_transport = NULL;
+continue_hostname = NULL;
+return yield;
+}
+
+
+/*************************************************
+*       Deliver address list to given host       *
+*************************************************/
+
+/* If continue_hostname is not null, we get here only when continuing to
+deliver down an existing channel. The channel was passed as the standard
+input. TLS is never active on a passed channel; the previous process always
+closes it down before passing the connection on.
+
+Otherwise, we have to make a connection to the remote host, and do the
+initial protocol exchange.
+
+When running as an MUA wrapper, if the sender or any recipient is rejected,
+temporarily or permanently, we force failure for all recipients.
+
+Arguments:
+  addrlist        chain of potential addresses to deliver; only those whose
+                  transport_return field is set to PENDING_DEFER are currently
+                  being processed; others should be skipped - they have either
+                  been delivered to an earlier host or IP address, or been
+                  failed by one of them.
+  host            host to deliver to
+  host_af         AF_INET or AF_INET6
+  port            default TCP/IP port to use, in host byte order
+  interface       interface to bind to, or NULL
+  tblock          transport instance block
+  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
+                    that is specific to the message.
+  suppress_tls    if TRUE, don't attempt a TLS connection - this is set for
+                    a second attempt after TLS initialization fails
+
+Returns:          OK    - the connection was made and the delivery attempted;
+                          the result for each address is in its data block.
+                  DEFER - the connection could not be made, or something failed
+                          while setting up the SMTP session, or there was a
+                          non-message-specific error, such as a timeout.
+                  ERROR - a filter command is specified for this transport,
+                          and there was a problem setting it up; OR helo_data
+                          or add_headers or authenticated_sender is specified
+                          for this transport, and the string failed to expand
+*/
+
+static int
+smtp_deliver(address_item *addrlist, host_item *host, int host_af, int port,
+  uschar *interface, transport_instance *tblock,
+  BOOL *message_defer, BOOL suppress_tls)
+{
+address_item *addr;
+address_item *sync_addr;
+address_item *first_addr = addrlist;
+int yield = OK;
+int address_count;
+int save_errno;
+int rc;
+time_t start_delivery_time = time(NULL);
+
+BOOL completed_address = FALSE;
+
+
+BOOL pass_message = FALSE;
+uschar *message = NULL;
+uschar new_message_id[MESSAGE_ID_LENGTH + 1];
+uschar *p;
+
+smtp_context sx;
+
+suppress_tls = suppress_tls;  /* stop compiler warning when no TLS support */
+
+sx.addrlist = addrlist;
+sx.host = host;
+sx.host_af = host_af,
+sx.port = port;
+sx.interface = interface;
+sx.tblock = tblock;
+
+if ((rc = smtp_setup_conn(&sx, message_defer, suppress_tls, FALSE)) != OK)
+  return rc;
+
 /* 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. */
 
@@ -2199,10 +2368,10 @@ if (tblock->filter_command != NULL)
   if (  transport_filter_argv
      && *transport_filter_argv
      && **transport_filter_argv
-     && peer_offered & PEER_OFFERED_CHUNKING
+     && sx.peer_offered & PEER_OFFERED_CHUNKING
      )
     {
-    peer_offered &= ~PEER_OFFERED_CHUNKING;
+    sx.peer_offered &= ~PEER_OFFERED_CHUNKING;
     DEBUG(D_transport) debug_printf("CHUNKING not usable due to transport filter\n");
     }
   }
@@ -2218,9 +2387,8 @@ transaction to handle. */
 
 SEND_MESSAGE:
 sync_addr = first_addr;
-address_count = 0;
-lflags.ok = FALSE;
-lflags.send_rset = TRUE;
+sx.ok = FALSE;
+sx.send_rset = TRUE;
 completed_address = FALSE;
 
 
@@ -2231,25 +2399,25 @@ in a message, so we can give an accurate value for the original message, but we
 need some additional to handle added headers. (Double "." characters don't get
 included in the count.) */
 
-p = buffer;
+p = sx.buffer;
 *p = 0;
 
-if (peer_offered & PEER_OFFERED_SIZE)
+if (sx.peer_offered & PEER_OFFERED_SIZE)
   {
-  sprintf(CS p, " SIZE=%d", message_size+message_linecount+ob->size_addition);
+  sprintf(CS p, " SIZE=%d", message_size+message_linecount+sx.ob->size_addition);
   while (*p) p++;
   }
 
 #ifndef DISABLE_PRDR
-lflags.prdr_active = FALSE;
-if (peer_offered & PEER_OFFERED_PRDR)
+sx.prdr_active = FALSE;
+if (sx.peer_offered & PEER_OFFERED_PRDR)
   for (addr = first_addr; addr; addr = addr->next)
     if (addr->transport_return == PENDING_DEFER)
       {
       for (addr = addr->next; addr; addr = addr->next)
         if (addr->transport_return == PENDING_DEFER)
          {                     /* at least two recipients to send */
-         lflags.prdr_active = TRUE;
+         sx.prdr_active = TRUE;
          sprintf(CS p, " PRDR"); p += 5;
          break;
          }
@@ -2260,25 +2428,27 @@ if (peer_offered & PEER_OFFERED_PRDR)
 #ifdef SUPPORT_I18N
 if (  addrlist->prop.utf8_msg
    && !addrlist->prop.utf8_downcvt
-   && peer_offered & PEER_OFFERED_UTF8
+   && sx.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 */
-for (lflags.dsn_all_lasthop = TRUE, addr = first_addr;
-     address_count < max_rcpt && addr != NULL;
-     addr = addr->next)
-  if ((addr->dsn_flags & rf_dsnlasthop) != 1)
+/* check if all addresses have lasthop flag; do not send RET and ENVID if so */
+for (sx.dsn_all_lasthop = TRUE, addr = first_addr, address_count = 0;
+     addr && address_count < sx.max_rcpt;
+     addr = addr->next) if (addr->transport_return == PENDING_DEFER)
+  {
+  address_count++;
+  if (!(addr->dsn_flags & rf_dsnlasthop))
     {
-    lflags.dsn_all_lasthop = FALSE;
+    sx.dsn_all_lasthop = FALSE;
     break;
     }
+  }
 
 /* Add any DSN flags to the mail command */
 
-if (peer_offered & PEER_OFFERED_DSN && !lflags.dsn_all_lasthop)
+if (sx.peer_offered & PEER_OFFERED_DSN && !sx.dsn_all_lasthop)
   {
   if (dsn_ret == dsn_ret_hdrs)
     { Ustrcpy(p, " RET=HDRS"); p += 9; }
@@ -2287,7 +2457,7 @@ if (peer_offered & PEER_OFFERED_DSN && !lflags.dsn_all_lasthop)
 
   if (dsn_envid)
     {
-    string_format(p, sizeof(buffer) - (p-buffer), " ENVID=%s", dsn_envid);
+    string_format(p, sizeof(sx.buffer) - (p-sx.buffer), " ENVID=%s", dsn_envid);
     while (*p) p++;
     }
   }
@@ -2299,7 +2469,7 @@ Other expansion failures are serious. An empty result is ignored, but there is
 otherwise no check - this feature is expected to be used with LMTP and other
 cases where non-standard addresses (e.g. without domains) might be required. */
 
-if (smtp_mail_auth_str(p, sizeof(buffer) - (p-buffer), addrlist, ob))
+if (smtp_mail_auth_str(p, sizeof(sx.buffer) - (p-sx.buffer), addrlist, sx.ob))
   {
   yield = ERROR;
   goto SEND_QUIT;
@@ -2311,7 +2481,7 @@ at any point, for when the buffer fills up, so we write it totally generally.
 When PIPELINING is off, each command written reports that it has flushed the
 buffer. */
 
-lflags.pending_MAIL = TRUE;     /* The block starts with MAIL */
+sx.pending_MAIL = TRUE;     /* The block starts with MAIL */
 
   {
   uschar * s = return_path;
@@ -2323,7 +2493,7 @@ lflags.pending_MAIL = TRUE;     /* The block starts with MAIL */
   the delivery log line. */
 
   if (  addrlist->prop.utf8_msg
-     && (addrlist->prop.utf8_downcvt || !(peer_offered & PEER_OFFERED_UTF8))
+     && (addrlist->prop.utf8_downcvt || !(sx.peer_offered & PEER_OFFERED_UTF8))
      )
     {
     if (s = string_address_utf8_to_alabel(return_path, &errstr), errstr)
@@ -2336,8 +2506,8 @@ lflags.pending_MAIL = TRUE;     /* The block starts with MAIL */
     }
 #endif
 
-  rc = smtp_write_command(&outblock, pipelining_active,
-         "MAIL FROM:<%s>%s\r\n", s, buffer);
+  rc = smtp_write_command(&sx.outblock, pipelining_active,
+         "MAIL FROM:<%s>%s\r\n", s, sx.buffer);
   }
 
 mail_command = string_copy(big_buffer);  /* Save for later error message */
@@ -2348,17 +2518,17 @@ switch(rc)
     goto SEND_FAILED;
 
   case +1:                /* Block was sent */
-    if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
-       ob->command_timeout))
+    if (!smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
+       sx.ob->command_timeout))
       {
-      if (errno == 0 && buffer[0] == '4')
+      if (errno == 0 && sx.buffer[0] == '4')
        {
        errno = ERRNO_MAIL4XX;
-       addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+       addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
        }
       goto RESPONSE_FAILED;
       }
-    lflags.pending_MAIL = FALSE;
+    sx.pending_MAIL = FALSE;
     break;
   }
 
@@ -2375,16 +2545,15 @@ problem, temporary or permanent. We know that all recipient addresses will have
 the PENDING_DEFER status, because only one attempt is ever made, and we know
 that max_rcpt will be large, so all addresses will be done at once. */
 
-for (addr = first_addr;
-     addr  &&  address_count < max_rcpt;
-     addr = addr->next)
-    if (addr->transport_return == PENDING_DEFER)
+for (addr = first_addr, address_count = 0;
+     addr  &&  address_count < sx.max_rcpt;
+     addr = addr->next) if (addr->transport_return == PENDING_DEFER)
   {
   int count;
   BOOL no_flush;
   uschar * rcpt_addr;
 
-  addr->dsn_aware = peer_offered & PEER_OFFERED_DSN
+  addr->dsn_aware = sx.peer_offered & PEER_OFFERED_DSN
     ? dsn_support_yes : dsn_support_no;
 
   address_count++;
@@ -2392,10 +2561,10 @@ for (addr = first_addr;
 
   /* Add any DSN flags to the rcpt command and add to the sent string */
 
-  p = buffer;
+  p = sx.buffer;
   *p = 0;
 
-  if (peer_offered & PEER_OFFERED_DSN && !(addr->dsn_flags & rf_dsnlasthop))
+  if (sx.peer_offered & PEER_OFFERED_DSN && !(addr->dsn_flags & rf_dsnlasthop))
     {
     if (addr->dsn_flags & rf_dsnflags)
       {
@@ -2415,7 +2584,7 @@ for (addr = first_addr;
 
     if (addr->dsn_orcpt)
       {
-      string_format(p, sizeof(buffer) - (p-buffer), " ORCPT=%s",
+      string_format(p, sizeof(sx.buffer) - (p-sx.buffer), " ORCPT=%s",
         addr->dsn_orcpt);
       while (*p) p++;
       }
@@ -2439,30 +2608,30 @@ for (addr = first_addr;
     }
 #endif
 
-  count = smtp_write_command(&outblock, no_flush, "RCPT TO:<%s>%s%s\r\n",
-    rcpt_addr, igquotstr, buffer);
+  count = smtp_write_command(&sx.outblock, no_flush, "RCPT TO:<%s>%s%s\r\n",
+    rcpt_addr, sx.igquotstr, sx.buffer);
 
   if (count < 0) goto SEND_FAILED;
   if (count > 0)
     {
     switch(sync_responses(first_addr, tblock->rcpt_include_affixes,
-             &sync_addr, host, count, ob->address_retry_include_sender,
-             lflags.pending_MAIL, 0, &inblock, ob->command_timeout, buffer,
-             sizeof(buffer)))
+             &sync_addr, host, count, sx.ob->address_retry_include_sender,
+             sx.pending_MAIL, 0, &sx.inblock, sx.ob->command_timeout, sx.buffer,
+             sizeof(sx.buffer)))
       {
-      case 3: lflags.ok = TRUE;            /* 2xx & 5xx => OK & progress made */
+      case 3: sx.ok = TRUE;            /* 2xx & 5xx => OK & progress made */
       case 2: completed_address = TRUE;    /* 5xx (only) => progress made */
       break;
 
-      case 1: lflags.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
-      if (!lflags.lmtp) completed_address = TRUE; /* can't tell about progress yet */
+      case 1: sx.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
+      if (!sx.lmtp) completed_address = TRUE; /* can't tell about progress yet */
       case 0:                              /* No 2xx or 5xx, but no probs */
       break;
 
       case -1: goto END_OFF;               /* Timeout on RCPT */
       default: goto RESPONSE_FAILED;       /* I/O error, or any MAIL error */
       }
-    lflags.pending_MAIL = FALSE;            /* Dealt with MAIL */
+    sx.pending_MAIL = FALSE;            /* Dealt with MAIL */
     }
   }      /* Loop for next address */
 
@@ -2479,7 +2648,7 @@ if (mua_wrapper)
       /*XXX could we find a better errno than 0 here? */
       set_errno_nohost(addrlist, 0, badaddr->message, FAIL,
        testflag(badaddr, af_pass_message));
-      lflags.ok = FALSE;
+      sx.ok = FALSE;
       break;
       }
   }
@@ -2492,22 +2661,22 @@ are pipelining. The responses are all handled by sync_responses().
 If using CHUNKING, do not send a BDAT until we know how big a chunk we want
 to send is. */
 
-if (  !(peer_offered & PEER_OFFERED_CHUNKING)
-   && (lflags.ok || (pipelining_active && !mua_wrapper)))
+if (  !(sx.peer_offered & PEER_OFFERED_CHUNKING)
+   && (sx.ok || (pipelining_active && !mua_wrapper)))
   {
-  int count = smtp_write_command(&outblock, FALSE, "DATA\r\n");
+  int count = smtp_write_command(&sx.outblock, FALSE, "DATA\r\n");
 
   if (count < 0) goto SEND_FAILED;
   switch(sync_responses(first_addr, tblock->rcpt_include_affixes, &sync_addr,
-           host, count, ob->address_retry_include_sender, lflags.pending_MAIL,
-           lflags.ok ? +1 : -1, &inblock, ob->command_timeout, buffer, sizeof(buffer)))
+           host, count, sx.ob->address_retry_include_sender, sx.pending_MAIL,
+           sx.ok ? +1 : -1, &sx.inblock, sx.ob->command_timeout, sx.buffer, sizeof(sx.buffer)))
     {
-    case 3: lflags.ok = TRUE;            /* 2xx & 5xx => OK & progress made */
+    case 3: sx.ok = TRUE;            /* 2xx & 5xx => OK & progress made */
     case 2: completed_address = TRUE;    /* 5xx (only) => progress made */
     break;
 
-    case 1: lflags.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
-    if (!lflags.lmtp) completed_address = TRUE; /* can't tell about progress yet */
+    case 1: sx.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
+    if (!sx.lmtp) completed_address = TRUE; /* can't tell about progress yet */
     case 0: break;                       /* No 2xx or 5xx, but no probs */
 
     case -1: goto END_OFF;               /* Timeout on RCPT */
@@ -2524,12 +2693,12 @@ for handling the SMTP dot-handling protocol, flagging to apply to headers as
 well as body. Set the appropriate timeout value to be used for each chunk.
 (Haven't been able to make it work using select() for writing yet.) */
 
-if (!(peer_offered & PEER_OFFERED_CHUNKING) && !lflags.ok)
+if (!(sx.peer_offered & PEER_OFFERED_CHUNKING) && !sx.ok)
   {
   /* Save the first address of the next batch. */
   first_addr = addr;
 
-  lflags.ok = TRUE;
+  sx.ok = TRUE;
   }
 else
   {
@@ -2550,22 +2719,22 @@ else
   of responses.  The callback needs a whole bunch of state so set up
   a transport-context structure to be passed around. */
 
-  if (peer_offered & PEER_OFFERED_CHUNKING)
+  if (sx.peer_offered & PEER_OFFERED_CHUNKING)
     {
     tctx.check_string = tctx.escape_string = NULL;
     tctx.options |= topt_use_bdat;
     tctx.chunk_cb = smtp_chunk_cmd_callback;
-    tctx.inblock = &inblock;
-    tctx.outblock = &outblock;
+    tctx.inblock = &sx.inblock;
+    tctx.outblock = &sx.outblock;
     tctx.host = host;
     tctx.first_addr = first_addr;
     tctx.sync_addr = &sync_addr;
-    tctx.pending_MAIL = lflags.pending_MAIL;
+    tctx.pending_MAIL = sx.pending_MAIL;
     tctx.pending_BDAT = FALSE;
-    tctx.good_RCPT = lflags.ok;
+    tctx.good_RCPT = sx.ok;
     tctx.completed_address = &completed_address;
     tctx.cmd_count = 0;
-    tctx.buffer = buffer;
+    tctx.buffer = sx.buffer;
     }
   else
     tctx.options |= topt_end_dot;
@@ -2576,22 +2745,22 @@ else
   /* Responses from CHUNKING commands go in buffer.  Otherwise,
   there has not been a response. */
 
-  buffer[0] = 0;
+  sx.buffer[0] = 0;
 
   sigalrm_seen = FALSE;
-  transport_write_timeout = ob->data_timeout;
+  transport_write_timeout = sx.ob->data_timeout;
   smtp_command = US"sending data block";   /* For error messages */
   DEBUG(D_transport|D_v)
-    if (peer_offered & PEER_OFFERED_CHUNKING)
+    if (sx.peer_offered & PEER_OFFERED_CHUNKING)
       debug_printf("         will write message using CHUNKING\n");
     else
       debug_printf("  SMTP>> writing message and terminating \".\"\n");
   transport_count = 0;
 
 #ifndef DISABLE_DKIM
-  lflags.ok = dkim_transport_write_message(inblock.sock, &tctx, &ob->dkim);
+  sx.ok = dkim_transport_write_message(sx.inblock.sock, &tctx, &sx.ob->dkim);
 #else
-  lflags.ok = transport_write_message(inblock.sock, &tctx, 0);
+  sx.ok = transport_write_message(sx.inblock.sock, &tctx, 0);
 #endif
 
   /* transport_write_message() uses write() because it is called from other
@@ -2605,7 +2774,7 @@ else
   or the failure of a transport filter or the expansion of added headers.
   Or, when CHUNKING, it can be a protocol-detected failure. */
 
-  if (!lflags.ok)
+  if (!sx.ok)
     goto RESPONSE_FAILED;
 
   /* We used to send the terminating "." explicitly here, but because of
@@ -2616,20 +2785,20 @@ else
 
   smtp_command = US"end of data";
 
-  if (peer_offered & PEER_OFFERED_CHUNKING && tctx.cmd_count > 1)
+  if (sx.peer_offered & PEER_OFFERED_CHUNKING && tctx.cmd_count > 1)
     {
     /* Reap any outstanding MAIL & RCPT commands, but not a DATA-go-ahead */
     switch(sync_responses(first_addr, tblock->rcpt_include_affixes, &sync_addr,
-            host, tctx.cmd_count-1, ob->address_retry_include_sender,
-            lflags.pending_MAIL, 0,
-            &inblock, ob->command_timeout, buffer, sizeof(buffer)))
+            host, tctx.cmd_count-1, sx.ob->address_retry_include_sender,
+            sx.pending_MAIL, 0,
+            &sx.inblock, sx.ob->command_timeout, sx.buffer, sizeof(sx.buffer)))
       {
-      case 3: lflags.ok = TRUE;            /* 2xx & 5xx => OK & progress made */
+      case 3: sx.ok = TRUE;            /* 2xx & 5xx => OK & progress made */
       case 2: completed_address = TRUE;    /* 5xx (only) => progress made */
       break;
 
-      case 1: lflags.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
-      if (!lflags.lmtp) completed_address = TRUE; /* can't tell about progress yet */
+      case 1: sx.ok = TRUE;            /* 2xx (only) => OK, but if LMTP, */
+      if (!sx.lmtp) completed_address = TRUE; /* can't tell about progress yet */
       case 0: break;                       /* No 2xx or 5xx, but no probs */
 
       case -1: goto END_OFF;               /* Timeout on RCPT */
@@ -2642,18 +2811,18 @@ else
    * followed by the individual responses, before going on with
    * the overall response.  If we don't get the warning then deal
    * with per non-PRDR. */
-  if(lflags.prdr_active)
+  if(sx.prdr_active)
     {
-    lflags.ok = smtp_read_response(&inblock, buffer, sizeof(buffer), '3',
-      ob->final_timeout);
-    if (!lflags.ok && errno == 0) switch(buffer[0])
+    sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '3',
+      sx.ob->final_timeout);
+    if (!sx.ok && errno == 0) switch(sx.buffer[0])
       {
-      case '2': lflags.prdr_active = FALSE;
-               lflags.ok = TRUE;
+      case '2': sx.prdr_active = FALSE;
+               sx.ok = TRUE;
                break;
       case '4': errno = ERRNO_DATA4XX;
                addrlist->more_errno |=
-                 ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+                 ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
                break;
       }
     }
@@ -2663,14 +2832,14 @@ else
   /* For non-PRDR SMTP, we now read a single response that applies to the
   whole message.  If it is OK, then all the addresses have been delivered. */
 
-  if (!lflags.lmtp)
+  if (!sx.lmtp)
     {
-    lflags.ok = smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
-      ob->final_timeout);
-    if (!lflags.ok && errno == 0 && buffer[0] == '4')
+    sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
+      sx.ob->final_timeout);
+    if (!sx.ok && errno == 0 && sx.buffer[0] == '4')
       {
       errno = ERRNO_DATA4XX;
-      addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+      addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
       }
     }
 
@@ -2686,14 +2855,14 @@ else
   software before the spool gets updated. Also record the final SMTP
   confirmation if needed (for SMTP only). */
 
-  if (lflags.ok)
+  if (sx.ok)
     {
     int flag = '=';
     int delivery_time = (int)(time(NULL) - start_delivery_time);
     int len;
     uschar *conf = NULL;
 
-    lflags.send_rset = FALSE;
+    sx.send_rset = FALSE;
     pipelining_active = FALSE;
 
     /* Set up confirmation if needed - applies only to SMTP */
@@ -2702,12 +2871,12 @@ else
 #ifdef DISABLE_EVENT
           LOGGING(smtp_confirmation) &&
 #endif
-          !lflags.lmtp
+          !sx.lmtp
        )
       {
-      const uschar *s = string_printing(buffer);
+      const uschar *s = string_printing(sx.buffer);
       /* deconst cast ok here as string_printing was checked to have alloc'n'copied */
-      conf = (s == buffer)? (uschar *)string_copy(s) : US s;
+      conf = (s == sx.buffer)? (uschar *)string_copy(s) : US s;
       }
 
     /* Process all transported addresses - for LMTP or PRDR, read a status for
@@ -2723,32 +2892,32 @@ else
       it doesn't get tried again too soon. */
 
 #ifndef DISABLE_PRDR
-      if (lflags.lmtp || lflags.prdr_active)
+      if (sx.lmtp || sx.prdr_active)
 #else
-      if (lflags.lmtp)
+      if (sx.lmtp)
 #endif
         {
-        if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
-            ob->final_timeout))
+        if (!smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
+            sx.ob->final_timeout))
           {
-          if (errno != 0 || buffer[0] == 0) goto RESPONSE_FAILED;
+          if (errno != 0 || sx.buffer[0] == 0) goto RESPONSE_FAILED;
           addr->message = string_sprintf(
 #ifndef DISABLE_PRDR
-           "%s error after %s: %s", lflags.prdr_active ? "PRDR":"LMTP",
+           "%s error after %s: %s", sx.prdr_active ? "PRDR":"LMTP",
 #else
            "LMTP error after %s: %s",
 #endif
-            data_command, string_printing(buffer));
+           data_command, string_printing(sx.buffer));
           setflag(addr, af_pass_message);   /* Allow message to go to user */
-          if (buffer[0] == '5')
+          if (sx.buffer[0] == '5')
             addr->transport_return = FAIL;
           else
             {
             errno = ERRNO_DATA4XX;
-            addr->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+            addr->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
             addr->transport_return = DEFER;
 #ifndef DISABLE_PRDR
-            if (!lflags.prdr_active)
+            if (!sx.prdr_active)
 #endif
               retry_add_item(addr, addr->address_retry_key, 0);
             }
@@ -2757,9 +2926,9 @@ else
         completed_address = TRUE;   /* NOW we can set this flag */
         if (LOGGING(smtp_confirmation))
           {
-          const uschar *s = string_printing(buffer);
+          const uschar *s = string_printing(sx.buffer);
          /* deconst cast ok here as string_printing was checked to have alloc'n'copied */
-          conf = (s == buffer)? (uschar *)string_copy(s) : US s;
+          conf = (s == sx.buffer) ? US string_copy(s) : US s;
           }
         }
 
@@ -2772,13 +2941,13 @@ else
       addr->special_action = flag;
       addr->message = conf;
 #ifndef DISABLE_PRDR
-      if (lflags.prdr_active) addr->flags |= af_prdr_used;
+      if (sx.prdr_active) addr->flags |= af_prdr_used;
 #endif
-      if (peer_offered & PEER_OFFERED_CHUNKING) addr->flags |= af_chunking_used;
+      if (sx.peer_offered & PEER_OFFERED_CHUNKING) addr->flags |= af_chunking_used;
       flag = '-';
 
 #ifndef DISABLE_PRDR
-      if (!lflags.prdr_active)
+      if (!sx.prdr_active)
 #endif
         {
         /* Update the journal. For homonymic addresses, use the base address plus
@@ -2787,34 +2956,34 @@ else
         write error, as it may prove possible to update the spool file later. */
 
         if (testflag(addr, af_homonym))
-          sprintf(CS buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
+          sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
         else
-          sprintf(CS buffer, "%.500s\n", addr->unique);
+          sprintf(CS sx.buffer, "%.500s\n", addr->unique);
 
-        DEBUG(D_deliver) debug_printf("journalling %s\n", buffer);
-        len = Ustrlen(CS buffer);
-        if (write(journal_fd, buffer, len) != len)
+        DEBUG(D_deliver) debug_printf("journalling %s\n", sx.buffer);
+        len = Ustrlen(CS sx.buffer);
+        if (write(journal_fd, sx.buffer, len) != len)
           log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
-            "%s: %s", buffer, strerror(errno));
+            "%s: %s", sx.buffer, strerror(errno));
         }
       }
 
 #ifndef DISABLE_PRDR
-      if (lflags.prdr_active)
+      if (sx.prdr_active)
         {
        /* PRDR - get the final, overall response.  For any non-success
        upgrade all the address statuses. */
-        lflags.ok = smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
-          ob->final_timeout);
-        if (!lflags.ok)
+        sx.ok = smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer), '2',
+          sx.ob->final_timeout);
+        if (!sx.ok)
          {
-         if(errno == 0 && buffer[0] == '4')
+         if(errno == 0 && sx.buffer[0] == '4')
             {
             errno = ERRNO_DATA4XX;
-            addrlist->more_errno |= ((buffer[1] - '0')*10 + buffer[2] - '0') << 8;
+            addrlist->more_errno |= ((sx.buffer[1] - '0')*10 + sx.buffer[2] - '0') << 8;
             }
          for (addr = addrlist; addr != first_addr; addr = addr->next)
-            if (buffer[0] == '5' || addr->transport_return == OK)
+            if (sx.buffer[0] == '5' || addr->transport_return == OK)
               addr->transport_return = PENDING_OK; /* allow set_errno action */
          goto RESPONSE_FAILED;
          }
@@ -2824,15 +2993,15 @@ else
          if (addr->transport_return == OK)
          {
           if (testflag(addr, af_homonym))
-            sprintf(CS buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
+            sprintf(CS sx.buffer, "%.500s/%s\n", addr->unique + 3, tblock->name);
           else
-            sprintf(CS buffer, "%.500s\n", addr->unique);
+            sprintf(CS sx.buffer, "%.500s\n", addr->unique);
 
-          DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", buffer);
-          len = Ustrlen(CS buffer);
-          if (write(journal_fd, buffer, len) != len)
+          DEBUG(D_deliver) debug_printf("journalling(PRDR) %s\n", sx.buffer);
+          len = Ustrlen(CS sx.buffer);
+          if (write(journal_fd, sx.buffer, len) != len)
             log_write(0, LOG_MAIN|LOG_PANIC, "failed to write journal for "
-              "%s: %s", buffer, strerror(errno));
+              "%s: %s", sx.buffer, strerror(errno));
          }
        else if (addr->transport_return == DEFER)
           retry_add_item(addr, addr->address_retry_key, -2);
@@ -2856,7 +3025,7 @@ assumed if errno == 0 and there is no text in the buffer. If control reaches
 here during the setting up phase (i.e. before MAIL FROM) then always defer, as
 the problem is not related to this specific message. */
 
-if (!lflags.ok)
+if (!sx.ok)
   {
   int code, set_rc;
   uschar * set_message;
@@ -2865,8 +3034,8 @@ if (!lflags.ok)
     {
     save_errno = errno;
     message = NULL;
-    lflags.send_quit = check_response(host, &save_errno, addrlist->more_errno,
-      buffer, &code, &message, &pass_message);
+    sx.send_quit = check_response(host, &save_errno, addrlist->more_errno,
+      sx.buffer, &code, &message, &pass_message);
     goto FAILED;
     }
 
@@ -2876,7 +3045,7 @@ if (!lflags.ok)
     code = '4';
     message = US string_sprintf("send() to %s [%s] failed: %s",
       host->name, host->address, strerror(save_errno));
-    lflags.send_quit = FALSE;
+    sx.send_quit = FALSE;
     goto FAILED;
     }
 
@@ -2898,10 +3067,10 @@ if (!lflags.ok)
   tried again for a while. */
 
   FAILED:
-  lflags.ok = FALSE;                /* For when reached by GOTO */
+  sx.ok = FALSE;                /* For when reached by GOTO */
   set_message = message;
 
-  if (lflags.setting_up)
+  if (sx.setting_up)
     if (code == '5')
       set_rc = FAIL;
     else
@@ -2996,7 +3165,7 @@ if (!lflags.ok)
 
   set_errno(addrlist, save_errno, set_message, set_rc, pass_message, host
 #ifdef EXPERIMENTAL_DSN_INFO
-           , smtp_greeting, helo_response
+           , sx.smtp_greeting, sx.helo_response
 #endif
            );
   }
@@ -3032,10 +3201,10 @@ 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", lflags.ok, lflags.send_quit,
-    lflags.send_rset, continue_more, yield, first_addr ? "not " : "");
+    "yield=%d first_address is %sNULL\n", sx.ok, sx.send_quit,
+    sx.send_rset, continue_more, yield, first_addr ? "not " : "");
 
-if (completed_address && lflags.ok && lflags.send_quit)
+if (completed_address && sx.ok && sx.send_quit)
   {
   BOOL more;
   smtp_compare_t t_compare;
@@ -3046,7 +3215,7 @@ if (completed_address && lflags.ok && lflags.send_quit)
   if (  first_addr != NULL
      || continue_more
      || (  (  tls_out.active < 0
-           || verify_check_given_host(&ob->hosts_nopass_tls, host) != OK
+           || verify_check_given_host(&sx.ob->hosts_nopass_tls, host) != OK
           )
         &&
            transport_check_waiting(tblock->name, host->name,
@@ -3057,31 +3226,29 @@ if (completed_address && lflags.ok && lflags.send_quit)
     uschar *msg;
     BOOL pass_message;
 
-    if (lflags.send_rset)
-      {
-      if (! (lflags.ok = smtp_write_command(&outblock, FALSE, "RSET\r\n") >= 0))
+    if (sx.send_rset)
+      if (! (sx.ok = smtp_write_command(&sx.outblock, FALSE, "RSET\r\n") >= 0))
         {
         msg = US string_sprintf("send() to %s [%s] failed: %s", host->name,
           host->address, strerror(save_errno));
-        lflags.send_quit = FALSE;
+        sx.send_quit = FALSE;
         }
-      else if (! (lflags.ok = smtp_read_response(&inblock, buffer,
-                 sizeof(buffer), '2', ob->command_timeout)))
+      else if (! (sx.ok = smtp_read_response(&sx.inblock, sx.buffer,
+                 sizeof(sx.buffer), '2', sx.ob->command_timeout)))
         {
         int code;
-        lflags.send_quit = check_response(host, &errno, 0, buffer, &code, &msg,
+        sx.send_quit = check_response(host, &errno, 0, sx.buffer, &code, &msg,
           &pass_message);
-        if (!lflags.send_quit)
+        if (!sx.send_quit)
           {
           DEBUG(D_transport) debug_printf("H=%s [%s] %s\n",
            host->name, host->address, msg);
           }
         }
-      }
 
     /* Either RSET was not needed, or it succeeded */
 
-    if (lflags.ok)
+    if (sx.ok)
       {
       if (first_addr != NULL)            /* More addresses still to be sent */
         {                                /*   in this run of the transport */
@@ -3100,12 +3267,11 @@ if (completed_address && lflags.ok && lflags.send_quit)
         {
         tls_close(FALSE, TRUE);
        smtp_peer_options = smtp_peer_options_wrap;
-        if (lflags.smtps)
-          lflags.ok = FALSE;
-        else
-          lflags.ok = smtp_write_command(&outblock,FALSE,"EHLO %s\r\n",helo_data) >= 0 &&
-               smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
-                 ob->command_timeout);
+       sx.ok = !sx.smtps
+          && smtp_write_command(&sx.outblock, FALSE,
+                                   "EHLO %s\r\n", sx.helo_data) >= 0
+         && smtp_read_response(&sx.inblock, sx.buffer, sizeof(sx.buffer),
+                                   '2', sx.ob->command_timeout);
         }
 #endif
 
@@ -3115,16 +3281,16 @@ if (completed_address && lflags.ok && lflags.send_quit)
 /*XXX DSN_INFO: assume likely to do new HELO; but for greet we'll want to
 propagate it from the initial
 */
-      if (lflags.ok && transport_pass_socket(tblock->name, host->name,
-           host->address, new_message_id, inblock.sock))
-        lflags.send_quit = FALSE;
+      if (sx.ok && transport_pass_socket(tblock->name, host->name,
+           host->address, new_message_id, sx.inblock.sock))
+        sx.send_quit = FALSE;
       }
 
     /* If RSET failed and there are addresses left, they get deferred. */
 
     else set_errno(first_addr, errno, msg, DEFER, FALSE, host
 #ifdef EXPERIMENTAL_DSN_INFO
-                 , smtp_greeting, helo_response
+                 , sx.smtp_greeting, sx.helo_response
 #endif
                  );
     }
@@ -3149,7 +3315,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 (lflags.send_quit) (void)smtp_write_command(&outblock, FALSE, "QUIT\r\n");
+if (sx.send_quit) (void)smtp_write_command(&sx.outblock, FALSE, "QUIT\r\n");
 
 END_OFF:
 
@@ -3168,14 +3334,14 @@ 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");
-if (lflags.send_quit)
+if (sx.send_quit)
   {
-  shutdown(outblock.sock, SHUT_WR);
-  if (fcntl(inblock.sock, F_SETFL, O_NONBLOCK) == 0)
-    for (rc = 16; read(inblock.sock, inbuffer, sizeof(inbuffer)) > 0 && rc > 0;)
+  shutdown(sx.outblock.sock, SHUT_WR);
+  if (fcntl(sx.inblock.sock, F_SETFL, O_NONBLOCK) == 0)
+    for (rc = 16; read(sx.inblock.sock, sx.inbuffer, sizeof(sx.inbuffer)) > 0 && rc > 0;)
       rc--;                            /* drain socket */
   }
-(void)close(inblock.sock);
+(void)close(sx.inblock.sock);
 
 #ifndef DISABLE_EVENT
 (void) event_raise(tblock->event_action, US"tcp:close", NULL);
index cbf33c6..74acec3 100644 (file)
@@ -14,7 +14,7 @@
 1999-03-02 09:44:33 End queue run: pid=pppp -qf
 1999-03-02 09:44:33 Test: reject connect
 1999-03-02 09:44:33 Start queue run: pid=pppp -qf
-1999-03-02 09:44:33 10HmaX-0005vi-00 ** userx@domain1 F=<CALLER@test.ex> R=others T=smtp H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after initial connection: 550 Go away
+1999-03-02 09:44:33 10HmaX-0005vi-00 ** userx@domain1 F=<CALLER@test.ex> R=others T=smtp H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after initial connection: 550 Go away (A)
 1999-03-02 09:44:33 10HmaY-0005vi-00 <= <> R=10HmaX-0005vi-00 U=EXIMUSER P=local S=sss
 1999-03-02 09:44:33 10HmaY-0005vi-00 => CALLER <CALLER@test.ex> F=<> R=all T=local_delivery
 1999-03-02 09:44:33 10HmaY-0005vi-00 Completed
@@ -22,8 +22,8 @@
 1999-03-02 09:44:33 End queue run: pid=pppp -qf
 1999-03-02 09:44:33 Test: reject helo
 1999-03-02 09:44:33 10HmaZ-0005vi-00 <= CALLER@test.ex U=CALLER P=local S=sss
-1999-03-02 09:44:33 10HmaZ-0005vi-00 ** userx@domain1 F=<CALLER@test.ex> R=others T=smtp H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after HELO the.local.host.name: 550 Go away
-1999-03-02 09:44:33 10HmaZ-0005vi-00 ** usery@domain2 F=<CALLER@test.ex> R=others T=smtp H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after HELO the.local.host.name: 550 Go away
+1999-03-02 09:44:33 10HmaZ-0005vi-00 ** userx@domain1 F=<CALLER@test.ex> R=others T=smtp H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after HELO the.local.host.name: 550 Go away (C)
+1999-03-02 09:44:33 10HmaZ-0005vi-00 ** usery@domain2 F=<CALLER@test.ex> R=others T=smtp H=localhost4.test.ex [127.0.0.1]: SMTP error from remote mail server after HELO the.local.host.name: 550 Go away (C)
 1999-03-02 09:44:33 10HmbA-0005vi-00 <= <> R=10HmaZ-0005vi-00 U=EXIMUSER P=local S=sss
 1999-03-02 09:44:33 10HmbA-0005vi-00 => CALLER <CALLER@test.ex> F=<> R=all T=local_delivery
 1999-03-02 09:44:33 10HmbA-0005vi-00 Completed
index bf77aff..12511cd 100644 (file)
@@ -24,7 +24,7 @@ recipients. This is a permanent error. The following address(es) failed:
   userx@domain1
   host localhost4.test.ex [127.0.0.1]
     SMTP error from remote mail server after initial connection:
-    550 Go away
+    550 Go away (A)
 
 --NNNNNNNNNN-eximdsn-MMMMMMMMMM
 Content-type: message/delivery-status
@@ -35,7 +35,7 @@ Action: failed
 Final-Recipient: rfc822;userx@domain1
 Status: 5.0.0
 Remote-MTA: dns; localhost4.test.ex
-Diagnostic-Code: smtp; 550 Go away
+Diagnostic-Code: smtp; 550 Go away (A)
 
 --NNNNNNNNNN-eximdsn-MMMMMMMMMM
 Content-type: message/rfc822
@@ -80,11 +80,11 @@ recipients. This is a permanent error. The following address(es) failed:
   usery@domain2
   host localhost4.test.ex [127.0.0.1]
     SMTP error from remote mail server after HELO the.local.host.name:
-    550 Go away
+    550 Go away (C)
   userx@domain1
   host localhost4.test.ex [127.0.0.1]
     SMTP error from remote mail server after HELO the.local.host.name:
-    550 Go away
+    550 Go away (C)
 
 --NNNNNNNNNN-eximdsn-MMMMMMMMMM
 Content-type: message/delivery-status
@@ -95,13 +95,13 @@ Action: failed
 Final-Recipient: rfc822;userx@domain1
 Status: 5.0.0
 Remote-MTA: dns; localhost4.test.ex
-Diagnostic-Code: smtp; 550 Go away
+Diagnostic-Code: smtp; 550 Go away (C)
 
 Action: failed
 Final-Recipient: rfc822;usery@domain2
 Status: 5.0.0
 Remote-MTA: dns; localhost4.test.ex
-Diagnostic-Code: smtp; 550 Go away
+Diagnostic-Code: smtp; 550 Go away (C)
 
 --NNNNNNNNNN-eximdsn-MMMMMMMMMM
 Content-type: message/rfc822
index dbe41c7..655b4dd 100644 (file)
@@ -38,7 +38,7 @@ exim -qf
 exim -z "Test: reject connect"
 ****
 server PORT_S
-550 Go away
+550 Go away (A)
 QUIT
 250 OK
 ****
@@ -50,9 +50,9 @@ exim -z "Test: reject helo"
 server PORT_S
 220 Connected OK
 EHLO
-550 Go away
+550 Go away (B)
 HELO
-550 Go away
+550 Go away (C)
 QUIT
 250 OK
 ****
index 93e271c..2d484bd 100644 (file)
@@ -22,7 +22,7 @@ Connection request from [127.0.0.1]
 End of script
 Listening on port 1224 ... 
 Connection request from [127.0.0.1]
-550 Go away
+550 Go away (A)
 QUIT
 250 OK
 End of script
@@ -30,9 +30,9 @@ Listening on port 1224 ...
 Connection request from [127.0.0.1]
 220 Connected OK
 EHLO the.local.host.name
-550 Go away
+550 Go away (B)
 HELO the.local.host.name
-550 Go away
+550 Go away (C)
 QUIT
 250 OK
 End of script