+ set_errno_nohost(sx->addrlist,
+ errno == ETIMEDOUT ? ERRNO_CONNECTTIMEOUT : errno,
+ sx->verify ? string_sprintf("could not connect: %s", msg)
+ : NULL,
+ DEFER, FALSE);
+ sx->send_quit = FALSE;
+ return DEFER;
+ }
+
+#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_DANE)
+ {
+ tls_out.dane_verified = FALSE;
+ tls_out.tlsa_usage = 0;
+
+ if (sx->host->dnssec == DS_YES)
+ {
+ if( sx->dane_required
+ || verify_check_given_host(&sx->ob->hosts_try_dane, sx->host) == OK
+ )
+ switch (rc = tlsa_lookup(sx->host, &tlsa_dnsa, sx->dane_required))
+ {
+ case OK: sx->dane = TRUE; break;
+ case FAIL_FORCED: break;
+ 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 (sx->dane_required)
+ {
+ set_errno_nohost(sx->addrlist, ERRNO_DNSDEFER,
+ string_sprintf("DANE error: %s lookup not DNSSEC", sx->host->name),
+ FAIL, FALSE);
+ return FAIL;
+ }
+
+ if (sx->dane)
+ sx->ob->tls_tempfail_tryclear = FALSE;
+ }
+#endif /*DANE*/
+
+ /* Expand the greeting message while waiting for the initial response. (Makes
+ sense if helo_data contains ${lookup dnsdb ...} stuff). The expansion is
+ delayed till here so that $sending_interface and $sending_port are set. */
+
+ if (sx->helo_data)
+ if (!(sx->helo_data = expand_string(sx->helo_data)))
+ if (sx->verify)
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "<%s>: failed to expand transport's helo_data value for callout: %s",
+ sx->addrlist->address, expand_string_message);
+
+#ifdef SUPPORT_I18N
+ if (sx->helo_data)
+ {
+ expand_string_message = NULL;
+ if ((sx->helo_data = string_domain_utf8_to_alabel(sx->helo_data,
+ &expand_string_message)),
+ expand_string_message)
+ if (sx->verify)
+ log_write(0, LOG_MAIN|LOG_PANIC,
+ "<%s>: failed to expand transport's helo_data value for callout: %s",
+ sx->addrlist->address, expand_string_message);
+ else
+ sx->helo_data = NULL;
+ }
+#endif
+
+ /* The first thing is to wait for an initial OK response. The dreaded "goto"
+ is nevertheless a reasonably clean way of programming this kind of logic,
+ where you want to escape on any error. */
+
+ if (!sx->smtps)
+ {
+ BOOL good_response;
+
+#ifdef TCP_QUICKACK
+ (void) setsockopt(sx->inblock.sock, IPPROTO_TCP, TCP_QUICKACK, US &off, sizeof(off));
+#endif
+ good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+ '2', sx->ob->command_timeout);
+#ifdef EXPERIMENTAL_DSN_INFO
+ sx->smtp_greeting = string_copy(sx->buffer);
+#endif
+ if (!good_response) goto RESPONSE_FAILED;
+
+#ifndef DISABLE_EVENT
+ {
+ uschar * s;
+ 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(sx->addrlist, ERRNO_EXPANDFAIL,
+ string_sprintf("deferred by smtp:connect event expansion: %s", s),
+ DEFER, FALSE);
+ yield = DEFER;
+ goto SEND_QUIT;
+ }
+ }
+#endif
+
+ /* Now check if the helo_data expansion went well, and sign off cleanly if
+ it didn't. */
+
+ if (!sx->helo_data)
+ {
+ 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;
+ }
+ }
+
+/** Debugging without sending a message
+sx->addrlist->transport_return = DEFER;
+goto SEND_QUIT;
+**/
+
+ /* Errors that occur after this point follow an SMTP command, which is
+ left in big_buffer by smtp_write_command() for use in error messages. */
+
+ smtp_command = big_buffer;
+
+ /* Tell the remote who we are...
+
+ February 1998: A convention has evolved that ESMTP-speaking MTAs include the
+ string "ESMTP" in their greeting lines, so make Exim send EHLO if the
+ greeting is of this form. The assumption was that the far end supports it
+ properly... but experience shows that there are some that give 5xx responses,
+ even though the banner includes "ESMTP" (there's a bloody-minded one that
+ says "ESMTP not spoken here"). Cope with that case.
+
+ September 2000: Time has passed, and it seems reasonable now to always send
+ EHLO at the start. It is also convenient to make the change while installing
+ the TLS stuff.
+
+ July 2003: Joachim Wieland met a broken server that advertises "PIPELINING"
+ but times out after sending MAIL FROM, RCPT TO and DATA all together. There
+ would be no way to send out the mails, so there is now a host list
+ "hosts_avoid_esmtp" that disables ESMTP for special hosts and solves the
+ PIPELINING problem as well. Maybe it can also be useful to cure other
+ problems with broken servers.
+
+ Exim originally sent "Helo" at this point and ran for nearly a year that way.
+ Then somebody tried it with a Microsoft mailer... It seems that all other
+ 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;
+
+ /* 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 (sx->smtps)
+ {
+ smtp_peer_options |= PEER_OFFERED_TLS;
+ suppress_tls = FALSE;
+ sx->ob->tls_tempfail_tryclear = FALSE;
+ smtp_command = US"SSL-on-connect";
+ goto TLS_NEGOTIATE;
+ }
+#endif
+
+ if (sx->esmtp)
+ {
+ if (smtp_write_command(&sx->outblock, FALSE, "%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',
+ sx->ob->command_timeout))
+ {
+ if (errno != 0 || sx->buffer[0] == 0 || sx->lmtp)
+ {
+#ifdef EXPERIMENTAL_DSN_INFO
+ sx->helo_response = string_copy(sx->buffer);
+#endif
+ goto RESPONSE_FAILED;
+ }
+ sx->esmtp = FALSE;
+ }
+#ifdef EXPERIMENTAL_DSN_INFO
+ sx->helo_response = string_copy(sx->buffer);
+#endif
+ }
+ else
+ DEBUG(D_transport)
+ debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
+
+ if (!sx->esmtp)
+ {
+ BOOL good_response;
+ int n = sizeof(sx->buffer);
+ uschar * rsp = sx->buffer;
+
+ 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, FALSE, "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);
+#ifdef EXPERIMENTAL_DSN_INFO
+ 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 != sx->buffer && rsp[0] == 0 && (errno == 0 || errno == ECONNRESET))
+ {
+ errno = ERRNO_SMTPCLOSED;
+ goto EHLOHELO_FAILED;
+ }
+ Ustrncpy(sx->buffer, rsp, sizeof(sx->buffer)/2);
+ goto RESPONSE_FAILED;
+ }
+ }
+
+ sx->peer_offered = smtp_peer_options = 0;
+
+ if (sx->esmtp || sx->lmtp)
+ {
+ sx->peer_offered = ehlo_response(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 |= sx->peer_offered & PEER_OFFERED_TLS;
+#endif
+ }
+ }
+
+/* For continuing deliveries down the same channel, having re-exec'd the socket
+is the standard input; for a socket held open from verify it is recorded
+in the cutthrough context block. Either way we don't need to redo EHLO here
+(but may need to do so for TLS - see below).
+Set up the pointer to where subsequent commands will be left, for
+error messages. Note that smtp_peer_options will have been
+set from the command line if they were set in the process that passed the
+connection on. */
+
+/*XXX continue case needs to propagate DSN_INFO, prob. in deliver.c
+as the continue goes via transport_pass_socket() and doublefork and exec.
+It does not wait. Unclear how we keep separate host's responses
+separate - we could match up by host ip+port as a bodge. */
+
+else
+ {
+ if (cutthrough.fd >= 0 && cutthrough.callout_hold_only)
+ {
+ sx->inblock.sock = sx->outblock.sock = cutthrough.fd;
+ sx->host->port = sx->port = cutthrough.host.port;
+ }
+ else
+ {
+ sx->inblock.sock = sx->outblock.sock = 0; /* stdin */
+ sx->host->port = sx->port; /* Record the port that was used */
+ }
+ smtp_command = big_buffer;
+ sx->helo_data = NULL; /* ensure we re-expand ob->helo_data */
+
+ /* For a continued connection with TLS being proxied for us, or a
+ held-open verify connection with TLS, nothing more to do. */
+
+ if ( continue_proxy_cipher
+ || (cutthrough.fd >= 0 && cutthrough.callout_hold_only && cutthrough.is_tls)
+ )
+ {
+ sx->peer_offered = smtp_peer_options;
+ pipelining_active = !!(smtp_peer_options & PEER_OFFERED_PIPE);
+ HDEBUG(D_transport) debug_printf("continued connection, %s TLS\n",
+ continue_proxy_cipher ? "proxied" : "verify conn with");
+ return OK;
+ }
+ HDEBUG(D_transport) debug_printf("continued connection, no TLS\n");
+ }
+
+/* If TLS is available on this connection, whether continued or not, attempt to
+start up a TLS session, unless the host is in hosts_avoid_tls. If successful,
+send another EHLO - the server may give a different answer in secure mode. We
+use a separate buffer for reading the response to STARTTLS so that if it is
+negative, the original EHLO data is available for subsequent analysis, should
+the client not be required to use TLS. If the response is bad, copy the buffer
+for error analysis. */
+
+#ifdef SUPPORT_TLS
+if ( smtp_peer_options & PEER_OFFERED_TLS
+ && !suppress_tls
+ && verify_check_given_host(&sx->ob->hosts_avoid_tls, sx->host) != OK
+ && ( !sx->verify
+ || verify_check_given_host(&sx->ob->hosts_verify_avoid_tls, sx->host) != OK
+ ) )
+ {
+ uschar buffer2[4096];
+ 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
+ there is a temporary rejection of STARRTLS and tls_tempfail_tryclear is
+ false, we also defer. However, if there is a temporary rejection of STARTTLS
+ and tls_tempfail_tryclear is true, or if there is an outright rejection of
+ 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',
+ sx->ob->command_timeout))
+ {
+ if ( errno != 0
+ || buffer2[0] == 0
+ || (buffer2[0] == '4' && !sx->ob->tls_tempfail_tryclear)
+ )
+ {
+ Ustrncpy(sx->buffer, buffer2, sizeof(sx->buffer));
+ sx->buffer[sizeof(sx->buffer)-1] = '\0';
+ goto RESPONSE_FAILED;
+ }
+ }
+
+ /* STARTTLS accepted: try to negotiate a TLS session. */
+
+ else
+ TLS_NEGOTIATE:
+ {
+ address_item * addr;
+ uschar * errstr;
+ int rc = tls_client_start(sx->inblock.sock, sx->host, sx->addrlist, sx->tblock,
+# ifdef EXPERIMENTAL_DANE
+ sx->dane ? &tlsa_dnsa : NULL,
+# endif
+ &errstr);
+
+ /* TLS negotiation failed; give an error. From outside, this function may
+ be called again to try in clear on a new connection, if the options permit
+ it for this host. */
+
+ if (rc != OK)
+ {
+# ifdef EXPERIMENTAL_DANE
+ if (sx->dane) log_write(0, LOG_MAIN,
+ "DANE attempt failed; TLS connection to %s [%s]: %s",
+ sx->host->name, sx->host->address, errstr);
+# endif
+
+ errno = ERRNO_TLSFAILURE;
+ message = string_sprintf("TLS session: %s", errstr);
+ sx->send_quit = FALSE;
+ goto TLS_FAILED;
+ }
+
+ /* TLS session is set up */
+
+ smtp_peer_options_wrap = smtp_peer_options;
+ for (addr = sx->addrlist; addr; addr = addr->next)
+ if (addr->transport_return == PENDING_DEFER)
+ {
+ addr->cipher = tls_out.cipher;
+ addr->ourcert = tls_out.ourcert;
+ addr->peercert = tls_out.peercert;
+ addr->peerdn = tls_out.peerdn;
+ addr->ocsp = tls_out.ocsp;
+ }
+ }
+ }
+
+/* if smtps, we'll have smtp_command set to something else; always safe to
+reset it here. */
+smtp_command = big_buffer;
+
+/* If we started TLS, redo the EHLO/LHLO exchange over the secure channel. If
+helo_data is null, we are dealing with a connection that was passed from
+another process, and so we won't have expanded helo_data above. We have to
+expand it here. $sending_ip_address and $sending_port are set up right at the
+start of the Exim process (in exim.c). */
+
+if (tls_out.active >= 0)
+ {
+ char *greeting_cmd;
+ BOOL good_response;
+
+ if (!sx->helo_data && !(sx->helo_data = expand_string(sx->ob->helo_data)))
+ {
+ 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 (sx->smtps)
+ {
+ good_response = smtp_read_response(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+ '2', sx->ob->command_timeout);
+#ifdef EXPERIMENTAL_DSN_INFO
+ sx->smtp_greeting = string_copy(sx->buffer);
+#endif
+ if (!good_response) goto RESPONSE_FAILED;
+ }
+
+ if (sx->esmtp)
+ greeting_cmd = "EHLO";
+ else
+ {
+ greeting_cmd = "HELO";
+ DEBUG(D_transport)
+ debug_printf("not sending EHLO (host matches hosts_avoid_esmtp)\n");
+ }
+
+ 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(&sx->inblock, sx->buffer, sizeof(sx->buffer),
+ '2', sx->ob->command_timeout);
+#ifdef EXPERIMENTAL_DSN_INFO
+ sx->helo_response = string_copy(sx->buffer);
+#endif
+ if (!good_response) goto RESPONSE_FAILED;
+ smtp_peer_options = 0;
+ }
+
+/* If the host is required to use a secure channel, ensure that we
+have one. */
+
+else if ( sx->smtps
+# ifdef EXPERIMENTAL_DANE
+ || sx->dane
+# endif
+ || verify_check_given_host(&sx->ob->hosts_require_tls, sx->host) == OK
+ )
+ {
+ errno = ERRNO_TLSREQUIRED;
+ message = string_sprintf("a TLS session is required, but %s",
+ smtp_peer_options & PEER_OFFERED_TLS
+ ? "an attempt to start TLS failed" : "the server did not offer TLS support");
+ goto TLS_FAILED;
+ }
+#endif /*SUPPORT_TLS*/
+
+/* If TLS is active, we have just started it up and re-done the EHLO command,
+so its response needs to be analyzed. If TLS is not active and this is a
+continued session down a previously-used socket, we haven't just done EHLO, so
+we skip this. */
+
+if (continue_hostname == NULL
+#ifdef SUPPORT_TLS
+ || tls_out.active >= 0
+#endif
+ )
+ {
+ if (sx->esmtp || sx->lmtp)
+ {
+ sx->peer_offered = ehlo_response(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
+ | (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
+ | (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. */
+
+ 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 |= 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 ( 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 ( 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 (sx->peer_offered & PEER_OFFERED_CHUNKING)
+ {DEBUG(D_transport) debug_printf("CHUNKING usable\n");}
+
+#ifndef DISABLE_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 (sx->peer_offered & PEER_OFFERED_PRDR)
+ {DEBUG(D_transport) debug_printf("PRDR usable\n");}
+#endif
+
+ /* Note if the server supports DSN */
+ smtp_peer_options |= sx->peer_offered & PEER_OFFERED_DSN;
+ DEBUG(D_transport) debug_printf("%susing DSN\n",
+ 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(sx->buffer, sizeof(sx->buffer), sx->addrlist, sx->host,
+ sx->ob, sx->esmtp, &sx->inblock, &sx->outblock))
+ {
+ default: goto SEND_QUIT;
+ case OK: break;
+ case FAIL_SEND: goto SEND_FAILED;
+ case FAIL: goto RESPONSE_FAILED;
+ }
+ }
+ }
+pipelining_active = !!(smtp_peer_options & PEER_OFFERED_PIPE);
+
+/* The setting up of the SMTP call is now complete. Any subsequent errors are
+message-specific. */
+
+sx->setting_up = FALSE;
+
+#ifdef SUPPORT_I18N
+if (sx->addrlist->prop.utf8_msg)
+ {
+ 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",
+ sx->addrlist->prop.utf8_downcvt ? "mandatory" : "optional");
+ }
+
+/* If this is an international message we need the host to speak SMTPUTF8 */
+if (sx->utf8_needed && !(sx->peer_offered & PEER_OFFERED_UTF8))
+ {
+ errno = ERRNO_UTF8_FWD;
+ goto RESPONSE_FAILED;
+ }
+#endif
+
+return OK;
+
+
+ {
+ int code;
+
+ RESPONSE_FAILED:
+ message = NULL;
+ sx->send_quit = check_response(sx->host, &errno, sx->addrlist->more_errno,
+ sx->buffer, &code, &message, &pass_message);
+ goto FAILED;
+
+ SEND_FAILED:
+ code = '4';
+ message = US string_sprintf("send() to %s [%s] failed: %s",
+ sx->host->name, sx->host->address, strerror(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 errno, and setting_up will always be true. Treat as
+ a temporary error. */
+
+ EHLOHELO_FAILED:
+ code = '4';
+ message = string_sprintf("Remote host closed connection in response to %s"
+ " (EHLO response was: %s)", smtp_command, sx->buffer);
+ sx->send_quit = FALSE;
+ goto FAILED;
+
+#ifdef SUPPORT_TLS
+ TLS_FAILED:
+ code = '4';
+ goto FAILED;
+#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 */
+
+ yield = code == '5'
+#ifdef SUPPORT_I18N
+ || errno == ERRNO_UTF8_FWD
+#endif
+ ? FAIL : DEFER;
+
+ set_errno(sx->addrlist, errno, 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");
+
+#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_indent(" 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 */
+ sx->send_quit = FALSE;
+ }
+(void)close(sx->inblock.sock);
+sx->inblock.sock = sx->outblock.sock = -1;
+
+#ifndef DISABLE_EVENT
+(void) event_raise(sx->tblock->event_action, US"tcp:close", NULL);
+#endif
+
+continue_transport = NULL;
+continue_hostname = NULL;
+return yield;
+}
+
+
+
+
+/* Create the string of options that will be appended to the MAIL FROM:
+in the connection context buffer */
+
+static int
+build_mailcmd_options(smtp_context * sx, address_item * addrlist)
+{
+uschar * p = sx->buffer;
+address_item * addr;
+int address_count;
+
+*p = 0;
+
+/* If we know the receiving MTA supports the SIZE qualification,
+send it, adding something to the message size to allow for imprecision
+and things that get added en route. Exim keeps the number of lines
+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.) */
+
+if (sx->peer_offered & PEER_OFFERED_SIZE)
+ {
+ sprintf(CS p, " SIZE=%d", message_size+message_linecount+sx->ob->size_addition);
+ while (*p) p++;