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 *
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);
}
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. */
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
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();
}
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
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
{
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)
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)
);