X-Git-Url: https://git.exim.org/exim.git/blobdiff_plain/13c7874e0a41d696ecf55774d62ea7d11778414f..4e48d56c083d2f763a5978e1dbf515b12dc12f96:/src/src/smtp_in.c diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c index cb6469811..1e478a6ac 100644 --- a/src/src/smtp_in.c +++ b/src/src/smtp_in.c @@ -133,11 +133,8 @@ to the circular buffer that holds a list of the last n received. */ static struct { BOOL auth_advertised :1; -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS BOOL tls_advertised :1; -# ifdef EXPERIMENTAL_REQUIRETLS - BOOL requiretls_advertised :1; -# endif #endif BOOL dsn_advertised :1; BOOL esmtp :1; @@ -145,6 +142,9 @@ static struct { BOOL helo_verify :1; BOOL helo_seen :1; BOOL helo_accept_junk :1; +#ifdef SUPPORT_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; @@ -194,7 +194,7 @@ static smtp_cmd_list cmd_list[] = { { "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 @@ -264,9 +264,6 @@ enum { 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 { @@ -286,10 +283,6 @@ static env_mail_type_t env_mail_type_list[] = { { 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 }, @@ -355,7 +348,7 @@ int fd, rc; fd_set fds; struct timeval tzero; -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS if (tls_in.active.sock >= 0) return !tls_could_read(); #endif @@ -404,6 +397,18 @@ return TRUE; } +#ifdef SUPPORT_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 * @@ -420,17 +425,16 @@ Returns: nothing 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++) + for (int i = 0; i < recipients_count; i++) raw_recipients[i] = recipients_list[i].address; raw_recipients_count = recipients_count; } @@ -499,14 +503,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 @@ -898,18 +902,20 @@ call another vararg function, only a function which accepts a va_list. */ 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); } @@ -939,16 +945,16 @@ if (fl.rcpt_in_progress) /* Now write the string */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_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; } @@ -1564,7 +1570,6 @@ smtp_read_command(BOOL check_sync, unsigned buffer_lim) { int c; int ptr = 0; -smtp_cmd_list *p; BOOL hadnull = FALSE; had_command_timeout = 0; @@ -1609,7 +1614,7 @@ if (hadnull) return BADCHAR_CMD; 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 */ @@ -1760,7 +1765,7 @@ if (f.sender_host_unknown || f.sender_host_notsocket) 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); @@ -1769,7 +1774,7 @@ return string_sprintf("SMTP connection from %s", hostname); -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS /* Append TLS-related information to a log line Arguments: @@ -1781,7 +1786,13 @@ static gstring * 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) @@ -1807,7 +1818,6 @@ Returns: nothing void smtp_log_no_mail(void) { -int i; uschar * sep, * s; gstring * g = NULL; @@ -1820,20 +1830,20 @@ if (sender_host_authenticated) 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++) +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 (i = 0; i < smtp_ch_index; i++) +for (int i = 0; i < smtp_ch_index; i++) { g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]); sep = US","; @@ -1842,7 +1852,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); } @@ -1852,15 +1862,14 @@ log_write(0, LOG_MAIN, "no MAIL in %sSMTP connection from %s D=%s%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); @@ -2067,7 +2076,6 @@ 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 f.dmarc_has_been_checked = f.dmarc_disable_verify = f.dmarc_enable_forensic = FALSE; @@ -2367,7 +2375,7 @@ return done - 2; /* Convert yield values */ -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS static BOOL smtp_log_tls_fail(uschar * errstr) { @@ -2392,13 +2400,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 @@ -2451,15 +2466,12 @@ if (!host_checking && !f.sender_host_notsocket) sender_host_auth_pubname = sender_host_authenticated = NULL; authenticated_by = NULL; -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS tls_in.cipher = tls_in.peerdn = NULL; tls_in.ourcert = tls_in.peercert = NULL; tls_in.sni = NULL; tls_in.ocsp = OCSP_NOT_REQ; fl.tls_advertised = FALSE; -# ifdef EXPERIMENTAL_REQUIRETLS -fl.requiretls_advertised = FALSE; -# endif #endif fl.dsn_advertised = FALSE; #ifdef SUPPORT_I18N @@ -2626,7 +2638,7 @@ if (!f.sender_host_unknown) { uschar *p = big_buffer; uschar *pend = big_buffer + big_buffer_size; - uschar *opt, *adptr; + uschar *adptr; int optcount; struct in_addr addr; @@ -2643,9 +2655,7 @@ if (!f.sender_host_unknown) 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: @@ -2693,18 +2703,16 @@ if (!f.sender_host_unknown) 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); @@ -2895,7 +2903,7 @@ if (check_proxy_protocol_host()) /* 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) @@ -2987,22 +2995,39 @@ while (*p); /* Before we write the banner, check that there is no input pending, unless this synchronisation check is disabled. */ +#ifdef SUPPORT_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 SUPPORT_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. */ @@ -3379,7 +3404,7 @@ is closing if required and return 2. */ 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""; @@ -3444,7 +3469,7 @@ int rc; uschar *user_msg = NULL; uschar *log_msg = NULL; -/* Check for recursive acll */ +/* Check for recursive call */ if (fl.smtp_exit_function_called) { @@ -3465,10 +3490,11 @@ 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 -string, even if it is incomplete. */ +responses are all internal, they should be reasonable size. */ if (code && defaultrespond) { @@ -3476,13 +3502,13 @@ 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(); } @@ -3587,33 +3613,25 @@ else 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) { 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; } } @@ -3656,7 +3674,7 @@ static int 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 variables as empty. Initialize $0 empty and set @@ -3670,14 +3688,14 @@ userid. On success, require set_id to expand and exist, and put it in 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 */ +for (int i = 0; i < AUTH_VARS; i++) auth_vars[i] = NULL; /* Reset $auth */ /* 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 @@ -3797,12 +3815,17 @@ if (acl_smtp_quit) 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 @@ -3873,7 +3896,7 @@ 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 @@ -3907,9 +3930,8 @@ while (done <= 0) 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 @@ -3922,7 +3944,7 @@ while (done <= 0) { 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 @@ -3950,7 +3972,13 @@ while (done <= 0) US &off, sizeof(off)); #endif - switch(smtp_read_command(TRUE, GETC_BUFFER_UNLIMITED)) + switch(smtp_read_command( +#ifdef SUPPORT_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 @@ -4028,23 +4056,26 @@ while (done <= 0) 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 || f.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 */ @@ -4121,9 +4152,8 @@ while (done <= 0) 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 @@ -4183,7 +4213,12 @@ while (done <= 0) host_build_sender_fullhost(); /* Rebuild */ break; } - else if (!check_sync()) goto SYNC_FAILURE; +#ifdef SUPPORT_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 @@ -4193,11 +4228,8 @@ while (done <= 0) fl.auth_advertised = FALSE; f.smtp_in_pipelining_advertised = FALSE; -#ifdef SUPPORT_TLS +#ifndef DISABLE_TLS fl.tls_advertised = FALSE; -# ifdef EXPERIMENTAL_REQUIRETLS - fl.requiretls_advertised = FALSE; -# endif #endif fl.dsn_advertised = FALSE; #ifdef SUPPORT_I18N @@ -4207,20 +4239,15 @@ while (done <= 0) 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 @@ -4258,11 +4285,8 @@ while (done <= 0) 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); @@ -4312,13 +4336,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 SUPPORT_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 } @@ -4339,9 +4372,8 @@ while (done <= 0) && 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) @@ -4389,7 +4421,7 @@ while (done <= 0) 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) { @@ -4397,17 +4429,6 @@ while (done <= 0) g = string_catn(g, US"-STARTTLS\r\n", 11); fl.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); - fl.requiretls_advertised = TRUE; - } -# endif #endif #ifndef DISABLE_PRDR @@ -4438,8 +4459,15 @@ while (done <= 0) /* 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, +# ifdef SUPPORT_PIPE_CONNECT + fl.pipe_connect_acceptable && pipeline_connect_sends()); +# else + FALSE); +# endif + else #endif { @@ -4725,28 +4753,6 @@ while (done <= 0) break; #endif -#if defined(SUPPORT_TLS) && defined(EXPERIMENTAL_REQUIRETLS) - case ENV_MAIL_OPT_REQTLS: - { - uschar * r, * t; - - if (!fl.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. @@ -4764,17 +4770,6 @@ while (done <= 0) 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. */ @@ -4962,7 +4957,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(;;) { @@ -4987,14 +4982,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; @@ -5006,17 +5001,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 { @@ -5027,7 +5022,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); } } @@ -5145,7 +5140,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, @@ -5244,7 +5239,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 SUPPORT_PIPE_CONNECT + fl.pipe_connect_acceptable = FALSE; +#endif if (!discarded && recipients_count <= 0) { if (fl.rcpt_smtp_response_same && rcpt_smtp_response != NULL) @@ -5397,7 +5395,7 @@ while (done <= 0) break; - #ifdef SUPPORT_TLS + #ifndef DISABLE_TLS case STARTTLS_CMD: HAD(SCH_STARTTLS); @@ -5508,7 +5506,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)) @@ -5586,7 +5586,7 @@ while (done <= 0) uschar buffer[256]; buffer[0] = 0; Ustrcat(buffer, " AUTH"); - #ifdef SUPPORT_TLS + #ifndef DISABLE_TLS if (tls_in.active.sock < 0 && verify_check_host(&tls_advertise_hosts) != FAIL) Ustrcat(buffer, " STARTTLS"); @@ -5614,7 +5614,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) );