BOOL helo_verify :1;
BOOL helo_seen :1;
BOOL helo_accept_junk :1;
+#ifdef EXPERIMENTAL_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 EXPERIMENTAL_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 *
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
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);
+yield = !! string_vformat(&gs, FALSE, 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);
}
#ifdef SUPPORT_TLS
if (tls_in.active.sock >= 0)
{
- if (tls_write(NULL, big_buffer, Ustrlen(big_buffer), more) < 0)
+ if (tls_write(NULL, gs.s, gs.ptr, more) < 0)
smtp_write_error = -1;
}
else
#endif
-if (fprintf(smtp_out, "%s", big_buffer) < 0) smtp_write_error = -1;
+if (fprintf(smtp_out, "%s", gs.s) < 0) smtp_write_error = -1;
}
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);
if (!(s = string_from_gstring(g))) s = US"";
log_write(0, LOG_MAIN, "no MAIL in %sSMTP connection from %s D=%s%s",
- f.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);
}
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
f.dmarc_has_been_checked = f.dmarc_disable_verify = f.dmarc_enable_forensic = FALSE;
+#ifdef SUPPORT_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
struct tcp_info tinfo;
socklen_t len = sizeof(tinfo);
-if ( getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0
- && tinfo.tcpi_state == TCP_SYN_RECV
- )
- {
- DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
- f.tcp_in_fastopen = TRUE;
- }
+if (getsockopt(fileno(smtp_out), IPPROTO_TCP, TCP_INFO, &tinfo, &len) == 0)
+#ifdef TCPI_OPT_SYN_DATA /* FreeBSD 11 does not seem to have this yet */
+ if (tinfo.tcpi_options & TCPI_OPT_SYN_DATA)
+ {
+ DEBUG(D_receive) debug_printf("TCP_FASTOPEN 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)
+ {
+ DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
+ f.tcp_in_fastopen = TRUE;
+ }
# endif
}
#endif
/* Before we write the banner, check that there is no input pending, unless
this synchronisation check is disabled. */
+#ifdef EXPERIMENTAL_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 > 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;
+ }
/* 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",
+#ifdef EXPERIMENTAL_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. */
uschar *user_msg = NULL;
uschar *log_msg = NULL;
-/* Check for recursive acll */
+/* Check for recursive call */
if (fl.smtp_exit_function_called)
{
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, TRUE, CS defaultrespond, ap);
va_end(ap);
+ smtp_printf("%s %s\r\n", FALSE, code, string_from_gstring(g));
}
mac_smtp_fflush();
}
int c;
auth_instance *au;
uschar *orcpt = NULL;
- int flags;
+ int dsn_flags;
gstring * g;
#ifdef AUTH_TLS
US &off, sizeof(off));
#endif
- switch(smtp_read_command(TRUE, GETC_BUFFER_UNLIMITED))
+ switch(smtp_read_command(
+#ifdef EXPERIMENTAL_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
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
host_build_sender_fullhost(); /* Rebuild */
break;
}
- else if (!check_sync()) goto SYNC_FAILURE;
+#ifdef EXPERIMENTAL_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
smtp_code = US"250 "; /* Default response code plus space*/
if (!user_msg)
{
- s = string_sprintf("%.3s %s Hello %s%s%s",
+ g = string_fmt_append(NULL, "%.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
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);
/* Exim is quite happy with pipelining, so let the other end know that
it is safe to use it, unless advertising is disabled. */
- if (f.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;
f.smtp_in_pipelining_advertised = TRUE;
+
+#ifdef EXPERIMENTAL_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
}
has been seen. */
#ifdef SUPPORT_TLS
- if (tls_in.active.sock >= 0) (void)tls_write(NULL, g->s, g->ptr, FALSE); else
+ if (tls_in.active.sock >= 0)
+ (void)tls_write(NULL, g->s, g->ptr,
+# ifdef EXPERIMENTAL_PIPE_CONNECT
+ fl.pipe_connect_acceptable && pipeline_connect_sends());
+# else
+ FALSE);
+# endif
+ else
#endif
{
/* Set the DSN flags orcpt and dsn_flags from the session*/
orcpt = NULL;
- flags = 0;
+ dsn_flags = 0;
if (fl.esmtp) for(;;)
{
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);
}
}
/* 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",
recipients_list[recipients_count-1].orcpt,
HAD(SCH_DATA);
f.dot_ends = TRUE;
- DATA_BDAT: /* Common code for DATA and BDAT */
+ DATA_BDAT: /* Common code for DATA and BDAT */
+#ifdef EXPERIMENTAL_PIPE_CONNECT
+ fl.pipe_connect_acceptable = FALSE;
+#endif
if (!discarded && recipients_count <= 0)
{
if (fl.rcpt_smtp_response_same && rcpt_smtp_response != NULL)
/* 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))
log_write(L_lost_incoming_connection, LOG_MAIN,
"unexpected %s while reading SMTP command from %s%s%s D=%s",
f.sender_host_unknown ? "EOF" : "disconnection",
- f.tcp_in_fastopen && !f.tcp_in_fastopen_logged ? US"TFO " : US"",
+ 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)
);