*************************************************/
/* Copyright (c) University of Cambridge 1995 - 2018 */
+/* Copyright (c) The Exim Maintainers 2020 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for handling an incoming SMTP call. */
* Local static variables *
*************************************************/
-static auth_instance *authenticated_by;
-static BOOL auth_advertised;
-#ifdef SUPPORT_TLS
-static BOOL tls_advertised;
-# ifdef EXPERIMENTAL_REQUIRETLS
-static BOOL requiretls_advertised;
-# endif
+static struct {
+ BOOL auth_advertised :1;
+#ifndef DISABLE_TLS
+ BOOL tls_advertised :1;
#endif
-static BOOL dsn_advertised;
-static BOOL esmtp;
-static BOOL helo_required = FALSE;
-static BOOL helo_verify = FALSE;
-static BOOL helo_seen;
-static BOOL helo_accept_junk;
-static BOOL count_nonmail;
-static BOOL rcpt_smtp_response_same;
-static BOOL rcpt_in_progress;
-static int nonmail_command_count;
-static BOOL smtp_exit_function_called = 0;
+ BOOL dsn_advertised :1;
+ BOOL esmtp :1;
+ BOOL helo_required :1;
+ BOOL helo_verify :1;
+ BOOL helo_seen :1;
+ BOOL helo_accept_junk :1;
+#ifndef DISABLE_PIPE_CONNECT
+ BOOL pipe_connect_acceptable :1;
+#endif
+ BOOL rcpt_smtp_response_same :1;
+ BOOL rcpt_in_progress :1;
+ BOOL smtp_exit_function_called :1;
#ifdef SUPPORT_I18N
-static BOOL smtputf8_advertised;
+ BOOL smtputf8_advertised :1;
#endif
+} fl = {
+ .helo_required = FALSE,
+ .helo_verify = FALSE,
+ .smtp_exit_function_called = FALSE,
+};
+
+static auth_instance *authenticated_by;
+static int count_nonmail;
+static int nonmail_command_count;
static int synprot_error_count;
static int unknown_command_count;
static int sync_cmd_limit;
{ "helo", sizeof("helo")-1, HELO_CMD, TRUE, FALSE },
{ "ehlo", sizeof("ehlo")-1, EHLO_CMD, TRUE, FALSE },
{ "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE },
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
{ "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE },
{ "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE },
#endif
ENV_MAIL_OPT_RET, ENV_MAIL_OPT_ENVID,
#ifdef SUPPORT_I18N
ENV_MAIL_OPT_UTF8,
-#endif
-#ifdef EXPERIMENTAL_REQUIRETLS
- ENV_MAIL_OPT_REQTLS,
#endif
};
typedef struct {
{ US"ENVID", ENV_MAIL_OPT_ENVID, TRUE },
#ifdef SUPPORT_I18N
{ US"SMTPUTF8",ENV_MAIL_OPT_UTF8, FALSE }, /* rfc6531 */
-#endif
-#ifdef EXPERIMENTAL_REQUIRETLS
- /* https://tools.ietf.org/html/draft-ietf-uta-smtp-require-tls-03 */
- { US"REQUIRETLS",ENV_MAIL_OPT_REQTLS, FALSE },
#endif
/* keep this the last entry */
{ US"NULL", ENV_MAIL_OPT_NULL, FALSE },
fd_set fds;
struct timeval tzero;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (tls_in.active.sock >= 0)
return !tls_could_read();
#endif
static BOOL
check_sync(void)
{
-if (!smtp_enforce_sync || !sender_host_address || sender_host_notsocket)
+if (!smtp_enforce_sync || !sender_host_address || f.sender_host_notsocket)
return TRUE;
return wouldblock_reading();
pipeline_response(void)
{
if ( !smtp_enforce_sync || !sender_host_address
- || sender_host_notsocket || !smtp_in_pipelining_advertised)
+ || f.sender_host_notsocket || !f.smtp_in_pipelining_advertised)
return FALSE;
-return !wouldblock_reading();
+if (wouldblock_reading()) return FALSE;
+f.smtp_in_pipelining_used = TRUE;
+return TRUE;
}
+#ifndef DISABLE_PIPE_CONNECT
+static BOOL
+pipeline_connect_sends(void)
+{
+if (!sender_host_address || f.sender_host_notsocket || !fl.pipe_connect_acceptable)
+ return FALSE;
+
+if (wouldblock_reading()) return FALSE;
+f.smtp_in_early_pipe_used = TRUE;
+return TRUE;
+}
+#endif
/*************************************************
* Log incomplete transactions *
static void
incomplete_transaction_log(uschar *what)
{
-if (sender_address == NULL || /* No transaction in progress */
- !LOGGING(smtp_incomplete_transaction))
+if (!sender_address /* No transaction in progress */
+ || !LOGGING(smtp_incomplete_transaction))
return;
/* Build list of recipients for logging */
if (recipients_count > 0)
{
- int i;
- raw_recipients = store_get(recipients_count * sizeof(uschar *));
- for (i = 0; i < recipients_count; i++)
+ raw_recipients = store_get(recipients_count * sizeof(uschar *), FALSE);
+ for (int i = 0; i < recipients_count; i++)
raw_recipients[i] = recipients_list[i].address;
raw_recipients_count = recipients_count;
}
smtp_notquit_exit(US"command-timeout", US"421",
US"%s: SMTP command timeout - closing connection",
smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+exim_exit(EXIT_FAILURE);
}
void
moan_smtp_batch(NULL, "421 SIGTERM received"); /* Does not return */
smtp_notquit_exit(US"signal-exit", US"421",
US"%s: Service not available - closing connection", smtp_active_hostname);
-exim_exit(EXIT_FAILURE, US"receiving");
+exim_exit(EXIT_FAILURE);
}
void
int rc, save_errno;
if (!smtp_out) return FALSE;
fflush(smtp_out);
-if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
+if (smtp_receive_timeout > 0) ALARM(smtp_receive_timeout);
/* Limit amount read, so non-message data is not fed to DKIM.
Take care to not touch the safety NUL at the end of the buffer. */
rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE-1, lim));
save_errno = errno;
-if (smtp_receive_timeout > 0) alarm(0);
+if (smtp_receive_timeout > 0) ALARM_CLR(0);
if (rc <= 0)
{
/* Must put the error text in fixed store, because this might be during
smtp_data_sigint_exit();
smtp_had_error = save_errno;
- smtp_read_error = string_copy_malloc(
- string_sprintf(" (error: %s)", strerror(save_errno)));
+ smtp_read_error = string_copy_perm(
+ string_sprintf(" (error: %s)", strerror(save_errno)), FALSE);
}
else
smtp_had_eof = 1;
/* Unless PIPELINING was offered, there should be no next command
until after we ack that chunk */
- if (!smtp_in_pipelining_advertised && !check_sync())
+ if (!f.smtp_in_pipelining_advertised && !check_sync())
{
unsigned n = smtp_inend - smtp_inptr;
if (n > 32) n = 32;
checking that: for convenience, TLS output errors are remembered here so that
they are also picked up later by smtp_fflush().
+This function is exposed to the local_scan API; do not change the signature.
+
Arguments:
format format string
more further data expected
/* This is split off so that verify.c:respond_printf() can, in effect, call
smtp_printf(), bearing in mind that in C a vararg function can't directly
-call another vararg function, only a function which accepts a va_list. */
+call another vararg function, only a function which accepts a va_list.
+
+This function is exposed to the local_scan API; do not change the signature.
+*/
+/*XXX consider passing caller-info in, for string_vformat-onward */
void
smtp_vprintf(const char *format, BOOL more, va_list ap)
{
+gstring gs = { .size = big_buffer_size, .ptr = 0, .s = big_buffer };
BOOL yield;
-yield = string_vformat(big_buffer, big_buffer_size, format, ap);
+/* Use taint-unchecked routines for writing into big_buffer, trusting
+that we'll never expand it. */
+
+yield = !! string_vformat(&gs, SVFMT_TAINT_NOCHK, format, ap);
+string_from_gstring(&gs);
DEBUG(D_receive)
{
- void *reset_point = store_get(0);
uschar *msg_copy, *cr, *end;
- msg_copy = string_copy(big_buffer);
- end = msg_copy + Ustrlen(msg_copy);
+ msg_copy = string_copy(gs.s);
+ end = msg_copy + gs.ptr;
while ((cr = Ustrchr(msg_copy, '\r')) != NULL) /* lose CRs */
- memmove(cr, cr + 1, (end--) - cr);
+ memmove(cr, cr + 1, (end--) - cr);
debug_printf("SMTP>> %s", msg_copy);
- store_reset(reset_point);
}
if (!yield)
{
log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()");
smtp_closedown(US"Unexpected error");
- exim_exit(EXIT_FAILURE, NULL);
+ exim_exit(EXIT_FAILURE);
}
/* If this is the first output for a (non-batch) RCPT command, see if all RCPTs
do it that way, so as not to have to mess with the code for the RCPT command,
which sometimes uses smtp_printf() and sometimes smtp_respond(). */
-if (rcpt_in_progress)
+if (fl.rcpt_in_progress)
{
if (rcpt_smtp_response == NULL)
rcpt_smtp_response = string_copy(big_buffer);
- else if (rcpt_smtp_response_same &&
+ else if (fl.rcpt_smtp_response_same &&
Ustrcmp(rcpt_smtp_response, big_buffer) != 0)
- rcpt_smtp_response_same = FALSE;
- rcpt_in_progress = FALSE;
+ fl.rcpt_smtp_response_same = FALSE;
+ fl.rcpt_in_progress = FALSE;
}
/* Now write the string */
-#ifdef SUPPORT_TLS
-if (tls_in.active.sock >= 0)
- {
- if (tls_write(NULL, big_buffer, Ustrlen(big_buffer), more) < 0)
- smtp_write_error = -1;
- }
-else
+if (
+#ifndef DISABLE_TLS
+ tls_in.active.sock >= 0 ? (tls_write(NULL, gs.s, gs.ptr, more) < 0) :
#endif
-
-if (fprintf(smtp_out, "%s", big_buffer) < 0) smtp_write_error = -1;
+ (fwrite(gs.s, gs.ptr, 1, smtp_out) == 0)
+ )
+ smtp_write_error = -1;
}
/* This function isn't currently used within Exim (it detects errors when it
tries to read the next SMTP input), but is available for use in local_scan().
-For non-TLS connections, it flushes the output and checks for errors. For
-TLS-connections, it checks for a previously-detected TLS write error.
+It flushes the output and checks for errors.
Arguments: none
Returns: 0 for no error; -1 after an error
smtp_fflush(void)
{
if (tls_in.active.sock < 0 && fflush(smtp_out) != 0) smtp_write_error = -1;
+
+if (
+#ifndef DISABLE_TLS
+ tls_in.active.sock >= 0 ? (tls_write(NULL, NULL, 0, FALSE) < 0) :
+#endif
+ (fflush(smtp_out) != 0)
+ )
+ smtp_write_error = -1;
+
return smtp_write_error;
}
}
else
{
- proxy_session_failed = TRUE;
+ f.proxy_session_failed = TRUE;
DEBUG(D_receive)
debug_printf("Failure to extract proxied host, only QUIT allowed\n");
}
{
int c;
int ptr = 0;
-smtp_cmd_list *p;
BOOL hadnull = FALSE;
had_command_timeout = 0;
to the start of the actual data characters. Check for SMTP synchronization
if required. */
-for (p = cmd_list; p < cmd_list_end; p++)
+for (smtp_cmd_list * p = cmd_list; p < cmd_list_end; p++)
{
#ifdef SUPPORT_PROXY
/* Only allow QUIT command if Proxy Protocol parsing failed */
- if (proxy_session && proxy_session_failed && p->cmd != QUIT_CMD)
+ if (proxy_session && f.proxy_session_failed && p->cmd != QUIT_CMD)
continue;
#endif
if ( p->len
check_sync && /* Local flag set */
smtp_enforce_sync && /* Global flag set */
sender_host_address != NULL && /* Not local input */
- !sender_host_notsocket) /* Really is a socket */
+ !f.sender_host_notsocket) /* Really is a socket */
return BADSYN_CMD;
/* The variables $smtp_command and $smtp_command_argument point into the
#ifdef SUPPORT_PROXY
/* Only allow QUIT command if Proxy Protocol parsing failed */
-if (proxy_session && proxy_session_failed)
+if (proxy_session && f.proxy_session_failed)
return PROXY_FAIL_IGNORE_CMD;
#endif
&& check_sync /* Local flag set */
&& smtp_enforce_sync /* Global flag set */
&& sender_host_address /* Not local input */
- && !sender_host_notsocket) /* Really is a socket */
+ && !f.sender_host_notsocket) /* Really is a socket */
return BADSYN_CMD;
return OTHER_CMD;
if (host_checking)
return string_sprintf("SMTP connection from %s", hostname);
-if (sender_host_unknown || sender_host_notsocket)
+if (f.sender_host_unknown || f.sender_host_notsocket)
return string_sprintf("SMTP connection from %s", sender_ident);
-if (is_inetd)
+if (f.is_inetd)
return string_sprintf("SMTP connection from %s (via inetd)", hostname);
-if (LOGGING(incoming_interface) && interface_address != NULL)
+if (LOGGING(incoming_interface) && interface_address)
return string_sprintf("SMTP connection from %s I=[%s]:%d", hostname,
interface_address, interface_port);
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
/* Append TLS-related information to a log line
Arguments:
s_tlslog(gstring * g)
{
if (LOGGING(tls_cipher) && tls_in.cipher)
+ {
g = string_append(g, 2, US" X=", tls_in.cipher);
+#ifdef EXPERIMENTAL_TLS_RESUME
+ if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED)
+ g = string_catn(g, US"*", 1);
+#endif
+ }
if (LOGGING(tls_certificate_verified) && tls_in.cipher)
g = string_append(g, 2, US" CV=", tls_in.certificate_verified? "yes":"no");
if (LOGGING(tls_peerdn) && tls_in.peerdn)
g = string_append(g, 3, US" DN=\"", string_printing(tls_in.peerdn), US"\"");
if (LOGGING(tls_sni) && tls_in.sni)
- g = string_append(g, 3, US" SNI=\"", string_printing(tls_in.sni), US"\"");
+ g = string_append(g, 2, US" SNI=", string_printing2(tls_in.sni, SP_TAB|SP_SPACE));
return g;
}
#endif
+
+
+static gstring *
+s_connhad_log(gstring * g)
+{
+uschar * sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE
+ ? US" C=..." : US" C=";
+
+for (int i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
+ if (smtp_connection_had[i] != SCH_NONE)
+ {
+ g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
+ sep = US",";
+ }
+for (int i = 0; i < smtp_ch_index; i++)
+ {
+ g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
+ sep = US",";
+ }
+return g;
+}
+
+
/*************************************************
* Log lack of MAIL if so configured *
*************************************************/
void
smtp_log_no_mail(void)
{
-int i;
-uschar * sep, * s;
+uschar * s;
gstring * g = NULL;
if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail))
if (authenticated_id) g = string_append(g, 2, US":", authenticated_id);
}
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
g = s_tlslog(g);
#endif
-sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE ? US" C=..." : US" C=";
-
-for (i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
- if (smtp_connection_had[i] != SCH_NONE)
- {
- g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
- sep = US",";
- }
-
-for (i = 0; i < smtp_ch_index; i++)
- {
- g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
- sep = US",";
- }
+g = s_connhad_log(g);
if (!(s = string_from_gstring(g))) s = US"";
log_write(0, LOG_MAIN, "no MAIL in %sSMTP connection from %s D=%s%s",
- tcp_in_fastopen ? US"TFO " : US"",
+ f.tcp_in_fastopen ? f.tcp_in_fastopen_data ? US"TFO* " : US"TFO " : US"",
host_and_ident(FALSE), string_timesince(&smtp_connection_start), s);
}
uschar *
smtp_cmd_hist(void)
{
-int i;
gstring * list = NULL;
uschar * s;
-for (i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
+for (int i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
if (smtp_connection_had[i] != SCH_NONE)
list = string_append_listele(list, ',', smtp_names[smtp_connection_had[i]]);
-for (i = 0; i < smtp_ch_index; i++)
+for (int i = 0; i < smtp_ch_index; i++)
list = string_append_listele(list, ',', smtp_names[smtp_connection_had[i]]);
s = string_from_gstring(list);
{
uschar *start = s;
uschar *end = s + Ustrlen(s);
-BOOL yield = helo_accept_junk;
+BOOL yield = fl.helo_accept_junk;
/* Discard any previous helo name */
-if (sender_helo_name)
- {
- store_free(sender_helo_name);
- sender_helo_name = NULL;
- }
+sender_helo_name = NULL;
/* Skip tests if junk is permitted. */
/* Save argument if OK */
-if (yield) sender_helo_name = string_copy_malloc(start);
+if (yield) sender_helo_name = string_copy_perm(start, TRUE);
return yield;
}
Returns: nothing
*/
-void
+void *
smtp_reset(void *reset_point)
{
recipients_list = NULL;
message_size = -1;
acl_added_headers = NULL;
acl_removed_headers = NULL;
-queue_only_policy = FALSE;
+f.queue_only_policy = FALSE;
rcpt_smtp_response = NULL;
-rcpt_smtp_response_same = TRUE;
-rcpt_in_progress = FALSE;
-deliver_freeze = FALSE; /* Can be set by ACL */
+fl.rcpt_smtp_response_same = TRUE;
+fl.rcpt_in_progress = FALSE;
+f.deliver_freeze = FALSE; /* Can be set by ACL */
freeze_tell = freeze_tell_config; /* Can be set by ACL */
fake_response = OK; /* Can be set by ACL */
#ifdef WITH_CONTENT_SCAN
-no_mbox_unspool = FALSE; /* Can be set by ACL */
+f.no_mbox_unspool = FALSE; /* Can be set by ACL */
#endif
-submission_mode = FALSE; /* Can be set by ACL */
-suppress_local_fixups = suppress_local_fixups_default; /* Can be set by ACL */
-active_local_from_check = local_from_check; /* Can be set by ACL */
-active_local_sender_retain = local_sender_retain; /* Can be set by ACL */
+f.submission_mode = FALSE; /* Can be set by ACL */
+f.suppress_local_fixups = f.suppress_local_fixups_default; /* Can be set by ACL */
+f.active_local_from_check = local_from_check; /* Can be set by ACL */
+f.active_local_sender_retain = local_sender_retain; /* Can be set by ACL */
sending_ip_address = NULL;
return_path = sender_address = NULL;
-sender_data = NULL; /* Can be set by ACL */
+deliver_localpart_data = deliver_domain_data =
+recipient_data = sender_data = NULL; /* Can be set by ACL */
deliver_localpart_parent = deliver_localpart_orig = NULL;
deliver_domain_parent = deliver_domain_orig = NULL;
callout_address = NULL;
dkim_cur_signer = dkim_signers =
dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL;
dkim_cur_signer = dkim_signers = dkim_signing_domain = dkim_signing_selector = NULL;
-dkim_disable_verify = FALSE;
+f.dkim_disable_verify = FALSE;
dkim_collect_input = 0;
dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
dkim_key_length = 0;
-dkim_verify_signers = US"$dkim_signers";
#endif
-#ifdef EXPERIMENTAL_DMARC
-dmarc_has_been_checked = dmarc_disable_verify = dmarc_enable_forensic = FALSE;
+#ifdef SUPPORT_DMARC
+f.dmarc_has_been_checked = f.dmarc_disable_verify = f.dmarc_enable_forensic = FALSE;
dmarc_domain_policy = dmarc_status = dmarc_status_text =
dmarc_used_domain = NULL;
#endif
#ifdef EXPERIMENTAL_ARC
arc_state = arc_state_reason = NULL;
+arc_received_instance = 0;
#endif
dsn_ret = 0;
dsn_envid = NULL;
store_free(this);
}
store_reset(reset_point);
+return store_mark();
}
smtp_setup_batch_msg(void)
{
int done = 0;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
/* Save the line count at the start of each transaction - single commands
like HELO and RSET count as whole transactions. */
if ((receive_feof)()) return 0; /* Treat EOF as QUIT */
cancel_cutthrough_connection(TRUE, US"smtp_setup_batch_msg");
-smtp_reset(reset_point); /* Reset for start of message */
+reset_point = smtp_reset(reset_point); /* Reset for start of message */
/* Deal with SMTP commands. This loop is exited by setting done to a POSITIVE
value. The values are 2 larger than the required yield of the function. */
case RSET_CMD:
cancel_cutthrough_connection(TRUE, US"RSET received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
bsmtp_transaction_linecount = receive_linecount;
break;
/* Reset to start of message */
cancel_cutthrough_connection(TRUE, US"MAIL received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
/* Apply SMTP rewrite */
if ( !sender_domain
&& sender_address[0] != 0 && sender_address[0] != '@')
- if (allow_unqualified_sender)
+ if (f.allow_unqualified_sender)
{
sender_address = rewrite_address_qualify(sender_address, FALSE);
DEBUG(D_receive) debug_printf("unqualified address %s accepted "
add it to the list of recipients. */
if (!recipient_domain)
- if (allow_unqualified_recipient)
+ if (f.allow_unqualified_recipient)
{
DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
recipient);
+#ifndef DISABLE_TLS
static BOOL
smtp_log_tls_fail(uschar * errstr)
{
log_write(0, LOG_MAIN, "TLS error on %s %s", conn_info, errstr);
return FALSE;
}
+#endif
static void
tfo_in_check(void)
{
-# ifdef TCP_INFO
-struct tcp_info tinfo;
-socklen_t len = sizeof(tinfo);
+# ifdef __FreeBSD__
+int is_fastopen;
+socklen_t len = sizeof(is_fastopen);
-if ( getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
- && tinfo.tcpi_state == TCP_SYN_RECV
- )
+/* The tinfo TCPOPT_FAST_OPEN bit seems unreliable, and we don't see state
+TCP_SYN_RCV (as of 12.1) so no idea about data-use. */
+
+if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_FASTOPEN, &is_fastopen, &len) == 0)
{
- DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
- tcp_in_fastopen = TRUE;
+ if (is_fastopen)
+ {
+ DEBUG(D_receive)
+ debug_printf("TFO mode connection (TCP_FASTOPEN getsockopt)\n");
+ f.tcp_in_fastopen = TRUE;
+ }
}
+else DEBUG(D_receive)
+ debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
+
+# elif defined(TCP_INFO)
+struct tcp_info tinfo;
+socklen_t len = sizeof(tinfo);
+
+if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0)
+# ifdef TCPI_OPT_SYN_DATA /* FreeBSD 11,12 do not seem to have this yet */
+ if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
+ {
+ DEBUG(D_receive)
+ debug_printf("TFO mode connection (ACKd data-on-SYN)\n");
+ f.tcp_in_fastopen_data = f.tcp_in_fastopen = TRUE;
+ }
+ else
+# endif
+ if (tinfo.tcpi_state == TCP_SYN_RECV) /* Not seen on FreeBSD 12.1 */
+ {
+ DEBUG(D_receive)
+ debug_printf("TFO mode connection (state TCP_SYN_RECV)\n");
+ f.tcp_in_fastopen = TRUE;
+ }
+else DEBUG(D_receive)
+ debug_printf("TCP_INFO getsockopt: %s\n", strerror(errno));
# endif
}
#endif
/* Default values for certain variables */
-helo_seen = esmtp = helo_accept_junk = FALSE;
+fl.helo_seen = fl.esmtp = fl.helo_accept_junk = FALSE;
smtp_mailcmd_count = 0;
count_nonmail = TRUE_UNSET;
synprot_error_count = unknown_command_count = nonmail_command_count = 0;
smtp_delay_mail = smtp_rlm_base;
-auth_advertised = FALSE;
-smtp_in_pipelining_advertised = FALSE;
-pipelining_enable = TRUE;
+fl.auth_advertised = FALSE;
+f.smtp_in_pipelining_advertised = f.smtp_in_pipelining_used = FALSE;
+f.pipelining_enable = TRUE;
sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING;
-smtp_exit_function_called = FALSE; /* For avoiding loop in not-quit exit */
+fl.smtp_exit_function_called = FALSE; /* For avoiding loop in not-quit exit */
/* If receiving by -bs from a trusted user, or testing with -bh, we allow
authentication settings from -oMaa to remain in force. */
-if (!host_checking && !sender_host_notsocket)
+if (!host_checking && !f.sender_host_notsocket)
sender_host_auth_pubname = sender_host_authenticated = NULL;
authenticated_by = NULL;
-#ifdef SUPPORT_TLS
-tls_in.cipher = tls_in.peerdn = NULL;
+#ifndef DISABLE_TLS
+tls_in.ver = tls_in.cipher = tls_in.peerdn = NULL;
tls_in.ourcert = tls_in.peercert = NULL;
tls_in.sni = NULL;
tls_in.ocsp = OCSP_NOT_REQ;
-tls_advertised = FALSE;
-# ifdef EXPERIMENTAL_REQUIRETLS
-requiretls_advertised = FALSE;
-# endif
+fl.tls_advertised = FALSE;
#endif
-dsn_advertised = FALSE;
+fl.dsn_advertised = FALSE;
#ifdef SUPPORT_I18N
-smtputf8_advertised = FALSE;
+fl.smtputf8_advertised = FALSE;
#endif
/* Reset ACL connection variables */
acl_var_c = NULL;
-/* Allow for trailing 0 in the command and data buffers. */
+/* Allow for trailing 0 in the command and data buffers. Tainted. */
-if (!(smtp_cmd_buffer = US malloc(2*SMTP_CMD_BUFFER_SIZE + 2)))
- log_write(0, LOG_MAIN|LOG_PANIC_DIE,
- "malloc() failed for SMTP command buffer");
+smtp_cmd_buffer = store_get_perm(2*SMTP_CMD_BUFFER_SIZE + 2, TRUE);
smtp_cmd_buffer[0] = 0;
smtp_data_buffer = smtp_cmd_buffer + SMTP_CMD_BUFFER_SIZE + 1;
If smtp_accept_max and smtp_accept_reserve are set, keep some connections in
reserve for certain hosts and/or networks. */
-if (!sender_host_unknown)
+if (!f.sender_host_unknown)
{
int rc;
BOOL reserved_host = FALSE;
#define OPTSTYLE 3
#endif
- if (!host_checking && !sender_host_notsocket)
+ if (!host_checking && !f.sender_host_notsocket)
{
#if OPTSTYLE == 1
EXIM_SOCKLEN_T optlen = sizeof(struct ip_options) + MAX_IPOPTLEN;
- struct ip_options *ipopt = store_get(optlen);
+ struct ip_options *ipopt = store_get(optlen, FALSE);
#elif OPTSTYLE == 2
struct ip_opts ipoptblock;
struct ip_opts *ipopt = &ipoptblock;
{
uschar *p = big_buffer;
uschar *pend = big_buffer + big_buffer_size;
- uschar *opt, *adptr;
+ uschar *adptr;
int optcount;
struct in_addr addr;
Ustrcpy(p, "IP options on incoming call:");
p += Ustrlen(p);
- for (opt = optstart; opt != NULL &&
- opt < US (ipopt) + optlen;)
- {
+ for (uschar * opt = optstart; opt && opt < US (ipopt) + optlen; )
switch (*opt)
{
case IPOPT_EOL:
default:
{
- int i;
if (pend - p < 4 + 3*opt[1]) { opt = NULL; break; }
Ustrcat(p, "[ ");
p += 2;
- for (i = 0; i < opt[1]; i++)
+ for (int i = 0; i < opt[1]; i++)
p += sprintf(CS p, "%2.2x ", opt[i]);
*p++ = ']';
}
opt += opt[1];
break;
}
- }
*p = 0;
log_write(0, LOG_MAIN, "%s", big_buffer);
setting is an attempt to get rid of some hanging connections that stick in
read() when the remote end (usually a dialup) goes away. */
- if (smtp_accept_keepalive && !sender_host_notsocket)
+ if (smtp_accept_keepalive && !f.sender_host_notsocket)
ip_keepalive(fileno(smtp_out), sender_host_address, FALSE);
/* If the current host matches host_lookup, set the name by doing a
addresses in the headers. For a site that permits no qualification, this
won't take long, however. */
- allow_unqualified_sender =
+ f.allow_unqualified_sender =
verify_check_host(&sender_unqualified_hosts) == OK;
- allow_unqualified_recipient =
+ f.allow_unqualified_recipient =
verify_check_host(&recipient_unqualified_hosts) == OK;
/* Determine whether HELO/EHLO is required for this host. The requirement
can be hard or soft. */
- helo_required = verify_check_host(&helo_verify_hosts) == OK;
- if (!helo_required)
- helo_verify = verify_check_host(&helo_try_verify_hosts) == OK;
+ fl.helo_required = verify_check_host(&helo_verify_hosts) == OK;
+ if (!fl.helo_required)
+ fl.helo_verify = verify_check_host(&helo_try_verify_hosts) == OK;
/* Determine whether this hosts is permitted to send syntactic junk
after a HELO or EHLO command. */
- helo_accept_junk = verify_check_host(&helo_accept_junk_hosts) == OK;
+ fl.helo_accept_junk = verify_check_host(&helo_accept_junk_hosts) == OK;
}
/* For batch SMTP input we are now done. */
#ifdef SUPPORT_PROXY
proxy_session = FALSE;
-proxy_session_failed = FALSE;
+f.proxy_session_failed = FALSE;
if (check_proxy_protocol_host())
setup_proxy_protocol_host();
#endif
/* Start up TLS if tls_on_connect is set. This is for supporting the legacy
smtps port for use with older style SSL MTAs. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (tls_in.on_connect)
{
if (tls_server_start(tls_require_ciphers, &user_msg) != OK)
/* Before we write the banner, check that there is no input pending, unless
this synchronisation check is disabled. */
+#ifndef DISABLE_PIPE_CONNECT
+fl.pipe_connect_acceptable =
+ sender_host_address && verify_check_host(&pipe_connect_advertise_hosts) == OK;
+
if (!check_sync())
- {
- unsigned n = smtp_inend - smtp_inptr;
- if (n > 32) n = 32;
-
- log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
- "synchronization error (input sent without waiting for greeting): "
- "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
- string_printing(string_copyn(smtp_inptr, n)));
- smtp_printf("554 SMTP synchronization error\r\n", FALSE);
- return FALSE;
- }
+ if (fl.pipe_connect_acceptable)
+ f.smtp_in_early_pipe_used = TRUE;
+ else
+#else
+if (!check_sync())
+#endif
+ {
+ unsigned n = smtp_inend - smtp_inptr;
+ if (n > 128) n = 128;
+
+ log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
+ "synchronization error (input sent without waiting for greeting): "
+ "rejected connection from %s input=\"%s\"", host_and_ident(TRUE),
+ string_printing(string_copyn(smtp_inptr, n)));
+ smtp_printf("554 SMTP synchronization error\r\n", FALSE);
+ return FALSE;
+ }
/* Now output the banner */
+/*XXX the ehlo-resp code does its own tls/nontls bit. Maybe subroutine that? */
-smtp_printf("%s", FALSE, string_from_gstring(ss));
+smtp_printf("%s",
+#ifndef DISABLE_PIPE_CONNECT
+ fl.pipe_connect_acceptable && pipeline_connect_sends(),
+#else
+ FALSE,
+#endif
+ string_from_gstring(ss));
/* Attempt to see if we sent the banner before the last ACK of the 3-way
handshake arrived. If so we must have managed a TFO. */
#ifdef TCP_FASTOPEN
-tfo_in_check();
+if (sender_host_address && !f.sender_host_notsocket) tfo_in_check();
#endif
return TRUE;
int yield = -1;
log_write(type, LOG_MAIN, "SMTP %s error in \"%s\" %s %s",
- (type == L_smtp_syntax_error)? "syntax" : "protocol",
+ type == L_smtp_syntax_error ? "syntax" : "protocol",
string_printing(smtp_cmd_buffer), host_and_ident(TRUE), errmess);
if (++synprot_error_count > smtp_max_synprot_errors)
{
yield = 1;
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
- "syntax or protocol errors (last command was \"%s\")",
- host_and_ident(FALSE), string_printing(smtp_cmd_buffer));
+ "syntax or protocol errors (last command was \"%s\", %s)",
+ host_and_ident(FALSE), string_printing(smtp_cmd_buffer),
+ string_from_gstring(s_connhad_log(NULL))
+ );
}
if (code > 0)
int esclen = 0;
uschar *esc = US"";
-if (!final && no_multiline_responses) return;
+if (!final && f.no_multiline_responses) return;
if (codelen > 4)
{
do it that way, so as not to have to mess with the code for the RCPT command,
which sometimes uses smtp_printf() and sometimes smtp_respond(). */
-if (rcpt_in_progress)
+if (fl.rcpt_in_progress)
{
if (rcpt_smtp_response == NULL)
rcpt_smtp_response = string_copy(msg);
- else if (rcpt_smtp_response_same &&
+ else if (fl.rcpt_smtp_response_same &&
Ustrcmp(rcpt_smtp_response, msg) != 0)
- rcpt_smtp_response_same = FALSE;
- rcpt_in_progress = FALSE;
+ fl.rcpt_smtp_response_same = FALSE;
+ fl.rcpt_in_progress = FALSE;
}
/* Now output the message, splitting it up into multiple lines if necessary.
smtp_printf("%.3s%c%.*s%s\r\n", !final, code, final ? ' ':'-', esclen, esc, msg);
return;
}
- else if (nl[1] == 0 || no_multiline_responses)
+ else if (nl[1] == 0 || f.no_multiline_responses)
{
smtp_printf("%.3s%c%.*s%.*s\r\n", !final, code, final ? ' ':'-', esclen, esc,
(int)(nl - msg), msg);
{
smtp_printf("%.3s-%.*s%.*s\r\n", TRUE, code, esclen, esc, (int)(nl - msg), msg);
msg = nl + 1;
- while (isspace(*msg)) msg++;
+ Uskip_whitespace(&msg);
}
}
}
if (sender_verified_failed &&
!testflag(sender_verified_failed, af_sverify_told))
{
- BOOL save_rcpt_in_progress = rcpt_in_progress;
- rcpt_in_progress = FALSE; /* So as not to treat these as the error */
+ BOOL save_rcpt_in_progress = fl.rcpt_in_progress;
+ fl.rcpt_in_progress = FALSE; /* So as not to treat these as the error */
setflag(sender_verified_failed, af_sverify_told);
sender_verified_failed->address,
sender_verified_failed->user_message));
- rcpt_in_progress = save_rcpt_in_progress;
+ fl.rcpt_in_progress = save_rcpt_in_progress;
}
/* Sort out text for logging */
be re-implemented in a tidier fashion. */
else
- if (acl_temp_details && user_msg)
+ if (f.acl_temp_details && user_msg)
{
if ( smtp_return_error_details
&& sender_verified_failed
if (log_reject_target != 0)
{
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
gstring * g = s_tlslog(NULL);
uschar * tls = string_from_gstring(g);
if (!tls) tls = US"";
uschar *user_msg = NULL;
uschar *log_msg = NULL;
-/* Check for recursive acll */
+/* Check for recursive call */
-if (smtp_exit_function_called)
+if (fl.smtp_exit_function_called)
{
log_write(0, LOG_PANIC, "smtp_notquit_exit() called more than once (%s)",
reason);
return;
}
-smtp_exit_function_called = TRUE;
+fl.smtp_exit_function_called = TRUE;
/* Call the not-QUIT ACL, if there is one, unless no reason is given. */
log_msg);
}
+/* If the connection was dropped, we certainly are no longer talking TLS */
+tls_in.active.sock = -1;
+
/* Write an SMTP response if we are expected to give one. As the default
-responses are all internal, they should always fit in the buffer, but code a
-warning, just in case. Note that string_vformat() still leaves a complete
-string, even if it is incomplete. */
+responses are all internal, they should be reasonable size. */
if (code && defaultrespond)
{
smtp_respond(code, 3, TRUE, user_msg);
else
{
- uschar buffer[128];
+ gstring * g;
va_list ap;
+
va_start(ap, defaultrespond);
- if (!string_vformat(buffer, sizeof(buffer), CS defaultrespond, ap))
- log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_notquit_exit()");
- smtp_printf("%s %s\r\n", FALSE, code, buffer);
+ g = string_vformat(NULL, SVFMT_EXTEND|SVFMT_REBUFFER, CS defaultrespond, ap);
va_end(ap);
+ smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g));
}
mac_smtp_fflush();
}
else if (sender_host_address == NULL)
{
HDEBUG(D_receive) debug_printf("no client IP address: assume success\n");
- helo_verified = TRUE;
+ f.helo_verified = TRUE;
}
/* Deal with the more common case when there is a sending IP address */
else if (sender_helo_name[0] == '[')
{
- helo_verified = Ustrncmp(sender_helo_name+1, sender_host_address,
+ f.helo_verified = Ustrncmp(sender_helo_name+1, sender_host_address,
Ustrlen(sender_host_address)) == 0;
#if HAVE_IPV6
- if (!helo_verified)
+ if (!f.helo_verified)
{
if (strncmpic(sender_host_address, US"::ffff:", 7) == 0)
- helo_verified = Ustrncmp(sender_helo_name + 1,
+ f.helo_verified = Ustrncmp(sender_helo_name + 1,
sender_host_address + 7, Ustrlen(sender_host_address) - 7) == 0;
}
#endif
HDEBUG(D_receive)
- { if (helo_verified) debug_printf("matched host address\n"); }
+ { if (f.helo_verified) debug_printf("matched host address\n"); }
}
/* Do a reverse lookup if one hasn't already given a positive or negative
/* If a host name is known, check it and all its aliases. */
if (sender_host_name)
- if ((helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0))
+ if ((f.helo_verified = strcmpic(sender_host_name, sender_helo_name) == 0))
{
sender_helo_dnssec = sender_host_dnssec;
HDEBUG(D_receive) debug_printf("matched host name\n");
{
uschar **aliases = sender_host_aliases;
while (*aliases)
- if ((helo_verified = strcmpic(*aliases++, sender_helo_name) == 0))
+ if ((f.helo_verified = strcmpic(*aliases++, sender_helo_name) == 0))
{
sender_helo_dnssec = sender_host_dnssec;
break;
}
- HDEBUG(D_receive) if (helo_verified)
+ HDEBUG(D_receive) if (f.helo_verified)
debug_printf("matched alias %s\n", *(--aliases));
}
/* Final attempt: try a forward lookup of the helo name */
- if (!helo_verified)
+ if (!f.helo_verified)
{
int rc;
- host_item h;
- dnssec_domains d;
- host_item *hh;
-
- h.name = sender_helo_name;
- h.address = NULL;
- h.mx = MX_NONE;
- h.next = NULL;
- d.request = US"*";
- d.require = US"";
+ host_item h =
+ {.name = sender_helo_name, .address = NULL, .mx = MX_NONE, .next = NULL};
+ dnssec_domains d =
+ {.request = US"*", .require = US""};
HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
sender_helo_name);
rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A | HOST_FIND_BY_AAAA,
NULL, NULL, NULL, &d, NULL, NULL);
if (rc == HOST_FOUND || rc == HOST_FOUND_LOCAL)
- for (hh = &h; hh; hh = hh->next)
+ for (host_item * hh = &h; hh; hh = hh->next)
if (Ustrcmp(hh->address, sender_host_address) == 0)
{
- helo_verified = TRUE;
+ f.helo_verified = TRUE;
if (h.dnssec == DS_YES) sender_helo_dnssec = TRUE;
HDEBUG(D_receive)
- {
debug_printf("IP address for %s matches calling address\n"
"Forward DNS security status: %sverified\n",
sender_helo_name, sender_helo_dnssec ? "" : "un");
- }
break;
}
}
}
-if (!helo_verified) helo_verify_failed = TRUE; /* We've tried ... */
+if (!f.helo_verified) f.helo_verify_failed = TRUE; /* We've tried ... */
return yield;
}
smtp_in_auth(auth_instance *au, uschar ** s, uschar ** ss)
{
const uschar *set_id = NULL;
-int rc, i;
+int rc;
/* Run the checking code, passing the remainder of the command line as
data. Initials the $auth<n> variables as empty. Initialize $0 empty and set
authenticated_id. Save this in permanent store, as the working store gets
reset at HELO, RSET, etc. */
-for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
+for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL;
expand_nmax = 0;
expand_nlength[0] = 0; /* $0 contains nothing */
rc = (au->info->servercode)(au, smtp_cmd_data);
if (au->set_id) set_id = expand_string(au->set_id);
expand_nmax = -1; /* Reset numeric variables */
-for (i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth<n> */
+for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth<n> */
/* The value of authenticated_id is stored in the spool file and printed in
log lines. It must not contain binary zeros or newline characters. In
switch(rc)
{
case OK:
- if (!au->set_id || set_id) /* Complete success */
- {
- if (set_id) authenticated_id = string_copy_malloc(set_id);
- sender_host_authenticated = au->name;
- sender_host_auth_pubname = au->public_name;
- authentication_failed = FALSE;
- authenticated_fail_id = NULL; /* Impossible to already be set? */
-
- received_protocol =
- (sender_host_address ? protocols : protocols_local)
- [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
- *s = *ss = US"235 Authentication succeeded";
- authenticated_by = au;
- break;
- }
+ if (!au->set_id || set_id) /* Complete success */
+ {
+ if (set_id) authenticated_id = string_copy_perm(set_id, TRUE);
+ sender_host_authenticated = au->name;
+ sender_host_auth_pubname = au->public_name;
+ authentication_failed = FALSE;
+ authenticated_fail_id = NULL; /* Impossible to already be set? */
+
+ received_protocol =
+ (sender_host_address ? protocols : protocols_local)
+ [pextend + pauthed + (tls_in.active.sock >= 0 ? pcrpted:0)];
+ *s = *ss = US"235 Authentication succeeded";
+ authenticated_by = au;
+ break;
+ }
- /* Authentication succeeded, but we failed to expand the set_id string.
- Treat this as a temporary error. */
+ /* Authentication succeeded, but we failed to expand the set_id string.
+ Treat this as a temporary error. */
- auth_defer_msg = expand_string_message;
- /* Fall through */
+ auth_defer_msg = expand_string_message;
+ /* Fall through */
case DEFER:
- if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
- *s = string_sprintf("435 Unable to authenticate at present%s",
- auth_defer_user_msg);
- *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
- set_id, auth_defer_msg);
- break;
+ if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+ *s = string_sprintf("435 Unable to authenticate at present%s",
+ auth_defer_user_msg);
+ *ss = string_sprintf("435 Unable to authenticate at present%s: %s",
+ set_id, auth_defer_msg);
+ break;
case BAD64:
- *s = *ss = US"501 Invalid base64 data";
- break;
+ *s = *ss = US"501 Invalid base64 data";
+ break;
case CANCELLED:
- *s = *ss = US"501 Authentication cancelled";
- break;
+ *s = *ss = US"501 Authentication cancelled";
+ break;
case UNEXPECTED:
- *s = *ss = US"553 Initial data not expected";
- break;
+ *s = *ss = US"553 Initial data not expected";
+ break;
case FAIL:
- if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
- *s = US"535 Incorrect authentication data";
- *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
- break;
+ if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+ *s = US"535 Incorrect authentication data";
+ *ss = string_sprintf("535 Incorrect authentication data%s", set_id);
+ break;
default:
- if (set_id) authenticated_fail_id = string_copy_malloc(set_id);
- *s = US"435 Internal error";
- *ss = string_sprintf("435 Internal error%s: return %d from authentication "
- "check", set_id, rc);
- break;
+ if (set_id) authenticated_fail_id = string_copy_perm(set_id, TRUE);
+ *s = US"435 Internal error";
+ *ss = string_sprintf("435 Internal error%s: return %d from authentication "
+ "check", set_id, rc);
+ break;
}
return rc;
qualify_recipient(uschar ** recipient, uschar * smtp_cmd_data, uschar * tag)
{
int rd;
-if (allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0)
+if (f.allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0)
{
DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
*recipient);
log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
*log_msgp);
}
+
+#ifdef TCP_CORK
+(void) setsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_CORK, US &on, sizeof(on));
+#endif
+
if (*user_msgp)
smtp_respond(US"221", 3, TRUE, *user_msgp);
else
smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
#endif
BOOL discarded = FALSE;
BOOL last_was_rej_mail = FALSE;
BOOL last_was_rcpt = FALSE;
-void *reset_point = store_get(0);
+rmark reset_point = store_mark();
DEBUG(D_receive) debug_printf("smtp_setup_msg entered\n");
TLS between messages (an Exim client may do this if it has messages queued up
for the host). Note: we do NOT reset AUTH at this point. */
-smtp_reset(reset_point);
+reset_point = smtp_reset(reset_point);
message_ended = END_NOTSTARTED;
-chunking_state = chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED;
+chunking_state = f.chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED;
cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
#endif
int start, end, sender_domain, recipient_domain;
int rc;
int c;
- auth_instance *au;
uschar *orcpt = NULL;
- int flags;
+ int dsn_flags;
gstring * g;
#ifdef AUTH_TLS
{
cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE;
- for (au = auths; au; au = au->next)
+ for (auth_instance * au = auths; au; au = au->next)
if (strcmpic(US"tls", au->driver_name) == 0)
{
if ( acl_smtp_auth
US &off, sizeof(off));
#endif
- switch(smtp_read_command(TRUE, GETC_BUFFER_UNLIMITED))
+ switch(smtp_read_command(
+#ifndef DISABLE_PIPE_CONNECT
+ !fl.pipe_connect_acceptable,
+#else
+ TRUE,
+#endif
+ GETC_BUFFER_UNLIMITED))
{
/* The AUTH command is not permitted to occur inside a transaction, and may
occur successfully only once per connection. Actually, that isn't quite
authentication_failed = TRUE;
cmd_list[CMD_LIST_AUTH].is_mail_cmd = FALSE;
- if (!auth_advertised && !allow_auth_unadvertised)
+ if (!fl.auth_advertised && !f.allow_auth_unadvertised)
{
done = synprot_error(L_smtp_protocol_error, 503, NULL,
US"AUTH command used when not advertised");
as a server and which has been advertised (unless, sigh, allow_auth_
unadvertised is set). */
- for (au = auths; au; au = au->next)
- if (strcmpic(s, au->public_name) == 0 && au->server &&
- (au->advertised || allow_auth_unadvertised))
- break;
-
- if (au)
{
- c = smtp_in_auth(au, &s, &ss);
+ auth_instance * au;
+ for (au = auths; au; au = au->next)
+ if (strcmpic(s, au->public_name) == 0 && au->server &&
+ (au->advertised || f.allow_auth_unadvertised))
+ break;
- smtp_printf("%s\r\n", FALSE, s);
- if (c != OK)
- log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
- au->name, host_and_ident(FALSE), ss);
+ if (au)
+ {
+ c = smtp_in_auth(au, &s, &ss);
+
+ smtp_printf("%s\r\n", FALSE, s);
+ if (c != OK)
+ log_write(0, LOG_MAIN|LOG_REJECT, "%s authenticator failed for %s: %s",
+ au->name, host_and_ident(FALSE), ss);
+ }
+ else
+ done = synprot_error(L_smtp_protocol_error, 504, NULL,
+ string_sprintf("%s authentication mechanism not supported", s));
}
- else
- done = synprot_error(L_smtp_protocol_error, 504, NULL,
- string_sprintf("%s authentication mechanism not supported", s));
break; /* AUTH_CMD */
case HELO_CMD:
HAD(SCH_HELO);
hello = US"HELO";
- esmtp = FALSE;
+ fl.esmtp = FALSE;
goto HELO_EHLO;
case EHLO_CMD:
HAD(SCH_EHLO);
hello = US"EHLO";
- esmtp = TRUE;
+ fl.esmtp = TRUE;
HELO_EHLO: /* Common code for HELO and EHLO */
cmd_list[CMD_LIST_HELO].is_mail_cmd = FALSE;
if (++synprot_error_count > smtp_max_synprot_errors)
{
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP call from %s dropped: too many "
- "syntax or protocol errors (last command was \"%s\")",
- host_and_ident(FALSE), string_printing(smtp_cmd_buffer));
+ "syntax or protocol errors (last command was \"%s\", %s)",
+ host_and_ident(FALSE), string_printing(smtp_cmd_buffer),
+ string_from_gstring(s_connhad_log(NULL))
+ );
done = 1;
}
is set, ensure that the HELO name matches the actual host. If helo_verify
is set, do the same check, but softly. */
- if (!sender_host_unknown)
+ if (!f.sender_host_unknown)
{
- BOOL old_helo_verified = helo_verified;
+ BOOL old_helo_verified = f.helo_verified;
uschar *p = smtp_cmd_data;
while (*p != 0 && !isspace(*p)) { *p = tolower(*p); p++; }
because otherwise the log can be confusing. */
if ( !sender_host_name
- && (deliver_domain = sender_helo_name, /* set $domain */
- match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0,
- &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL)) == OK)
+ && match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0,
+ &domainlist_anchor, NULL, MCL_DOMAIN, TRUE, NULL) == OK)
(void)host_name_lookup();
/* Rebuild the fullhost info to include the HELO name (and the real name
now obsolescent, since the verification can now be requested selectively
at ACL time. */
- helo_verified = helo_verify_failed = sender_helo_dnssec = FALSE;
- if (helo_required || helo_verify)
+ f.helo_verified = f.helo_verify_failed = sender_helo_dnssec = FALSE;
+ if (fl.helo_required || fl.helo_verify)
{
BOOL tempfail = !smtp_verify_helo();
- if (!helo_verified)
+ if (!f.helo_verified)
{
- if (helo_required)
+ if (fl.helo_required)
{
smtp_printf("%d %s argument does not match calling host\r\n", FALSE,
tempfail? 451 : 550, hello);
log_write(0, LOG_MAIN|LOG_REJECT, "%srejected \"%s %s\" from %s",
tempfail? "temporarily " : "",
hello, sender_helo_name, host_and_ident(FALSE));
- helo_verified = old_helo_verified;
+ f.helo_verified = old_helo_verified;
break; /* End of HELO/EHLO processing */
}
HDEBUG(D_all) debug_printf("%s verification failed but host is in "
#ifdef SUPPORT_SPF
/* set up SPF context */
- spf_init(sender_helo_name, sender_host_address);
+ spf_conn_init(sender_helo_name, sender_host_address);
#endif
/* Apply an ACL check if one is defined; afterwards, recheck
&user_msg, &log_msg)) != OK)
{
done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
- if (sender_helo_name)
- {
- store_free(sender_helo_name);
- sender_helo_name = NULL;
- }
+ sender_helo_name = NULL;
host_build_sender_fullhost(); /* Rebuild */
break;
}
- else if (!check_sync()) goto SYNC_FAILURE;
+#ifndef DISABLE_PIPE_CONNECT
+ else if (!fl.pipe_connect_acceptable && !check_sync())
+#else
+ else if (!check_sync())
+#endif
+ goto SYNC_FAILURE;
/* Generate an OK reply. The default string includes the ident if present,
and also the IP address if present. Reflecting back the ident is intended
some broken systems expect each response to be in a single packet, arrange
that the entire reply is sent in one write(). */
- auth_advertised = FALSE;
- smtp_in_pipelining_advertised = FALSE;
-#ifdef SUPPORT_TLS
- tls_advertised = FALSE;
-# ifdef EXPERIMENTAL_REQUIRETLS
- requiretls_advertised = FALSE;
-# endif
+ fl.auth_advertised = FALSE;
+ f.smtp_in_pipelining_advertised = FALSE;
+#ifndef DISABLE_TLS
+ fl.tls_advertised = FALSE;
#endif
- dsn_advertised = FALSE;
+ fl.dsn_advertised = FALSE;
#ifdef SUPPORT_I18N
- smtputf8_advertised = FALSE;
+ fl.smtputf8_advertised = FALSE;
#endif
smtp_code = US"250 "; /* Default response code plus space*/
if (!user_msg)
{
- s = string_sprintf("%.3s %s Hello %s%s%s",
+ /* sender_host_name below will be tainted, so save on copy when we hit it */
+ g = string_get_tainted(24, TRUE);
+ g = string_fmt_append(g, "%.3s %s Hello %s%s%s",
smtp_code,
smtp_active_hostname,
sender_ident ? sender_ident : US"",
sender_ident ? US" at " : US"",
sender_host_name ? sender_host_name : sender_helo_name);
- g = string_cat(NULL, s);
if (sender_host_address)
- {
- g = string_catn(g, US" [", 2);
- g = string_cat (g, sender_host_address);
- g = string_catn(g, US"]", 1);
- }
+ g = string_fmt_append(g, " [%s]", sender_host_address);
}
/* A user-supplied EHLO greeting may not contain more than one line. Note
/* If we received EHLO, we must create a multiline response which includes
the functions supported. */
- if (esmtp)
+ if (fl.esmtp)
{
g->s[3] = '-';
till then, VRFY and EXPN can be used after EHLO when space is short. */
if (thismessage_size_limit > 0)
- {
- sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code,
+ g = string_fmt_append(g, "%.3s-SIZE %d\r\n", smtp_code,
thismessage_size_limit);
- g = string_cat(g, big_buffer);
- }
else
{
g = string_catn(g, smtp_code, 3);
{
g = string_catn(g, smtp_code, 3);
g = string_catn(g, US"-DSN\r\n", 6);
- dsn_advertised = TRUE;
+ fl.dsn_advertised = TRUE;
}
/* Advertise ETRN/VRFY/EXPN if there's are ACL checking whether a host is
/* Exim is quite happy with pipelining, so let the other end know that
it is safe to use it, unless advertising is disabled. */
- if (pipelining_enable &&
- verify_check_host(&pipelining_advertise_hosts) == OK)
+ if ( f.pipelining_enable
+ && verify_check_host(&pipelining_advertise_hosts) == OK)
{
g = string_catn(g, smtp_code, 3);
g = string_catn(g, US"-PIPELINING\r\n", 13);
sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
- smtp_in_pipelining_advertised = TRUE;
+ f.smtp_in_pipelining_advertised = TRUE;
+
+#ifndef DISABLE_PIPE_CONNECT
+ if (fl.pipe_connect_acceptable)
+ {
+ f.smtp_in_early_pipe_advertised = TRUE;
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-" EARLY_PIPE_FEATURE_NAME "\r\n", EARLY_PIPE_FEATURE_LEN+3);
+ }
+#endif
}
&& verify_check_host(&auth_advertise_hosts) == OK
)
{
- auth_instance *au;
BOOL first = TRUE;
- for (au = auths; au; au = au->next)
+ for (auth_instance * au = auths; au; au = au->next)
{
au->advertised = FALSE;
if (au->server)
{
DEBUG(D_auth+D_expand) debug_printf_indent(
- "Evaluating advertise_condition for %s athenticator\n",
- au->public_name);
+ "Evaluating advertise_condition for %s %s athenticator\n",
+ au->name, au->public_name);
if ( !au->advertise_condition
|| expand_check_condition(au->advertise_condition, au->name,
US"authenticator")
g = string_catn(g, smtp_code, 3);
g = string_catn(g, US"-AUTH", 5);
first = FALSE;
- auth_advertised = TRUE;
+ fl.auth_advertised = TRUE;
}
saveptr = g->ptr;
g = string_catn(g, US" ", 1);
{
g = string_catn(g, smtp_code, 3);
g = string_catn(g, US"-CHUNKING\r\n", 11);
- chunking_offered = TRUE;
+ f.chunking_offered = TRUE;
chunking_state = CHUNKING_OFFERED;
}
tls_advertise_hosts. We must *not* advertise if we are already in a
secure connection. */
-#ifdef SUPPORT_TLS
+#ifndef DISABLE_TLS
if (tls_in.active.sock < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
{
g = string_catn(g, smtp_code, 3);
g = string_catn(g, US"-STARTTLS\r\n", 11);
- tls_advertised = TRUE;
- }
-
-# ifdef EXPERIMENTAL_REQUIRETLS
- /* Advertise REQUIRETLS only once we are in a secure connection */
- if ( tls_in.active.sock >= 0
- && verify_check_host(&tls_advertise_requiretls) != FAIL)
- {
- g = string_catn(g, smtp_code, 3);
- g = string_catn(g, US"-REQUIRETLS\r\n", 13);
- requiretls_advertised = TRUE;
+ fl.tls_advertised = TRUE;
}
-# endif
#endif
#ifndef DISABLE_PRDR
{
g = string_catn(g, smtp_code, 3);
g = string_catn(g, US"-SMTPUTF8\r\n", 11);
- smtputf8_advertised = TRUE;
+ fl.smtputf8_advertised = TRUE;
}
#endif
/* Terminate the string (for debug), write it, and note that HELO/EHLO
has been seen. */
-#ifdef SUPPORT_TLS
- if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else
+#ifndef DISABLE_TLS
+ if (tls_in.active.sock >= 0)
+ (void)tls_write(NULL, g->s, g->ptr,
+# ifndef DISABLE_PIPE_CONNECT
+ fl.pipe_connect_acceptable && pipeline_connect_sends());
+# else
+ FALSE);
+# endif
+ else
#endif
{
memmove(cr, cr + 1, (g->ptr--) - (cr - g->s));
debug_printf("SMTP>> %s", g->s);
}
- helo_seen = TRUE;
+ fl.helo_seen = TRUE;
/* Reset the protocol and the state, abandoning any previous message. */
received_protocol =
(sender_host_address ? protocols : protocols_local)
- [ (esmtp
+ [ (fl.esmtp
? pextend + (sender_host_authenticated ? pauthed : 0)
: pnormal)
+ (tls_in.active.sock >= 0 ? pcrpted : 0)
];
cancel_cutthrough_connection(TRUE, US"sent EHLO response");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
break; /* HELO/EHLO */
was_rej_mail = TRUE; /* Reset if accepted */
env_mail_type_t * mail_args; /* Sanity check & validate args */
- if (helo_required && !helo_seen)
+ if (fl.helo_required && !fl.helo_seen)
{
smtp_printf("503 HELO or EHLO required\r\n", FALSE);
log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no "
obviously need to throw away any previous data. */
cancel_cutthrough_connection(TRUE, US"MAIL received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
sender_data = recipient_data = NULL;
/* Loop, checking for ESMTP additions to the MAIL FROM command. */
- if (esmtp) for(;;)
+ if (fl.esmtp) for(;;)
{
uschar *name, *value, *end;
unsigned long int size;
is included only if configured in at build time. */
case ENV_MAIL_OPT_RET:
- if (dsn_advertised)
+ if (fl.dsn_advertised)
{
/* Check if RET has already been set */
if (dsn_ret > 0)
}
break;
case ENV_MAIL_OPT_ENVID:
- if (dsn_advertised)
+ if (fl.dsn_advertised)
{
/* Check if the dsn envid has been already set */
if (dsn_envid)
#ifdef SUPPORT_I18N
case ENV_MAIL_OPT_UTF8:
- if (!smtputf8_advertised)
+ if (!fl.smtputf8_advertised)
{
done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"SMTPUTF8 used when not advertised");
break;
#endif
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
- case ENV_MAIL_OPT_REQTLS:
- {
- uschar * r, * t;
-
- if (!requiretls_advertised)
- {
- done = synprot_error(L_smtp_syntax_error, 555, NULL,
- US"unadvertised MAIL option: REQUIRETLS");
- goto COMMAND_LOOP;
- }
-
- DEBUG(D_receive) debug_printf("requiretls requested\n");
- tls_requiretls = REQUIRETLS_MSG;
-
- r = string_copy_malloc(received_protocol);
- if ((t = Ustrrchr(r, 's'))) *t = 'S';
- received_protocol = r;
- }
- break;
-#endif
-
/* No valid option. Stick back the terminator characters and break
the loop. Do the name-terminator second as extract_option sets
value==name when it found no equal-sign.
if (arg_error) break;
}
-#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS)
- if (tls_requiretls & REQUIRETLS_MSG)
- {
- /* Ensure headers-only bounces whether a RET option was given or not. */
-
- DEBUG(D_receive) if (dsn_ret == dsn_ret_full)
- debug_printf("requiretls override: dsn_ret_full -> dsn_ret_hdrs\n");
- dsn_ret = dsn_ret_hdrs;
- }
-#endif
-
/* If we have passed the threshold for rate limiting, apply the current
delay, and update it for next time, provided this is a limited host. */
and EXPN etc. to be used when space is short. */
if (!receive_check_fs(
- (smtp_check_spool_space && message_size >= 0)?
- message_size + 5000 : 0))
+ smtp_check_spool_space && message_size >= 0
+ ? message_size + 5000 : 0))
{
smtp_printf("452 Space shortage, please try later\r\n", FALSE);
sender_address = NULL;
of the SMTP connection. */
if (!sender_domain && *sender_address)
- if (allow_unqualified_sender)
+ if (f.allow_unqualified_sender)
{
sender_domain = Ustrlen(sender_address) + 1;
sender_address = rewrite_address_qualify(sender_address, FALSE);
if (acl_smtp_mail)
{
rc = acl_check(ACL_WHERE_MAIL, NULL, acl_smtp_mail, &user_msg, &log_msg);
- if (rc == OK && !smtp_in_pipelining_advertised && !check_sync())
+ if (rc == OK && !f.smtp_in_pipelining_advertised && !check_sync())
goto SYNC_FAILURE;
}
else
smtp_user_msg(US"250", user_msg);
}
smtp_delay_rcpt = smtp_rlr_base;
- recipients_discarded = (rc == DISCARD);
+ f.recipients_discarded = (rc == DISCARD);
was_rej_mail = FALSE;
}
else
case RCPT_CMD:
HAD(SCH_RCPT);
rcpt_count++;
- was_rcpt = rcpt_in_progress = TRUE;
+ was_rcpt = fl.rcpt_in_progress = TRUE;
/* There must be a sender address; if the sender was rejected and
pipelining was advertised, we assume the client was pipelining, and do not
if (sender_address == NULL)
{
- if (smtp_in_pipelining_advertised && last_was_rej_mail)
+ if (f.smtp_in_pipelining_advertised && last_was_rej_mail)
{
smtp_printf("503 sender not yet given\r\n", FALSE);
was_rej_mail = TRUE;
/* Set the DSN flags orcpt and dsn_flags from the session*/
orcpt = NULL;
- flags = 0;
+ dsn_flags = 0;
- if (esmtp) for(;;)
+ if (fl.esmtp) for(;;)
{
uschar *name, *value;
if (!extract_option(&name, &value))
break;
- if (dsn_advertised && strcmpic(name, US"ORCPT") == 0)
+ if (fl.dsn_advertised && strcmpic(name, US"ORCPT") == 0)
{
/* Check whether orcpt has been already set */
if (orcpt)
DEBUG(D_receive) debug_printf("DSN orcpt: %s\n", orcpt);
}
- else if (dsn_advertised && strcmpic(name, US"NOTIFY") == 0)
+ else if (fl.dsn_advertised && strcmpic(name, US"NOTIFY") == 0)
{
/* Check if the notify flags have been already set */
- if (flags > 0)
+ if (dsn_flags > 0)
{
done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"NOTIFY can be specified once only");
goto COMMAND_LOOP;
}
if (strcmpic(value, US"NEVER") == 0)
- flags |= rf_notify_never;
+ dsn_flags |= rf_notify_never;
else
{
uschar *p = value;
if (strcmpic(p, US"SUCCESS") == 0)
{
DEBUG(D_receive) debug_printf("DSN: Setting notify success\n");
- flags |= rf_notify_success;
+ dsn_flags |= rf_notify_success;
}
else if (strcmpic(p, US"FAILURE") == 0)
{
DEBUG(D_receive) debug_printf("DSN: Setting notify failure\n");
- flags |= rf_notify_failure;
+ dsn_flags |= rf_notify_failure;
}
else if (strcmpic(p, US"DELAY") == 0)
{
DEBUG(D_receive) debug_printf("DSN: Setting notify delay\n");
- flags |= rf_notify_delay;
+ dsn_flags |= rf_notify_delay;
}
else
{
}
p = pp;
}
- DEBUG(D_receive) debug_printf("DSN Flags: %x\n", flags);
+ DEBUG(D_receive) debug_printf("DSN Flags: %x\n", dsn_flags);
}
}
there may be a delay in this, re-check for a synchronization error
afterwards, unless pipelining was advertised. */
- if (recipients_discarded)
+ if (f.recipients_discarded)
rc = DISCARD;
else
if ( (rc = acl_check(ACL_WHERE_RCPT, recipient, acl_smtp_rcpt, &user_msg,
&log_msg)) == OK
- && !smtp_in_pipelining_advertised && !check_sync())
+ && !f.smtp_in_pipelining_advertised && !check_sync())
goto SYNC_FAILURE;
/* The ACL was happy */
/* Set the dsn flags in the recipients_list */
recipients_list[recipients_count-1].orcpt = orcpt;
- recipients_list[recipients_count-1].dsn_flags = flags;
+ recipients_list[recipients_count-1].dsn_flags = dsn_flags;
- DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n",
+ /* DEBUG(D_receive) debug_printf("DSN: orcpt: %s flags: %d\n",
recipients_list[recipients_count-1].orcpt,
- recipients_list[recipients_count-1].dsn_flags);
+ recipients_list[recipients_count-1].dsn_flags); */
}
/* The recipient was discarded */
discarded = TRUE;
log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: "
"discarded by %s ACL%s%s", host_and_ident(TRUE),
- sender_address_unrewritten? sender_address_unrewritten : sender_address,
- smtp_cmd_argument, recipients_discarded? "MAIL" : "RCPT",
+ sender_address_unrewritten ? sender_address_unrewritten : sender_address,
+ smtp_cmd_argument, f.recipients_discarded ? "MAIL" : "RCPT",
log_msg ? US": " : US"", log_msg ? log_msg : US"");
}
receive_getc = bdat_getc;
receive_ungetc = bdat_ungetc;
- dot_ends = FALSE;
+ f.dot_ends = FALSE;
goto DATA_BDAT;
}
case DATA_CMD:
HAD(SCH_DATA);
- dot_ends = TRUE;
+ f.dot_ends = TRUE;
- DATA_BDAT: /* Common code for DATA and BDAT */
+ DATA_BDAT: /* Common code for DATA and BDAT */
+#ifndef DISABLE_PIPE_CONNECT
+ fl.pipe_connect_acceptable = FALSE;
+#endif
if (!discarded && recipients_count <= 0)
{
- if (rcpt_smtp_response_same && rcpt_smtp_response != NULL)
+ if (fl.rcpt_smtp_response_same && rcpt_smtp_response != NULL)
{
uschar *code = US"503";
int len = Ustrlen(rcpt_smtp_response);
rcpt_smtp_response[len-2] = 0;
smtp_respond(code, 3, FALSE, rcpt_smtp_response);
}
- if (smtp_in_pipelining_advertised && last_was_rcpt)
+ if (f.smtp_in_pipelining_advertised && last_was_rcpt)
smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE,
smtp_names[smtp_connection_had[smtp_ch_index-1]]);
else
else
{
uschar * acl = acl_smtp_predata ? acl_smtp_predata : US"accept";
- enable_dollar_recipients = TRUE;
+ f.enable_dollar_recipients = TRUE;
rc = acl_check(ACL_WHERE_PREDATA, NULL, acl, &user_msg,
&log_msg);
- enable_dollar_recipients = FALSE;
+ f.enable_dollar_recipients = FALSE;
if (rc == OK && !check_sync())
goto SYNC_FAILURE;
done = smtp_handle_acl_fail(ACL_WHERE_EXPN, rc, user_msg, log_msg);
else
{
- BOOL save_log_testing_mode = log_testing_mode;
- address_test_mode = log_testing_mode = TRUE;
+ BOOL save_log_testing_mode = f.log_testing_mode;
+ f.address_test_mode = f.log_testing_mode = TRUE;
(void) verify_address(deliver_make_addr(smtp_cmd_data, FALSE),
smtp_out, vopt_is_recipient | vopt_qualify | vopt_expn, -1, -1, -1,
NULL, NULL, NULL);
- address_test_mode = FALSE;
- log_testing_mode = save_log_testing_mode; /* true for -bh */
+ f.address_test_mode = FALSE;
+ f.log_testing_mode = save_log_testing_mode; /* true for -bh */
}
break;
- #ifdef SUPPORT_TLS
+ #ifndef DISABLE_TLS
case STARTTLS_CMD:
HAD(SCH_STARTTLS);
- if (!tls_advertised)
+ if (!fl.tls_advertised)
{
done = synprot_error(L_smtp_protocol_error, 503, NULL,
US"STARTTLS command used when not advertised");
incomplete_transaction_log(US"STARTTLS");
cancel_cutthrough_connection(TRUE, US"STARTTLS received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = FALSE;
if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK)
{
if (!tls_remember_esmtp)
- helo_seen = esmtp = auth_advertised = smtp_in_pipelining_advertised = FALSE;
+ fl.helo_seen = fl.esmtp = fl.auth_advertised = f.smtp_in_pipelining_advertised = FALSE;
cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
if (sender_helo_name)
{
- store_free(sender_helo_name);
sender_helo_name = NULL;
host_build_sender_fullhost(); /* Rebuild */
set_process_info("handling incoming TLS connection from %s",
}
received_protocol =
(sender_host_address ? protocols : protocols_local)
- [ (esmtp
+ [ (fl.esmtp
? pextend + (sender_host_authenticated ? pauthed : 0)
: pnormal)
+ (tls_in.active.sock >= 0 ? pcrpted : 0)
/* Hard failure. Reject everything except QUIT or closed connection. One
cause for failure is a nested STARTTLS, in which case tls_in.active remains
- set, but we must still reject all incoming commands. */
+ set, but we must still reject all incoming commands. Another is a handshake
+ failure - and there may some encrypted data still in the pipe to us, which we
+ see as garbage commands. */
DEBUG(D_tls) debug_printf("TLS failed to start\n");
while (done <= 0) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED))
case RSET_CMD:
smtp_rset_handler();
cancel_cutthrough_connection(TRUE, US"RSET received");
- smtp_reset(reset_point);
+ reset_point = smtp_reset(reset_point);
toomany = FALSE;
break;
{
uschar buffer[256];
buffer[0] = 0;
- Ustrcat(buffer, " AUTH");
- #ifdef SUPPORT_TLS
+ Ustrcat(buffer, US" AUTH");
+ #ifndef DISABLE_TLS
if (tls_in.active.sock < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
- Ustrcat(buffer, " STARTTLS");
+ Ustrcat(buffer, US" STARTTLS");
#endif
- Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA BDAT");
- Ustrcat(buffer, " NOOP QUIT RSET HELP");
- if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN");
- if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN");
- if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY");
+ Ustrcat(buffer, US" HELO EHLO MAIL RCPT DATA BDAT");
+ Ustrcat(buffer, US" NOOP QUIT RSET HELP");
+ if (acl_smtp_etrn) Ustrcat(buffer, US" ETRN");
+ if (acl_smtp_expn) Ustrcat(buffer, US" EXPN");
+ if (acl_smtp_vrfy) Ustrcat(buffer, US" VRFY");
smtp_printf("214%s\r\n", FALSE, buffer);
}
break;
if (sender_address || recipients_count > 0)
log_write(L_lost_incoming_connection, LOG_MAIN,
"unexpected %s while reading SMTP command from %s%s%s D=%s",
- sender_host_unknown ? "EOF" : "disconnection",
- tcp_in_fastopen && !tcp_in_fastopen_logged ? US"TFO " : US"",
+ f.sender_host_unknown ? "EOF" : "disconnection",
+ f.tcp_in_fastopen_logged
+ ? US""
+ : f.tcp_in_fastopen
+ ? f.tcp_in_fastopen_data ? US"TFO* " : US"TFO "
+ : US"",
host_and_ident(FALSE), smtp_read_error,
string_timesince(&smtp_connection_start)
);
else
log_write(L_smtp_connection, LOG_MAIN, "%s %slost%s D=%s",
smtp_get_connection_info(),
- tcp_in_fastopen && !tcp_in_fastopen_logged ? US"TFO " : US"",
+ f.tcp_in_fastopen && !f.tcp_in_fastopen_logged ? US"TFO " : US"",
smtp_read_error,
string_timesince(&smtp_connection_start)
);
oldsignal = signal(SIGCHLD, SIG_IGN);
- if ((pid = fork()) == 0)
+ if ((pid = exim_fork(US"etrn-command")) == 0)
{
smtp_input = FALSE; /* This process is not associated with the */
(void)fclose(smtp_in); /* SMTP call any more. */
/* If not serializing, do the exec right away. Otherwise, fork down
into another process. */
- if (!smtp_etrn_serialize || (pid = fork()) == 0)
+ if ( !smtp_etrn_serialize
+ || (pid = exim_fork(US"etrn-serialised-command")) == 0)
{
DEBUG(D_exec) debug_print_argv(argv);
exim_nullstd(); /* Ensure std{in,out,err} exist */
}
enq_end(etrn_serialize_key);
- _exit(EXIT_SUCCESS);
+ exim_underbar_exit(EXIT_SUCCESS);
}
/* Back in the top level SMTP process. Check that we started a subprocess
if (smtp_etrn_serialize) enq_end(etrn_serialize_key);
}
else
- {
- if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE);
- else smtp_user_msg(US"250", user_msg);
- }
+ if (!user_msg)
+ smtp_printf("250 OK\r\n", FALSE);
+ else
+ smtp_user_msg(US"250", user_msg);
signal(SIGCHLD, oldsignal);
break;
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error "
"(next input sent too soon: pipelining was%s advertised): "
"rejected \"%s\" %s next input=\"%s\"",
- smtp_in_pipelining_advertised ? "" : " not",
+ f.smtp_in_pipelining_advertised ? "" : " not",
smtp_cmd_buffer, host_and_ident(TRUE),
string_printing(smtp_inptr));
smtp_notquit_exit(US"synchronization-error", US"554",
g = string_append(g, 2, US";\n\tauth=pass (", sender_host_auth_pubname);
-if (Ustrcmp(sender_host_auth_pubname, "tls") != 0)
- g = string_append(g, 2, US") smtp.auth=", authenticated_id);
-else if (authenticated_id)
- g = string_append(g, 2, US") x509.auth=", authenticated_id);
+if (Ustrcmp(sender_host_auth_pubname, "tls") == 0)
+ g = authenticated_id
+ ? string_append(g, 2, US") x509.auth=", authenticated_id)
+ : string_cat(g, US") reason=x509.auth");
else
- g = string_catn(g, US") reason=x509.auth", 17);
+ g = authenticated_id
+ ? string_append(g, 2, US") smtp.auth=", authenticated_id)
+ : string_cat(g, US", no id saved)");
if (authenticated_sender)
g = string_append(g, 2, US" smtp.mailfrom=", authenticated_sender);