X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/88752192a16ea6f8f8bf4d3b3f801e20d49e5398..dbcb6d0acbbd69b8a68ba117530e00300ec698ba:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index 55272a99a..2e3c9b9ec 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -145,6 +145,9 @@ static struct { 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; @@ -404,6 +407,18 @@ return TRUE; } +#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 * @@ -499,14 +514,14 @@ smtp_refill(unsigned lim) 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 @@ -1842,7 +1857,7 @@ for (i = 0; i < smtp_ch_index; i++) 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); } @@ -2367,6 +2382,7 @@ return done - 2; /* Convert yield values */ +#ifdef SUPPORT_TLS static BOOL smtp_log_tls_fail(uschar * errstr) { @@ -2378,6 +2394,7 @@ if (Ustrncmp(conn_info, US"SMTP ", 5) == 0) conn_info += 5; log_write(0, LOG_MAIN, "TLS error on %s %s", conn_info, errstr); return FALSE; } +#endif @@ -2390,13 +2407,20 @@ tfo_in_check(void) 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 @@ -2985,22 +3009,39 @@ while (*p); /* 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. */ @@ -3463,6 +3504,9 @@ if (acl_smtp_notquit && reason) 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 @@ -3907,7 +3951,7 @@ while (done <= 0) int c; auth_instance *au; uschar *orcpt = NULL; - int flags; + int dsn_flags; gstring * g; #ifdef AUTH_TLS @@ -3948,7 +3992,13 @@ while (done <= 0) 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 @@ -4181,7 +4231,12 @@ while (done <= 0) 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 @@ -4310,13 +4365,22 @@ while (done <= 0) /* 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 } @@ -4437,7 +4501,14 @@ while (done <= 0) 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 { @@ -4960,7 +5031,7 @@ while (done <= 0) /* Set the DSN flags orcpt and dsn_flags from the session*/ orcpt = NULL; - flags = 0; + dsn_flags = 0; if (fl.esmtp) for(;;) { @@ -4985,14 +5056,14 @@ while (done <= 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; @@ -5004,17 +5075,17 @@ while (done <= 0) 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 { @@ -5025,7 +5096,7 @@ while (done <= 0) } p = pp; } - DEBUG(D_receive) debug_printf("DSN Flags: %x\n", flags); + DEBUG(D_receive) debug_printf("DSN Flags: %x\n", dsn_flags); } } @@ -5143,7 +5214,7 @@ while (done <= 0) /* 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, @@ -5242,7 +5313,10 @@ while (done <= 0) 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) @@ -5506,7 +5580,9 @@ while (done <= 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)) @@ -5612,7 +5688,11 @@ while (done <= 0) 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) );