.option protocol smtp string smtp
.cindex "LMTP" "over TCP/IP"
+.cindex "ssmtp protocol" "outbound"
+.cindex "TLS" "SSL-on-connect outbound"
+.vindex "&$port$&"
If this option is set to &"lmtp"& instead of &"smtp"&, the default value for
the &%port%& option changes to &"lmtp"&, and the transport operates the LMTP
protocol (RFC 2033) instead of SMTP. This protocol is sometimes used for local
deliveries into closed message stores. Exim also has support for running LMTP
over a pipe to a local process &-- see chapter &<<CHAPLMTP>>&.
+.new
+If this option is set to &"smtps"&, the default vaule for the &%port%& option
+changes to &"smtps"&, and the transport initiates TLS immediately after
+connecting, as an outbound SSL-on-connect, instead of using STARTTLS to upgrade.
+The Internet standards bodies strongly discourage use of this mode.
+.wen
+
.option retry_include_ip_address smtp boolean true
Exim normally includes both the host name and the IP address in the key it
/* Set the default port according to the protocol */
if (ob->port == NULL)
- ob->port = (strcmpic(ob->protocol, US"lmtp") == 0)? US"lmtp" : US"smtp";
+ ob->port = (strcmpic(ob->protocol, US"lmtp") == 0)? US"lmtp" :
+ (strcmpic(ob->protocol, US"smtps") == 0)? US"smtps" : US"smtp";
/* Set up the setup entry point, to be called before subprocesses for this
transport. */
smtp_transport_options_block *ob =
(smtp_transport_options_block *)(tblock->options_block);
BOOL lmtp = strcmpic(ob->protocol, US"lmtp") == 0;
+BOOL smtps = strcmpic(ob->protocol, US"smtps") == 0;
BOOL ok = FALSE;
BOOL send_rset = TRUE;
BOOL send_quit = TRUE;
else if (new[0] != 0) local_authenticated_sender = new;
}
+#ifndef SUPPORT_TLS
+if (smtps)
+ {
+ set_errno(addrlist, 0, US"TLS support not available", DEFER, FALSE);
+ return ERROR;
+ }
+#endif
+
/* Make a connection to the host if this isn't a continued delivery, and handle
the initial interaction and HELO/EHLO/LHLO. Connect timeout errors are handled
specially so they can be identified for retries. */
is nevertheless a reasonably clean way of programming this kind of logic,
where you want to escape on any error. */
- if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
- ob->command_timeout)) goto RESPONSE_FAILED;
+ if (!smtps)
+ {
+ if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+ ob->command_timeout)) goto RESPONSE_FAILED;
- /* Now check if the helo_data expansion went well, and sign off cleanly if it
- didn't. */
+ /* Now check if the helo_data expansion went well, and sign off cleanly if
+ it didn't. */
- if (helo_data == NULL)
- {
- uschar *message = string_sprintf("failed to expand helo_data: %s",
- expand_string_message);
- set_errno(addrlist, 0, message, DEFER, FALSE);
- yield = DEFER;
- goto SEND_QUIT;
+ if (helo_data == NULL)
+ {
+ uschar *message = string_sprintf("failed to expand helo_data: %s",
+ expand_string_message);
+ set_errno(addrlist, 0, message, DEFER, FALSE);
+ yield = DEFER;
+ goto SEND_QUIT;
+ }
}
/** Debugging without sending a message
esmtp = verify_check_this_host(&(ob->hosts_avoid_esmtp), NULL,
host->name, host->address, NULL) != 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 (smtps)
+ {
+ tls_offered = TRUE;
+ suppress_tls = FALSE;
+ ob->tls_tempfail_tryclear = FALSE;
+ smtp_command = US"SSL-on-connect";
+ goto TLS_NEGOTIATE;
+ }
+ #endif
+
if (esmtp)
{
if (smtp_write_command(&outblock, FALSE, "%s %s\r\n",
/* STARTTLS accepted: try to negotiate a TLS session. */
else
+ TLS_NEGOTIATE:
{
int rc = tls_client_start(inblock.sock,
host,
}
}
+/* 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
if (tls_active >= 0)
{
+ char *greeting_cmd;
if (helo_data == NULL)
{
helo_data = expand_string(ob->helo_data);
}
}
- if (smtp_write_command(&outblock, FALSE, "%s %s\r\n", lmtp? "LHLO" : "EHLO",
- helo_data) < 0)
+ /* For SMTPS we need to wait for the initial OK response.
+ Also, it seems likely that a server not supporting STARTTLS is broken
+ enough to perhaps not support EHLO. */
+ if (smtps)
+ {
+ if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+ ob->command_timeout)) goto RESPONSE_FAILED;
+ if (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(&outblock, FALSE, "%s %s\r\n",
+ lmtp? "LHLO" : greeting_cmd, helo_data) < 0)
goto SEND_FAILED;
if (!smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
ob->command_timeout))
if (tls_active >= 0)
{
tls_close(TRUE);
- ok = smtp_write_command(&outblock,FALSE,"EHLO %s\r\n",helo_data) >= 0 &&
- smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
- ob->command_timeout);
+ if (smtps)
+ ok = FALSE;
+ else
+ ok = smtp_write_command(&outblock,FALSE,"EHLO %s\r\n",helo_data) >= 0 &&
+ smtp_read_response(&inblock, buffer, sizeof(buffer), '2',
+ ob->command_timeout);
}
#endif