*************************************************/
/* 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. */
BOOL helo_verify :1;
BOOL helo_seen :1;
BOOL helo_accept_junk :1;
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
BOOL pipe_connect_acceptable :1;
#endif
BOOL rcpt_smtp_response_same :1;
/* This list of names is used for performing the smtp_no_mail logging action.
It must be kept in step with the SCH_xxx enumerations. */
-static uschar *smtp_names[] =
+uschar * smtp_names[] =
{
US"NONE", US"AUTH", US"DATA", US"BDAT", US"EHLO", US"ETRN", US"EXPN",
US"HELO", US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET",
{
int fd, rc;
fd_set fds;
-struct timeval tzero;
+struct timeval tzero = {.tv_sec = 0, .tv_usec = 0};
#ifndef DISABLE_TLS
if (tls_in.active.sock >= 0)
fd = fileno(smtp_in);
FD_ZERO(&fds);
FD_SET(fd, &fds);
-tzero.tv_sec = 0;
-tzero.tv_usec = 0;
rc = select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &tzero);
if (rc <= 0) return TRUE; /* Not ready to read */
}
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
static BOOL
pipeline_connect_sends(void)
{
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
{
#ifndef DISABLE_DKIM
int n = smtp_inend - smtp_inptr;
+if (chunking_state == CHUNKING_LAST && chunking_data_left < n)
+ n = chunking_data_left;
if (n > 0)
dkim_exim_verify_feed(smtp_inptr, n);
#endif
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
{
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
/* Now write the string */
+if (
#ifndef DISABLE_TLS
-if (tls_in.active.sock >= 0)
- {
- if (tls_write(NULL, gs.s, gs.ptr, more) < 0)
- smtp_write_error = -1;
- }
-else
+ tls_in.active.sock >= 0 ? (tls_write(NULL, gs.s, gs.ptr, more) < 0) :
#endif
-
-if (fprintf(smtp_out, "%s", gs.s) < 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;
}
#ifdef SUPPORT_PROXY
-/*************************************************
-* Restore socket timeout to previous value *
-*************************************************/
-/* If the previous value was successfully retrieved, restore
-it before returning control to the non-proxy routines
-
-Arguments: fd - File descriptor for input
- get_ok - Successfully retrieved previous values
- tvtmp - Time struct with previous values
- vslen - Length of time struct
-Returns: none
-*/
-static void
-restore_socket_timeout(int fd, int get_ok, struct timeval * tvtmp, socklen_t vslen)
-{
-if (get_ok == 0)
- (void) setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS tvtmp, vslen);
-}
-
/*************************************************
* Check if host is required proxy host *
*************************************************/
while (capacity > 0)
{
- do { ret = recv(fd, to, 1, 0); } while (ret == -1 && errno == EINTR);
+ do { ret = read(fd, to, 1); } while (ret == -1 && errno == EINTR && !had_command_timeout);
if (ret == -1)
return -1;
have++;
int fd = fileno(smtp_in);
const char v2sig[12] = "\x0D\x0A\x0D\x0A\x00\x0D\x0A\x51\x55\x49\x54\x0A";
uschar * iptype; /* To display debug info */
-struct timeval tv;
-struct timeval tvtmp;
socklen_t vslen = sizeof(struct timeval);
BOOL yield = FALSE;
-/* Save current socket timeout values */
-get_ok = getsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS &tvtmp, &vslen);
-
-/* Proxy Protocol host must send header within a short time
-(default 3 seconds) or it's considered invalid */
-tv.tv_sec = PROXY_NEGOTIATION_TIMEOUT_SEC;
-tv.tv_usec = PROXY_NEGOTIATION_TIMEOUT_USEC;
-if (setsockopt(fd, SOL_SOCKET, SO_RCVTIMEO, CS &tv, sizeof(tv)) < 0)
- goto bad;
+os_non_restarting_signal(SIGALRM, command_timeout_handler);
+ALARM(proxy_protocol_timeout);
do
{
don't do a PEEK into the data, actually slurp up enough to be
"safe". Can't take it all because TLS-on-connect clients follow
immediately with TLS handshake. */
- ret = recv(fd, &hdr, PROXY_INITIAL_READ, 0);
+ ret = read(fd, &hdr, PROXY_INITIAL_READ);
}
- while (ret == -1 && errno == EINTR);
+ while (ret == -1 && errno == EINTR && !had_command_timeout);
if (ret == -1)
goto proxyfail;
/* First get the length fields. */
do
{
- retmore = recv(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ, 0);
- } while (retmore == -1 && errno == EINTR);
+ retmore = read(fd, (uschar*)&hdr + ret, PROXY_V2_HEADER_SIZE - PROXY_INITIAL_READ);
+ } while (retmore == -1 && errno == EINTR && !had_command_timeout);
if (retmore == -1)
goto proxyfail;
ret += retmore;
{
do
{
- retmore = recv(fd, (uschar*)&hdr + ret, size-ret, 0);
- } while (retmore == -1 && errno == EINTR);
+ retmore = read(fd, (uschar*)&hdr + ret, size-ret);
+ } while (retmore == -1 && errno == EINTR && !had_command_timeout);
if (retmore == -1)
goto proxyfail;
ret += retmore;
should cause a synchronization failure */
proxyfail:
- restore_socket_timeout(fd, get_ok, &tvtmp, vslen);
+ DEBUG(D_receive) if (had_command_timeout)
+ debug_printf("Timeout while reading proxy header\n");
bad:
if (yield)
debug_printf("Failure to extract proxied host, only QUIT allowed\n");
}
+ALARM(0);
return;
}
#endif
return;
case QUIT_CMD:
+ f.smtp_in_quit = TRUE;
smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
mac_smtp_fflush();
return;
if (LOGGING(tls_cipher) && tls_in.cipher)
{
g = string_append(g, 2, US" X=", tls_in.cipher);
-#ifdef EXPERIMENTAL_TLS_RESUME
+#ifndef DISABLE_TLS_RESUME
if (LOGGING(tls_resumption) && tls_in.resumption & RESUME_USED)
g = string_catn(g, US"*", 1);
#endif
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)
+{
+const 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++, sep = US",")
+ g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
+return g;
+}
+
+
/*************************************************
* Log lack of MAIL if so configured *
*************************************************/
void
smtp_log_no_mail(void)
{
-uschar * sep, * s;
+uschar * s;
gstring * g = NULL;
if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail))
g = s_tlslog(g);
#endif
-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",";
- }
+g = s_connhad_log(g);
if (!(s = string_from_gstring(g))) s = US"";
raw_recipients_count = recipients_count = recipients_list_max = 0;
message_linecount = 0;
message_size = -1;
+message_body = message_body_end = NULL;
acl_added_headers = NULL;
acl_removed_headers = NULL;
f.queue_only_policy = FALSE;
rcpt_smtp_response = NULL;
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 */
+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
-f.no_mbox_unspool = FALSE; /* Can be set by ACL */
+f.no_mbox_unspool = FALSE; /* Can be set by ACL */
#endif
-f.submission_mode = FALSE; /* 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 */
+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 */
+recipient_verify_failure = NULL;
deliver_localpart_parent = deliver_localpart_orig = NULL;
deliver_domain_parent = deliver_domain_orig = NULL;
callout_address = NULL;
-submission_name = NULL; /* Can be set by ACL */
+submission_name = NULL; /* Can be set by ACL */
raw_sender = NULL; /* After SMTP rewrite, before qualifying */
sender_address_unrewritten = NULL; /* Set only after verify rewrite */
sender_verified_list = NULL; /* No senders verified */
dkim_verify_overall = dkim_verify_status = dkim_verify_reason = NULL;
dkim_key_length = 0;
#endif
-#ifdef EXPERIMENTAL_DMARC
+#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;
acl_var_m = NULL;
-/* The message body variables use malloc store. They may be set if this is
-not the first message in an SMTP session and the previous message caused them
-to be referenced in an ACL. */
-
-if (message_body)
- {
- store_free(message_body);
- message_body = NULL;
- }
-
-if (message_body_end)
- {
- store_free(message_body_end);
- message_body_end = NULL;
- }
-
-/* Warning log messages are also saved in malloc store. They are saved to avoid
+/* Warning log messages are saved in malloc store. They are saved to avoid
repetition in the same message, but it seems right to repeat them for different
messages. */
acl_warn_logged = acl_warn_logged->next;
store_free(this);
}
+
+message_tidyup();
store_reset(reset_point);
+
+message_start();
return store_mark();
}
case MAIL_CMD:
smtp_mailcmd_count++; /* Count for no-mail log */
- if (sender_address != NULL)
+ if (sender_address)
/* The function moan_smtp_batch() does not return. */
moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given");
break;
- case EOF_CMD:
case QUIT_CMD:
+ f.smtp_in_quit = TRUE;
+ case EOF_CMD:
done = 2;
break;
static void
tfo_in_check(void)
{
-# ifdef TCP_INFO
+# ifdef __FreeBSD__
+int is_fastopen;
+socklen_t len = sizeof(is_fastopen);
+
+/* 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)
+ {
+ 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 does not seem to have this yet */
+# 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("TCP_FASTOPEN mode connection (ACKd data-on-SYN)\n");
+ 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)
+# endif
+ if (tinfo.tcpi_state == TCP_SYN_RECV) /* Not seen on FreeBSD 12.1 */
{
- DEBUG(D_receive) debug_printf("TCP_FASTOPEN mode connection (state TCP_SYN_RECV)\n");
+ 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
authenticated_by = NULL;
#ifndef DISABLE_TLS
-tls_in.cipher = tls_in.peerdn = NULL;
+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;
if (smtp_batched_input) return TRUE;
/* If valid Proxy Protocol source is connecting, set up session.
- * Failure will not allow any SMTP function other than QUIT. */
+Failure will not allow any SMTP function other than QUIT. */
#ifdef SUPPORT_PROXY
proxy_session = FALSE;
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. */
+/* Start up TLS if tls_on_connect is set. This is for supporting the legacy
+smtps port for use with older style SSL MTAs. */
#ifndef DISABLE_TLS
- if (tls_in.on_connect)
- {
- if (tls_server_start(tls_require_ciphers, &user_msg) != OK)
- return smtp_log_tls_fail(user_msg);
- cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
- }
+if (tls_in.on_connect)
+ {
+ if (tls_server_start(&user_msg) != OK)
+ return smtp_log_tls_fail(user_msg);
+ cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
+ }
#endif
/* Run the connect ACL if it exists */
/* Before we write the banner, check that there is no input pending, unless
this synchronisation check is disabled. */
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
fl.pipe_connect_acceptable =
sender_host_address && verify_check_host(&pipe_connect_advertise_hosts) == OK;
#endif
{
unsigned n = smtp_inend - smtp_inptr;
- if (n > 32) n = 32;
+ if (n > 128) n = 128;
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol "
"synchronization error (input sent without waiting for greeting): "
/*XXX the ehlo-resp code does its own tls/nontls bit. Maybe subroutine that? */
smtp_printf("%s",
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
fl.pipe_connect_acceptable && pipeline_connect_sends(),
#else
FALSE,
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)
{
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);
}
}
}
uschar *smtp_code;
uschar *lognl;
uschar *sender_info = US"";
-uschar *what =
-#ifdef WITH_CONTENT_SCAN
- where == ACL_WHERE_MIME ? US"during MIME ACL checks" :
-#endif
- where == ACL_WHERE_PREDATA ? US"DATA" :
- where == ACL_WHERE_DATA ? US"after DATA" :
-#ifndef DISABLE_PRDR
- where == ACL_WHERE_PRDR ? US"after DATA PRDR" :
-#endif
- smtp_cmd_data ?
- string_sprintf("%s %s", acl_wherenames[where], smtp_cmd_data) :
- string_sprintf("%s in \"connect\" ACL", acl_wherenames[where]);
+uschar *what;
if (drop) rc = FAIL;
this is what should be logged, so I've changed to logging the unrewritten
address to retain backward compatibility. */
-#ifndef WITH_CONTENT_SCAN
-if (where == ACL_WHERE_RCPT || where == ACL_WHERE_DATA)
-#else
-if (where == ACL_WHERE_RCPT || where == ACL_WHERE_DATA || where == ACL_WHERE_MIME)
+switch (where)
+ {
+#ifdef WITH_CONTENT_SCAN
+ case ACL_WHERE_MIME: what = US"during MIME ACL checks"; break;
+#endif
+ case ACL_WHERE_PREDATA: what = US"DATA"; break;
+ case ACL_WHERE_DATA: what = US"after DATA"; break;
+#ifndef DISABLE_PRDR
+ case ACL_WHERE_PRDR: what = US"after DATA PRDR"; break;
#endif
+ default:
+ {
+ uschar * place = smtp_cmd_data ? smtp_cmd_data : US"in \"connect\" ACL";
+ int lim = 100;
+
+ if (where == ACL_WHERE_AUTH) /* avoid logging auth creds */
+ {
+ uschar * s;
+ for (s = smtp_cmd_data; *s && !isspace(*s); ) s++;
+ lim = s - smtp_cmd_data; /* atop after method */
+ }
+ what = string_sprintf("%s %.*s", acl_wherenames[where], lim, place);
+ }
+ }
+switch (where)
{
- sender_info = string_sprintf("F=<%s>%s%s%s%s ",
- sender_address_unrewritten ? sender_address_unrewritten : sender_address,
- sender_host_authenticated ? US" A=" : US"",
- sender_host_authenticated ? sender_host_authenticated : US"",
- sender_host_authenticated && authenticated_id ? US":" : US"",
- sender_host_authenticated && authenticated_id ? authenticated_id : US""
- );
+ case ACL_WHERE_RCPT:
+ case ACL_WHERE_DATA:
+#ifdef WITH_CONTENT_SCAN
+ case ACL_WHERE_MIME:
+#endif
+ sender_info = string_sprintf("F=<%s>%s%s%s%s ",
+ sender_address_unrewritten ? sender_address_unrewritten : sender_address,
+ sender_host_authenticated ? US" A=" : US"",
+ sender_host_authenticated ? sender_host_authenticated : US"",
+ sender_host_authenticated && authenticated_id ? US":" : US"",
+ sender_host_authenticated && authenticated_id ? authenticated_id : US""
+ );
+ break;
}
/* If there's been a sender verification failure with a specific message, and
switch(rc)
{
case OK:
- 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;
- }
+ 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? */
- /* Authentication succeeded, but we failed to expand the set_id string.
- Treat this as a temporary error. */
+ 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;
+ }
- auth_defer_msg = expand_string_message;
- /* Fall through */
+ /* 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 */
case DEFER:
- 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;
+ 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_perm(set_id, TRUE);
- *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_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;
+ 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;
smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp)
{
HAD(SCH_QUIT);
+f.smtp_in_quit = TRUE;
incomplete_transaction_log(US"QUIT");
-if (acl_smtp_quit)
- {
- int rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp);
- if (rc == ERROR)
+if ( acl_smtp_quit
+ && acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp)
+ == ERROR)
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));
+#ifdef EXIM_TCP_CORK
+(void) setsockopt(fileno(smtp_out), IPPROTO_TCP, EXIM_TCP_CORK, US &on, sizeof(on));
#endif
if (*user_msgp)
smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
#ifndef DISABLE_TLS
-tls_close(NULL, TLS_SHUTDOWN_NOWAIT);
+tls_close(NULL, TLS_SHUTDOWN_WAIT);
#endif
log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
smtp_get_connection_info());
+
+/* Pause, hoping client will FIN first so that they get the TIME_WAIT.
+The socket should become readble (though with no data) */
+
+ {
+ int fd = fileno(smtp_in);
+ fd_set fds;
+ struct timeval t_limit = {.tv_sec = 0, .tv_usec = 200*1000};
+
+ FD_ZERO(&fds);
+ FD_SET(fd, &fds);
+ (void) select(fd + 1, (SELECT_ARG2_TYPE *)&fds, NULL, NULL, &t_limit);
+ }
}
if (smtp_batched_input) return smtp_setup_batch_msg();
+#ifdef TCP_QUICKACK
+if (smtp_in) /* Avoid pure-ACKs while in cmd pingpong phase */
+ (void) setsockopt(fileno(smtp_in), IPPROTO_TCP, TCP_QUICKACK,
+ US &off, sizeof(off));
+#endif
+
/* 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. */
}
#endif
-#ifdef TCP_QUICKACK
- if (smtp_in) /* Avoid pure-ACKs while in cmd pingpong phase */
- (void) setsockopt(fileno(smtp_in), IPPROTO_TCP, TCP_QUICKACK,
- US &off, sizeof(off));
-#endif
-
switch(smtp_read_command(
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
!fl.pipe_connect_acceptable,
#else
TRUE,
/* Find the name of the requested authentication mechanism. */
s = smtp_cmd_data;
- while ((c = *smtp_cmd_data) != 0 && !isspace(c))
- {
+ for (; (c = *smtp_cmd_data) && !isspace(c); smtp_cmd_data++)
if (!isalnum(c) && c != '-' && c != '_')
{
done = synprot_error(L_smtp_syntax_error, 501, NULL,
US"invalid character in authentication mechanism name");
goto COMMAND_LOOP;
}
- smtp_cmd_data++;
- }
/* If not at the end of the line, we must be at white space. Terminate the
name and move the pointer on to any data that may be present. */
- if (*smtp_cmd_data != 0)
+ if (*smtp_cmd_data)
{
*smtp_cmd_data++ = 0;
while (isspace(*smtp_cmd_data)) smtp_cmd_data++;
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;
}
host_build_sender_fullhost(); /* Rebuild */
break;
}
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
else if (!fl.pipe_connect_acceptable && !check_sync())
#else
else if (!check_sync())
sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
f.smtp_in_pipelining_advertised = TRUE;
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
if (fl.pipe_connect_acceptable)
{
f.smtp_in_early_pipe_advertised = TRUE;
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")
#ifndef DISABLE_TLS
if (tls_in.active.sock >= 0)
(void)tls_write(NULL, g->s, g->ptr,
-# ifdef SUPPORT_PIPE_CONNECT
+# ifndef DISABLE_PIPE_CONNECT
fl.pipe_connect_acceptable && pipeline_connect_sends());
# else
FALSE);
# endif
else
#endif
+ (void) fwrite(g->s, 1, g->ptr, smtp_out);
- {
- int i = fwrite(g->s, 1, g->ptr, smtp_out); i = i; /* compiler quietening */
- }
DEBUG(D_receive)
{
uschar *cr;
case MAIL_CMD:
HAD(SCH_MAIL);
smtp_mailcmd_count++; /* Count for limit and ratelimit */
+ message_start();
was_rej_mail = TRUE; /* Reset if accepted */
env_mail_type_t * mail_args; /* Sanity check & validate args */
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;
recipients_list[recipients_count-1].orcpt = orcpt;
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, f.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"");
}
f.dot_ends = TRUE;
DATA_BDAT: /* Common code for DATA and BDAT */
-#ifdef SUPPORT_PIPE_CONNECT
+#ifndef DISABLE_PIPE_CONNECT
fl.pipe_connect_acceptable = FALSE;
#endif
if (!discarded && recipients_count <= 0)
}
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]]);
+ smtp_names[smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)]]);
else
done = synprot_error(L_smtp_protocol_error, 503, NULL,
- smtp_connection_had[smtp_ch_index-1] == SCH_DATA
+ smtp_connection_had[SMTP_HBUFF_PREV(smtp_ch_index)] == SCH_DATA
? US"valid RCPT command must precede DATA"
: US"valid RCPT command must precede BDAT");
STARTTLS that don't add to the nonmail command count. */
s = NULL;
- if ((rc = tls_server_start(tls_require_ciphers, &s)) == OK)
+ if ((rc = tls_server_start(&s)) == OK)
{
if (!tls_remember_esmtp)
fl.helo_seen = fl.esmtp = fl.auth_advertised = f.smtp_in_pipelining_advertised = FALSE;
some sense is perhaps "right". */
case QUIT_CMD:
+ f.smtp_in_quit = TRUE;
user_msg = NULL;
if ( acl_smtp_quit
&& ((rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg,
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 */
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);