* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2017 */
+/* Copyright (c) University of Cambridge 1995 - 2018 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for handling an incoming SMTP call. */
{ "auth", sizeof("auth")-1, AUTH_CMD, TRUE, TRUE },
#ifdef SUPPORT_TLS
{ "starttls", sizeof("starttls")-1, STARTTLS_CMD, FALSE, FALSE },
- { "tls_auth", 0, TLS_AUTH_CMD, FALSE, TRUE },
+ { "tls_auth", 0, TLS_AUTH_CMD, FALSE, FALSE },
#endif
/* If you change anything above here, also fix the definitions below. */
*************************************************/
/* Synchronization checks can never be perfect because a packet may be on its
-way but not arrived when the check is done. Such checks can in any case only be
-done when TLS is not in use. Normally, the checks happen when commands are
-read: Exim ensures that there is no more input in the input buffer. In normal
-cases, the response to the command will be fast, and there is no further check.
+way but not arrived when the check is done. Normally, the checks happen when
+commands are read: Exim ensures that there is no more input in the input buffer.
+In normal cases, the response to the command will be fast, and there is no
+further check.
However, for some commands an ACL is run, and that can include delays. In those
cases, it is useful to do another check on the input just before sending the
*/
static BOOL
-check_sync(void)
+wouldblock_reading(void)
{
int fd, rc;
fd_set fds;
struct timeval tzero;
-if (!smtp_enforce_sync || sender_host_address == NULL ||
- sender_host_notsocket || tls_in.active >= 0)
- return TRUE;
+#ifdef SUPPORT_TLS
+if (tls_in.active >= 0)
+ return !tls_could_read();
+#endif
if (smtp_inptr < smtp_inend)
return FALSE;
if (rc < 0) return TRUE; /* End of file or error */
smtp_ungetc(rc);
-rc = smtp_inend - smtp_inptr;
-if (rc > 150) rc = 150;
-smtp_inptr[rc] = 0;
return FALSE;
}
+static BOOL
+check_sync(void)
+{
+if (!smtp_enforce_sync || !sender_host_address || sender_host_notsocket)
+ return TRUE;
+
+return wouldblock_reading();
+}
+
+
+/* If there's input waiting (and we're doing pipelineing) then we can pipeline
+a reponse with the one following. */
+
+static BOOL
+pipeline_response(void)
+{
+if ( !smtp_enforce_sync || !sender_host_address
+ || sender_host_notsocket || !pipelining_advertised)
+ return FALSE;
+
+return !wouldblock_reading();
+}
+
/*************************************************
+void
+smtp_command_timeout_exit(void)
+{
+log_write(L_lost_incoming_connection,
+ LOG_MAIN, "SMTP command timeout on%s connection from %s",
+ tls_in.active >= 0 ? " TLS" : "", host_and_ident(FALSE));
+if (smtp_batched_input)
+ moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
+smtp_notquit_exit(US"command-timeout", US"421",
+ US"%s: SMTP command timeout - closing connection",
+ smtp_active_hostname);
+exim_exit(EXIT_FAILURE, US"receiving");
+}
+
+void
+smtp_command_sigterm_exit(void)
+{
+log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info());
+if (smtp_batched_input)
+ 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");
+}
+
+void
+smtp_data_timeout_exit(void)
+{
+log_write(L_lost_incoming_connection,
+ LOG_MAIN, "SMTP data timeout (message abandoned) on connection from %s F=<%s>",
+ sender_fullhost ? sender_fullhost : US"local process", sender_address);
+receive_bomb_out(US"data-timeout", US"SMTP incoming data timeout");
+/* Does not return */
+}
+
+void
+smtp_data_sigint_exit(void)
+{
+log_write(0, LOG_MAIN, "%s closed after %s",
+ smtp_get_connection_info(), had_data_sigint == SIGTERM ? "SIGTERM":"SIGINT");
+receive_bomb_out(US"signal-exit",
+ US"Service not available - SIGTERM or SIGINT received");
+/* Does not return */
+}
+
+
+
/* Refill the buffer, and notify DKIM verification code.
Return false for error or EOF.
*/
fflush(smtp_out);
if (smtp_receive_timeout > 0) alarm(smtp_receive_timeout);
-/* Limit amount read, so non-message data is not fed to DKIM */
+/* 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, lim));
+rc = read(fileno(smtp_in), smtp_inbuffer, MIN(IN_BUFFER_SIZE-1, lim));
save_errno = errno;
-alarm(0);
+if (smtp_receive_timeout > 0) alarm(0);
if (rc <= 0)
{
/* Must put the error text in fixed store, because this might be during
header reading, where it releases unused store above the header. */
if (rc < 0)
{
+ if (had_command_timeout) /* set by signal handler */
+ smtp_command_timeout_exit(); /* does not return */
+ if (had_command_sigterm)
+ smtp_command_sigterm_exit();
+ if (had_data_timeout)
+ smtp_data_timeout_exit();
+ if (had_data_sigint)
+ smtp_data_sigint_exit();
+
smtp_had_error = save_errno;
smtp_read_error = string_copy_malloc(
string_sprintf(" (error: %s)", strerror(save_errno)));
}
- else smtp_had_eof = 1;
+ else
+ smtp_had_eof = 1;
return FALSE;
}
#ifndef DISABLE_DKIM
for(;;)
{
#ifndef DISABLE_DKIM
- BOOL dkim_save;
+ unsigned dkim_save;
#endif
if (chunking_data_left > 0)
receive_ungetc = lwr_receive_ungetc;
#ifndef DISABLE_DKIM
dkim_save = dkim_collect_input;
- dkim_collect_input = FALSE;
+ dkim_collect_input = 0;
#endif
/* Unless PIPELINING was offered, there should be no next command
incomplete_transaction_log(US"sync failure");
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error "
"(next input sent too soon: pipelining was not advertised): "
- "rejected \"%s\" %s next input=\"%s\"",
+ "rejected \"%s\" %s next input=\"%s\"%s",
smtp_cmd_buffer, host_and_ident(TRUE),
- string_printing(string_copyn(smtp_inptr, n)));
- (void) synprot_error(L_smtp_protocol_error, 554, NULL,
- US"SMTP synchronization error");
+ string_printing(string_copyn(smtp_inptr, n)),
+ smtp_inend - smtp_inptr > n ? "..." : "");
+ (void) synprot_error(L_smtp_protocol_error, 554, NULL,
+ US"SMTP synchronization error");
goto repeat_until_rset;
}
return EOD;
}
- smtp_printf("250 %u byte chunk received\r\n", chunking_datasize);
+ smtp_printf("250 %u byte chunk received\r\n", FALSE, chunking_datasize);
chunking_state = CHUNKING_OFFERED;
DEBUG(D_receive) debug_printf("chunking state %d\n", (int)chunking_state);
case NOOP_CMD:
HAD(SCH_NOOP);
- smtp_printf("250 OK\r\n");
+ smtp_printf("250 OK\r\n", FALSE);
goto next_cmd;
case BDAT_CMD:
}
receive_getc = bdat_getc;
- receive_getbuf = bdat_getbuf;
+ receive_getbuf = bdat_getbuf; /* r~getbuf is never actually used */
receive_ungetc = bdat_ungetc;
#ifndef DISABLE_DKIM
dkim_collect_input = dkim_save;
void
bdat_flush_data(void)
{
-unsigned n = chunking_data_left;
-(void) bdat_getbuf(&n);
+while (chunking_data_left)
+ {
+ unsigned n = chunking_data_left;
+ if (!bdat_getbuf(&n)) break;
+ }
receive_getc = lwr_receive_getc;
receive_getbuf = lwr_receive_getbuf;
Arguments:
format format string
+ more further data expected
... optional arguments
Returns: nothing
*/
void
-smtp_printf(const char *format, ...)
+smtp_printf(const char *format, BOOL more, ...)
{
va_list ap;
-va_start(ap, format);
-smtp_vprintf(format, ap);
+va_start(ap, more);
+smtp_vprintf(format, more, ap);
va_end(ap);
}
call another vararg function, only a function which accepts a va_list. */
void
-smtp_vprintf(const char *format, va_list ap)
+smtp_vprintf(const char *format, BOOL more, va_list ap)
{
BOOL yield;
{
log_write(0, LOG_MAIN|LOG_PANIC, "string too large in smtp_printf()");
smtp_closedown(US"Unexpected error");
- exim_exit(EXIT_FAILURE);
+ exim_exit(EXIT_FAILURE, NULL);
}
/* If this is the first output for a (non-batch) RCPT command, see if all RCPTs
#ifdef SUPPORT_TLS
if (tls_in.active >= 0)
{
- if (tls_write(TRUE, big_buffer, Ustrlen(big_buffer)) < 0)
+ if (tls_write(TRUE, big_buffer, Ustrlen(big_buffer), more) < 0)
smtp_write_error = -1;
}
else
static void
command_timeout_handler(int sig)
{
-sig = sig; /* Keep picky compilers happy */
-log_write(L_lost_incoming_connection,
- LOG_MAIN, "SMTP command timeout on%s connection from %s",
- (tls_in.active >= 0)? " TLS" : "",
- host_and_ident(FALSE));
-if (smtp_batched_input)
- moan_smtp_batch(NULL, "421 SMTP command timeout"); /* Does not return */
-smtp_notquit_exit(US"command-timeout", US"421",
- US"%s: SMTP command timeout - closing connection", smtp_active_hostname);
-exim_exit(EXIT_FAILURE);
+had_command_timeout = sig;
}
static void
command_sigterm_handler(int sig)
{
-sig = sig; /* Keep picky compilers happy */
-log_write(0, LOG_MAIN, "%s closed after SIGTERM", smtp_get_connection_info());
-if (smtp_batched_input)
- 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);
+had_command_sigterm = sig;
}
smtp_cmd_list *p;
BOOL hadnull = FALSE;
+had_command_timeout = 0;
os_non_restarting_signal(SIGALRM, command_timeout_handler);
while ((c = (receive_getc)(buffer_lim)) != '\n' && c != EOF)
/* Enforce synchronization for unknown commands */
-if (smtp_inptr < smtp_inend && /* Outstanding input */
- check_sync && /* Local flag set */
- smtp_enforce_sync && /* Global flag set */
- sender_host_address != NULL && /* Not local input */
- !sender_host_notsocket) /* Really is a socket */
+if ( smtp_inptr < smtp_inend /* Outstanding input */
+ && check_sync /* Local flag set */
+ && smtp_enforce_sync /* Global flag set */
+ && sender_host_address /* Not local input */
+ && !sender_host_notsocket) /* Really is a socket */
return BADSYN_CMD;
return OTHER_CMD;
void
smtp_closedown(uschar *message)
{
-if (smtp_in == NULL || smtp_batched_input) return;
+if (!smtp_in || smtp_batched_input) return;
receive_swallow_smtp();
-smtp_printf("421 %s\r\n", message);
+smtp_printf("421 %s\r\n", FALSE, message);
for (;;) switch(smtp_read_command(FALSE, GETC_BUFFER_UNLIMITED))
{
case EOF_CMD:
- return;
+ return;
case QUIT_CMD:
- smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
- mac_smtp_fflush();
- return;
+ smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
+ mac_smtp_fflush();
+ return;
case RSET_CMD:
- smtp_printf("250 Reset OK\r\n");
- break;
+ smtp_printf("250 Reset OK\r\n", FALSE);
+ break;
default:
- smtp_printf("421 %s\r\n", message);
- break;
+ smtp_printf("421 %s\r\n", FALSE, message);
+ break;
}
}
/* Append TLS-related information to a log line
Arguments:
- s String under construction: allocated string to extend, or NULL
- sizep Pointer to current allocation size (update on return), or NULL
- ptrp Pointer to index for new entries in string (update on return), or NULL
+ g String under construction: allocated string to extend, or NULL
Returns: Allocated string or NULL
*/
-static uschar *
-s_tlslog(uschar * s, int * sizep, int * ptrp)
+static gstring *
+s_tlslog(gstring * g)
{
- int size = sizep ? *sizep : 0;
- int ptr = ptrp ? *ptrp : 0;
-
- if (LOGGING(tls_cipher) && tls_in.cipher != NULL)
- s = string_append(s, &size, &ptr, 2, US" X=", tls_in.cipher);
- if (LOGGING(tls_certificate_verified) && tls_in.cipher != NULL)
- s = string_append(s, &size, &ptr, 2, US" CV=",
- tls_in.certificate_verified? "yes":"no");
- if (LOGGING(tls_peerdn) && tls_in.peerdn != NULL)
- s = string_append(s, &size, &ptr, 3, US" DN=\"",
- string_printing(tls_in.peerdn), US"\"");
- if (LOGGING(tls_sni) && tls_in.sni != NULL)
- s = string_append(s, &size, &ptr, 3, US" SNI=\"",
- string_printing(tls_in.sni), US"\"");
-
- if (s)
- {
- s[ptr] = '\0';
- if (sizep) *sizep = size;
- if (ptrp) *ptrp = ptr;
- }
- return s;
+if (LOGGING(tls_cipher) && tls_in.cipher)
+ g = string_append(g, 2, US" X=", tls_in.cipher);
+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)
+ 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"\"");
+return g;
}
#endif
void
smtp_log_no_mail(void)
{
-int size, ptr, i;
-uschar *s, *sep;
+int i;
+uschar * sep, * s;
+gstring * g = NULL;
if (smtp_mailcmd_count > 0 || !LOGGING(smtp_no_mail))
return;
-s = NULL;
-size = ptr = 0;
-
-if (sender_host_authenticated != NULL)
+if (sender_host_authenticated)
{
- s = string_append(s, &size, &ptr, 2, US" A=", sender_host_authenticated);
- if (authenticated_id != NULL)
- s = string_append(s, &size, &ptr, 2, US":", authenticated_id);
+ g = string_append(g, 2, US" A=", sender_host_authenticated);
+ if (authenticated_id) g = string_append(g, 2, US":", authenticated_id);
}
#ifdef SUPPORT_TLS
-s = s_tlslog(s, &size, &ptr);
+g = s_tlslog(g);
#endif
-sep = (smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE)?
- US" C=..." : US" C=";
+sep = smtp_connection_had[SMTP_HBUFF_SIZE-1] != SCH_NONE ? US" C=..." : US" C=";
+
for (i = smtp_ch_index; i < SMTP_HBUFF_SIZE; i++)
- {
if (smtp_connection_had[i] != SCH_NONE)
{
- s = string_append(s, &size, &ptr, 2, sep,
- smtp_names[smtp_connection_had[i]]);
+ g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
sep = US",";
}
- }
for (i = 0; i < smtp_ch_index; i++)
{
- s = string_append(s, &size, &ptr, 2, sep, smtp_names[smtp_connection_had[i]]);
+ g = string_append(g, 2, sep, smtp_names[smtp_connection_had[i]]);
sep = US",";
}
-if (s != NULL) s[ptr] = 0; else s = US"";
-log_write(0, LOG_MAIN, "no MAIL in SMTP connection from %s D=%s%s",
- host_and_ident(FALSE),
- readconf_printtime( (int) ((long)time(NULL) - (long)smtp_connection_start)),
- s);
+if (!(s = string_from_gstring(g))) s = US"";
+
+log_write(0, LOG_MAIN, "no MAIL in %sSMTP connection from %s D=%s%s",
+ tcp_in_fastopen ? US"TFO " : US"",
+ host_and_ident(FALSE), string_timesince(&smtp_connection_start), s);
}
+/* Return list of recent smtp commands */
+
+uschar *
+smtp_cmd_hist(void)
+{
+int i;
+gstring * list = NULL;
+uschar * s;
+
+for (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++)
+ list = string_append_listele(list, ',', smtp_names[smtp_connection_had[i]]);
+
+s = string_from_gstring(list);
+return s ? s : US"";
+}
+
+
+
/*************************************************
* Check HELO line and set sender_helo_name *
/* Discard any previous helo name */
-if (sender_helo_name != NULL)
+if (sender_helo_name)
{
store_free(sender_helo_name);
sender_helo_name = NULL;
/* Skip tests if junk is permitted. */
if (!yield)
- {
+
/* Allow the new standard form for IPv6 address literals, namely,
[IPv6:....], and because someone is bound to use it, allow an equivalent
IPv4 form. Allow plain addresses as well. */
/* Non-literals must be alpha, dot, hyphen, plus any non-valid chars
that have been configured (usually underscore - sigh). */
- else if (*s != 0)
- {
- yield = TRUE;
- while (*s != 0)
- {
+ else if (*s)
+ for (yield = TRUE; *s; s++)
if (!isalnum(*s) && *s != '.' && *s != '-' &&
Ustrchr(helo_allow_chars, *s) == NULL)
{
yield = FALSE;
break;
}
- s++;
- }
- }
- }
/* Save argument if OK */
n = v;
if (*v == '=')
-{
+ {
while(isalpha(n[-1])) n--;
/* RFC says SP, but TAB seen in wild and other major MTAs accept it */
if (!isspace(n[-1])) return FALSE;
n[-1] = 0;
-}
+ }
else
-{
+ {
n++;
if (v == smtp_cmd_data) return FALSE;
-}
+ }
*v++ = 0;
*name = n;
*value = v;
*************************************************/
/* This function is called whenever the SMTP session is reset from
-within either of the setup functions.
+within either of the setup functions; also from the daemon loop.
Argument: the stacking pool storage reset point
Returns: nothing
*/
-static void
+void
smtp_reset(void *reset_point)
{
recipients_list = NULL;
sending_ip_address = NULL;
return_path = sender_address = NULL;
sender_data = NULL; /* Can be set by ACL */
-deliver_localpart_orig = NULL;
-deliver_domain_orig = 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 */
raw_sender = NULL; /* After SMTP rewrite, before qualifying */
bmi_verdicts = NULL;
#endif
dnslist_domain = dnslist_matched = NULL;
+#ifdef SUPPORT_SPF
+spf_header_comment = spf_received = spf_result = spf_smtp_comment = NULL;
+spf_result_guessed = FALSE;
+#endif
#ifndef DISABLE_DKIM
-dkim_signers = NULL;
+dkim_cur_signer = dkim_signers =
+dkim_signing_domain = dkim_signing_selector = dkim_signatures = NULL;
+dkim_cur_signer = dkim_signers = dkim_signing_domain = dkim_signing_selector = NULL;
dkim_disable_verify = FALSE;
-dkim_collect_input = 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
+dmarc_has_been_checked = dmarc_disable_verify = dmarc_enable_forensic = FALSE;
+dmarc_domain_policy = dmarc_forensic_sender =
+dmarc_history_file = dmarc_status = dmarc_status_text = dmarc_tld_file =
+dmarc_used_domain = NULL;
+#endif
+#ifdef EXPERIMENTAL_ARC
+arc_state = arc_state_reason = NULL;
#endif
dsn_ret = 0;
dsn_envid = NULL;
#ifndef DISABLE_PRDR
prdr_requested = FALSE;
#endif
-#ifdef EXPERIMENTAL_SPF
-spf_header_comment = NULL;
-spf_received = NULL;
-spf_result = NULL;
-spf_smtp_comment = NULL;
-#endif
#ifdef SUPPORT_I18N
message_smtputf8 = FALSE;
#endif
case HELO_CMD:
case EHLO_CMD:
- check_helo(smtp_cmd_data);
- /* Fall through */
+ check_helo(smtp_cmd_data);
+ /* Fall through */
case RSET_CMD:
- cancel_cutthrough_connection(TRUE, US"RSET received");
- smtp_reset(reset_point);
- bsmtp_transaction_linecount = receive_linecount;
- break;
+ cancel_cutthrough_connection(TRUE, US"RSET received");
+ smtp_reset(reset_point);
+ bsmtp_transaction_linecount = receive_linecount;
+ break;
/* The MAIL FROM command requires an address as an operand. All we
it is the canonical extracted address which is all that is kept. */
case MAIL_CMD:
- smtp_mailcmd_count++; /* Count for no-mail log */
- if (sender_address != NULL)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given");
+ smtp_mailcmd_count++; /* Count for no-mail log */
+ if (sender_address != NULL)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "503 Sender already given");
- if (smtp_cmd_data[0] == 0)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 MAIL FROM must have an address operand");
+ if (smtp_cmd_data[0] == 0)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 MAIL FROM must have an address operand");
- /* Reset to start of message */
+ /* Reset to start of message */
- cancel_cutthrough_connection(TRUE, US"MAIL received");
- smtp_reset(reset_point);
+ cancel_cutthrough_connection(TRUE, US"MAIL received");
+ smtp_reset(reset_point);
- /* Apply SMTP rewrite */
+ /* Apply SMTP rewrite */
- raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
- rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE,
- US"", global_rewrite_rules) : smtp_cmd_data;
+ raw_sender = ((rewrite_existflags & rewrite_smtp) != 0)?
+ rewrite_one(smtp_cmd_data, rewrite_smtp|rewrite_smtp_sender, NULL, FALSE,
+ US"", global_rewrite_rules) : smtp_cmd_data;
- /* Extract the address; the TRUE flag allows <> as valid */
+ /* Extract the address; the TRUE flag allows <> as valid */
- raw_sender =
- parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain,
- TRUE);
+ raw_sender =
+ parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain,
+ TRUE);
- if (raw_sender == NULL)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
+ if (!raw_sender)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
- sender_address = string_copy(raw_sender);
+ sender_address = string_copy(raw_sender);
- /* Qualify unqualified sender addresses if permitted to do so. */
+ /* Qualify unqualified sender addresses if permitted to do so. */
- if (sender_domain == 0 && sender_address[0] != 0 && sender_address[0] != '@')
- {
- if (allow_unqualified_sender)
- {
- sender_address = rewrite_address_qualify(sender_address, FALSE);
- DEBUG(D_receive) debug_printf("unqualified address %s accepted "
- "and rewritten\n", raw_sender);
- }
- /* The function moan_smtp_batch() does not return. */
- else moan_smtp_batch(smtp_cmd_buffer, "501 sender address must contain "
- "a domain");
- }
- break;
+ if ( !sender_domain
+ && sender_address[0] != 0 && sender_address[0] != '@')
+ if (allow_unqualified_sender)
+ {
+ sender_address = rewrite_address_qualify(sender_address, FALSE);
+ DEBUG(D_receive) debug_printf("unqualified address %s accepted "
+ "and rewritten\n", raw_sender);
+ }
+ /* The function moan_smtp_batch() does not return. */
+ else
+ moan_smtp_batch(smtp_cmd_buffer, "501 sender address must contain "
+ "a domain");
+ break;
/* The RCPT TO command requires an address as an operand. All we do
extracted address. */
case RCPT_CMD:
- if (sender_address == NULL)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "503 No sender yet given");
+ if (!sender_address)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "503 No sender yet given");
- if (smtp_cmd_data[0] == 0)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 RCPT TO must have an address operand");
+ if (smtp_cmd_data[0] == 0)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer,
+ "501 RCPT TO must have an address operand");
- /* Check maximum number allowed */
+ /* Check maximum number allowed */
- if (recipients_max > 0 && recipients_count + 1 > recipients_max)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients",
- recipients_max_reject? "552": "452");
+ if (recipients_max > 0 && recipients_count + 1 > recipients_max)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "%s too many recipients",
+ recipients_max_reject? "552": "452");
- /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a
- recipient address */
+ /* Apply SMTP rewrite, then extract address. Don't allow "<>" as a
+ recipient address */
- recipient = rewrite_existflags & rewrite_smtp
- ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
- global_rewrite_rules)
- : smtp_cmd_data;
+ recipient = rewrite_existflags & rewrite_smtp
+ ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+ global_rewrite_rules)
+ : smtp_cmd_data;
- recipient = parse_extract_address(recipient, &errmess, &start, &end,
- &recipient_domain, FALSE);
+ recipient = parse_extract_address(recipient, &errmess, &start, &end,
+ &recipient_domain, FALSE);
- if (!recipient)
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
+ if (!recipient)
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
- /* If the recipient address is unqualified, qualify it if permitted. Then
- add it to the list of recipients. */
+ /* If the recipient address is unqualified, qualify it if permitted. Then
+ add it to the list of recipients. */
- if (recipient_domain == 0)
- {
- if (allow_unqualified_recipient)
- {
- DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
- recipient);
- recipient = rewrite_address_qualify(recipient, TRUE);
- }
- /* The function moan_smtp_batch() does not return. */
- else moan_smtp_batch(smtp_cmd_buffer, "501 recipient address must contain "
- "a domain");
- }
- receive_add_recipient(recipient, -1);
- break;
+ if (!recipient_domain)
+ if (allow_unqualified_recipient)
+ {
+ DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
+ recipient);
+ recipient = rewrite_address_qualify(recipient, TRUE);
+ }
+ /* The function moan_smtp_batch() does not return. */
+ else
+ moan_smtp_batch(smtp_cmd_buffer,
+ "501 recipient address must contain a domain");
+
+ receive_add_recipient(recipient, -1);
+ break;
/* The DATA command is legal only if it follows successful MAIL FROM
command is encountered. */
case DATA_CMD:
- if (sender_address == NULL || recipients_count <= 0)
- {
- /* The function moan_smtp_batch() does not return. */
- if (sender_address == NULL)
- moan_smtp_batch(smtp_cmd_buffer,
- "503 MAIL FROM:<sender> command must precede DATA");
+ if (!sender_address || recipients_count <= 0)
+ /* The function moan_smtp_batch() does not return. */
+ if (!sender_address)
+ moan_smtp_batch(smtp_cmd_buffer,
+ "503 MAIL FROM:<sender> command must precede DATA");
+ else
+ moan_smtp_batch(smtp_cmd_buffer,
+ "503 RCPT TO:<recipient> must precede DATA");
else
- moan_smtp_batch(smtp_cmd_buffer,
- "503 RCPT TO:<recipient> must precede DATA");
- }
- else
- {
- done = 3; /* DATA successfully achieved */
- message_ended = END_NOTENDED; /* Indicate in middle of message */
- }
- break;
+ {
+ done = 3; /* DATA successfully achieved */
+ message_ended = END_NOTENDED; /* Indicate in middle of message */
+ }
+ break;
/* The VRFY, EXPN, HELP, ETRN, and NOOP commands are ignored. */
case HELP_CMD:
case NOOP_CMD:
case ETRN_CMD:
- bsmtp_transaction_linecount = receive_linecount;
- break;
+ bsmtp_transaction_linecount = receive_linecount;
+ break;
case EOF_CMD:
case QUIT_CMD:
- done = 2;
- break;
+ done = 2;
+ break;
case BADARG_CMD:
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected argument data");
- break;
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected argument data");
+ break;
case BADCHAR_CMD:
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected NULL in SMTP command");
- break;
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "501 Unexpected NULL in SMTP command");
+ break;
default:
- /* The function moan_smtp_batch() does not return. */
- moan_smtp_batch(smtp_cmd_buffer, "500 Command unrecognized");
- break;
+ /* The function moan_smtp_batch() does not return. */
+ moan_smtp_batch(smtp_cmd_buffer, "500 Command unrecognized");
+ break;
}
}
}
+
+
+#ifdef TCP_FASTOPEN
+static void
+tfo_in_check(void)
+{
+# ifdef TCP_INFO
+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");
+ tcp_in_fastopen = TRUE;
+ }
+# endif
+}
+#endif
+
+
/*************************************************
* Start an SMTP session *
*************************************************/
BOOL
smtp_start_session(void)
{
-int size = 256;
-int ptr, esclen;
+int esclen;
uschar *user_msg, *log_msg;
uschar *code, *esc;
-uschar *p, *s, *ss;
+uschar *p, *s;
+gstring * ss;
-smtp_connection_start = time(NULL);
+gettimeofday(&smtp_connection_start, NULL);
for (smtp_ch_index = 0; smtp_ch_index < SMTP_HBUFF_SIZE; smtp_ch_index++)
smtp_connection_had[smtp_ch_index] = SCH_NONE;
smtp_ch_index = 0;
/* If receiving by -bs from a trusted user, or testing with -bh, we allow
authentication settings from -oMaa to remain in force. */
-if (!host_checking && !sender_host_notsocket) sender_host_authenticated = NULL;
+if (!host_checking && !sender_host_notsocket)
+ sender_host_auth_pubname = sender_host_authenticated = NULL;
authenticated_by = NULL;
#ifdef SUPPORT_TLS
(sender_host_address ? protocols : protocols_local) [pnormal];
/* Set up the buffer for inputting using direct read() calls, and arrange to
-call the local functions instead of the standard C ones. */
+call the local functions instead of the standard C ones. Place a NUL at the
+end of the buffer to safety-stop C-string reads from it. */
-if (!(smtp_inbuffer = (uschar *)malloc(IN_BUFFER_SIZE)))
+if (!(smtp_inbuffer = US malloc(IN_BUFFER_SIZE)))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "malloc() failed for SMTP input buffer");
+smtp_inbuffer[IN_BUFFER_SIZE-1] = '\0';
receive_getc = smtp_getc;
receive_getbuf = smtp_getbuf;
/* Set up the message size limit; this may be host-specific */
thismessage_size_limit = expand_string_integer(message_size_limit, TRUE);
-if (expand_string_message != NULL)
+if (expand_string_message)
{
if (thismessage_size_limit == -1)
log_write(0, LOG_MAIN|LOG_PANIC, "unable to expand message_size_limit: "
DEBUG(D_receive) debug_printf("checking for IP options\n");
- if (getsockopt(fileno(smtp_out), IPPROTO_IP, IP_OPTIONS, (uschar *)(ipopt),
+ if (getsockopt(fileno(smtp_out), IPPROTO_IP, IP_OPTIONS, US (ipopt),
&optlen) < 0)
{
if (errno != ENOPROTOOPT)
{
log_write(0, LOG_MAIN, "getsockopt() failed from %s: %s",
host_and_ident(FALSE), strerror(errno));
- smtp_printf("451 SMTP service not available\r\n");
+ smtp_printf("451 SMTP service not available\r\n", FALSE);
return FALSE;
}
}
struct in_addr addr;
#if OPTSTYLE == 1
- uschar *optstart = (uschar *)(ipopt->__data);
+ uschar *optstart = US (ipopt->__data);
#elif OPTSTYLE == 2
- uschar *optstart = (uschar *)(ipopt->ip_opts);
+ uschar *optstart = US (ipopt->ip_opts);
#else
- uschar *optstart = (uschar *)(ipopt->ipopt_list);
+ uschar *optstart = US (ipopt->ipopt_list);
#endif
DEBUG(D_receive) debug_printf("IP options exist\n");
p += Ustrlen(p);
for (opt = optstart; opt != NULL &&
- opt < (uschar *)(ipopt) + optlen;)
+ opt < US (ipopt) + optlen;)
{
switch (*opt)
{
Ustrcat(p, "[ ");
p += 2;
for (i = 0; i < opt[1]; i++)
- {
- sprintf(CS p, "%2.2x ", opt[i]);
- p += 3;
- }
+ p += sprintf(CS p, "%2.2x ", opt[i]);
*p++ = ']';
}
opt += opt[1];
log_write(0, LOG_MAIN|LOG_REJECT,
"connection from %s refused (IP options)", host_and_ident(FALSE));
- smtp_printf("554 SMTP service not available\r\n");
+ smtp_printf("554 SMTP service not available\r\n", FALSE);
return FALSE;
}
{
log_write(L_connection_reject, LOG_MAIN|LOG_REJECT, "refused connection "
"from %s (host_reject_connection)", host_and_ident(FALSE));
- smtp_printf("554 SMTP service not available\r\n");
+ smtp_printf("554 SMTP service not available\r\n", FALSE);
return FALSE;
}
log_write(L_connection_reject,
LOG_MAIN|LOG_REJECT, "refused connection from %s "
"(tcp wrappers)", host_and_ident(FALSE));
- smtp_printf("554 SMTP service not available\r\n");
+ smtp_printf("554 SMTP service not available\r\n", FALSE);
}
else
{
log_write(L_connection_reject,
LOG_MAIN|LOG_REJECT, "temporarily refused connection from %s "
"(tcp wrappers errno=%d)", host_and_ident(FALSE), save_errno);
- smtp_printf("451 Temporary local problem - please try later\r\n");
+ smtp_printf("451 Temporary local problem - please try later\r\n", FALSE);
}
return FALSE;
}
host_and_ident(FALSE), smtp_accept_count - 1, smtp_accept_max,
smtp_accept_reserve, (rc == DEFER)? " (lookup deferred)" : "");
smtp_printf("421 %s: Too many concurrent SMTP connections; "
- "please try again later\r\n", smtp_active_hostname);
+ "please try again later\r\n", FALSE, smtp_active_hostname);
return FALSE;
}
reserved_host = TRUE;
LOG_MAIN, "temporarily refused connection from %s: not in "
"reserve list and load average = %.2f", host_and_ident(FALSE),
(double)load_average/1000.0);
- smtp_printf("421 %s: Too much load; please try again later\r\n",
+ smtp_printf("421 %s: Too much load; please try again later\r\n", FALSE,
smtp_active_hostname);
return FALSE;
}
smtps port for use with older style SSL MTAs. */
#ifdef SUPPORT_TLS
- if (tls_in.on_connect && tls_server_start(tls_require_ciphers, &user_msg) != OK)
- return smtp_log_tls_fail(user_msg);
+ 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;
+ }
#endif
/* Run the connect ACL if it exists */
first, and output it in one fell swoop. This gives a better chance of it
ending up as a single packet. */
-ss = store_get(size);
-ptr = 0;
+ss = string_get(256);
p = s;
do /* At least once, in case we have an empty string */
{
int len;
uschar *linebreak = Ustrchr(p, '\n');
- ss = string_catn(ss, &size, &ptr, code, 3);
- if (linebreak == NULL)
+ ss = string_catn(ss, code, 3);
+ if (!linebreak)
{
len = Ustrlen(p);
- ss = string_catn(ss, &size, &ptr, US" ", 1);
+ ss = string_catn(ss, US" ", 1);
}
else
{
len = linebreak - p;
- ss = string_catn(ss, &size, &ptr, US"-", 1);
+ ss = string_catn(ss, US"-", 1);
}
- ss = string_catn(ss, &size, &ptr, esc, esclen);
- ss = string_catn(ss, &size, &ptr, p, len);
- ss = string_catn(ss, &size, &ptr, US"\r\n", 2);
+ ss = string_catn(ss, esc, esclen);
+ ss = string_catn(ss, p, len);
+ ss = string_catn(ss, US"\r\n", 2);
p += len;
- if (linebreak != NULL) p++;
+ if (linebreak) p++;
}
-while (*p != 0);
-
-ss[ptr] = 0; /* string_cat leaves room for this */
+while (*p);
/* Before we write the banner, check that there is no input pending, unless
this synchronisation check is disabled. */
"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");
+ smtp_printf("554 SMTP synchronization error\r\n", FALSE);
return FALSE;
}
/* Now output the banner */
-smtp_printf("%s", ss);
+smtp_printf("%s", FALSE, 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. */
+
+#ifdef TCP_FASTOPEN
+tfo_in_check();
+#endif
+
return TRUE;
}
if (code > 0)
{
- smtp_printf("%d%c%s%s%s\r\n", code, (yield == 1)? '-' : ' ',
- (data == NULL)? US"" : data, (data == NULL)? US"" : US": ", errmess);
+ smtp_printf("%d%c%s%s%s\r\n", FALSE, code, yield == 1 ? '-' : ' ',
+ data ? data : US"", data ? US": " : US"", errmess);
if (yield == 1)
- smtp_printf("%d Too many syntax or protocol errors\r\n", code);
+ smtp_printf("%d Too many syntax or protocol errors\r\n", FALSE, code);
}
return yield;
rcpt_in_progress = FALSE;
}
-/* Not output the message, splitting it up into multiple lines if necessary. */
+/* Now output the message, splitting it up into multiple lines if necessary.
+We only handle pipelining these responses as far as nonfinal/final groups,
+not the whole MAIL/RCPT/DATA response set. */
for (;;)
{
uschar *nl = Ustrchr(msg, '\n');
if (nl == NULL)
{
- smtp_printf("%.3s%c%.*s%s\r\n", code, final? ' ':'-', esclen, esc, msg);
+ smtp_printf("%.3s%c%.*s%s\r\n", !final, code, final ? ' ':'-', esclen, esc, msg);
return;
}
else if (nl[1] == 0 || no_multiline_responses)
{
- smtp_printf("%.3s%c%.*s%.*s\r\n", code, final? ' ':'-', esclen, esc,
+ smtp_printf("%.3s%c%.*s%.*s\r\n", !final, code, final ? ' ':'-', esclen, esc,
(int)(nl - msg), msg);
return;
}
else
{
- smtp_printf("%.3s-%.*s%.*s\r\n", code, esclen, esc, (int)(nl - msg), msg);
+ smtp_printf("%.3s-%.*s%.*s\r\n", TRUE, code, esclen, esc, (int)(nl - msg), msg);
msg = nl + 1;
while (isspace(*msg)) msg++;
}
/* This function is called when acl_check() fails. As well as calls from within
this module, it is called from receive.c for an ACL after DATA. It sorts out
-logging the incident, and sets up the error response. A message containing
+logging the incident, and sends the error response. A message containing
newlines is turned into a multiline SMTP response, but for logging, only the
first line is used.
if (log_reject_target != 0)
{
#ifdef SUPPORT_TLS
- uschar * tls = s_tlslog(NULL, NULL, NULL);
+ gstring * g = s_tlslog(NULL);
+ uschar * tls = string_from_gstring(g);
if (!tls) tls = US"";
#else
uschar * tls = US"";
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", code, buffer);
+ smtp_printf("%s %s\r\n", FALSE, code, buffer);
va_end(ap);
}
mac_smtp_fflush();
HDEBUG(D_receive) debug_printf("getting IP address for %s\n",
sender_helo_name);
- rc = host_find_bydns(&h, NULL, HOST_FIND_BY_A,
+ 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)
{
if (set_id) authenticated_id = string_copy_malloc(set_id);
sender_host_authenticated = au->name;
+ sender_host_auth_pubname = au->public_name;
authentication_failed = FALSE;
authenticated_fail_id = NULL; /* Impossible to already be set? */
*recipient = rewrite_address_qualify(*recipient, TRUE);
return rd;
}
-smtp_printf("501 %s: recipient address must contain a domain\r\n",
+smtp_printf("501 %s: recipient address must contain a domain\r\n", FALSE,
smtp_cmd_data);
log_write(L_smtp_syntax_error,
LOG_MAIN|LOG_REJECT, "unqualified %s rejected: <%s> %s%s",
if (*user_msgp)
smtp_respond(US"221", 3, TRUE, *user_msgp);
else
- smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+ smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
#ifdef SUPPORT_TLS
-tls_close(TRUE, TRUE);
+tls_close(TRUE, TLS_SHUTDOWN_NOWAIT);
#endif
log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
{
HAD(SCH_RSET);
incomplete_transaction_log(US"RSET");
-smtp_printf("250 Reset OK\r\n");
+smtp_printf("250 Reset OK\r\n", FALSE);
cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE;
}
cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
#ifdef SUPPORT_TLS
cmd_list[CMD_LIST_STARTTLS].is_mail_cmd = TRUE;
-cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
#endif
/* Set the local signal handler for SIGTERM - it tries to end off tidily */
+had_command_sigterm = 0;
os_non_restarting_signal(SIGTERM, command_sigterm_handler);
/* Batched SMTP is handled in a different function. */
void (*oldsignal)(int);
pid_t pid;
int start, end, sender_domain, recipient_domain;
- int ptr, size, rc;
+ int rc;
int c;
auth_instance *au;
uschar *orcpt = NULL;
int flags;
+ gstring * g;
#ifdef AUTH_TLS
/* Check once per STARTTLS or SSL-on-connect for a TLS AUTH */
)
{
cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = FALSE;
- if ( acl_smtp_auth
- && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth,
- &user_msg, &log_msg)) != OK
- )
- {
- done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
- continue;
- }
for (au = auths; au; au = au->next)
if (strcmpic(US"tls", au->driver_name) == 0)
{
- smtp_cmd_data = NULL;
-
- if (smtp_in_auth(au, &s, &ss) == OK)
- { DEBUG(D_auth) debug_printf("tls auth succeeded\n"); }
+ if ( acl_smtp_auth
+ && (rc = acl_check(ACL_WHERE_AUTH, NULL, acl_smtp_auth,
+ &user_msg, &log_msg)) != OK
+ )
+ done = smtp_handle_acl_fail(ACL_WHERE_AUTH, rc, user_msg, log_msg);
else
- { DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); }
+ {
+ smtp_cmd_data = NULL;
+
+ if (smtp_in_auth(au, &s, &ss) == OK)
+ { DEBUG(D_auth) debug_printf("tls auth succeeded\n"); }
+ else
+ { DEBUG(D_auth) debug_printf("tls auth not succeeded\n"); }
+ }
break;
}
}
{
c = smtp_in_auth(au, &s, &ss);
- smtp_printf("%s\r\n", s);
+ 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 (!check_helo(smtp_cmd_data))
{
- smtp_printf("501 Syntactically invalid %s argument(s)\r\n", hello);
+ smtp_printf("501 Syntactically invalid %s argument(s)\r\n", FALSE, hello);
log_write(0, LOG_MAIN|LOG_REJECT, "rejected %s from %s: syntactically "
"invalid argument(s): %s", hello, host_and_ident(FALSE),
/* Force a reverse lookup if HELO quoted something in helo_lookup_domains
because otherwise the log can be confusing. */
- if (sender_host_name == NULL &&
- (deliver_domain = sender_helo_name, /* set $domain */
- match_isinlist(sender_helo_name, CUSS &helo_lookup_domains, 0,
+ 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)
(void)host_name_lookup();
{
if (helo_required)
{
- smtp_printf("%d %s argument does not match calling host\r\n",
+ smtp_printf("%d %s argument does not match calling host\r\n", FALSE,
tempfail? 451 : 550, hello);
log_write(0, LOG_MAIN|LOG_REJECT, "%srejected \"%s %s\" from %s",
tempfail? "temporarily " : "",
}
}
-#ifdef EXPERIMENTAL_SPF
+#ifdef SUPPORT_SPF
/* set up SPF context */
spf_init(sender_helo_name, sender_host_address);
#endif
&user_msg, &log_msg)) != OK)
{
done = smtp_handle_acl_fail(ACL_WHERE_HELO, rc, user_msg, log_msg);
- sender_helo_name = NULL;
+ if (sender_helo_name)
+ {
+ store_free(sender_helo_name);
+ sender_helo_name = NULL;
+ }
host_build_sender_fullhost(); /* Rebuild */
break;
}
sender_ident ? sender_ident : US"",
sender_ident ? US" at " : US"",
sender_host_name ? sender_host_name : sender_helo_name);
-
- ptr = Ustrlen(s);
- size = ptr + 1;
+ g = string_cat(NULL, s);
if (sender_host_address)
{
- s = string_catn(s, &size, &ptr, US" [", 2);
- s = string_cat (s, &size, &ptr, sender_host_address);
- s = string_catn(s, &size, &ptr, US"]", 1);
+ g = string_catn(g, US" [", 2);
+ g = string_cat (g, sender_host_address);
+ g = string_catn(g, US"]", 1);
}
}
"newlines: message truncated: %s", string_printing(s));
*ss = 0;
}
- ptr = Ustrlen(s);
- size = ptr + 1;
+ g = string_cat(NULL, s);
}
- s = string_catn(s, &size, &ptr, US"\r\n", 2);
+ g = string_catn(g, US"\r\n", 2);
/* If we received EHLO, we must create a multiline response which includes
the functions supported. */
if (esmtp)
{
- s[3] = '-';
+ g->s[3] = '-';
/* I'm not entirely happy with this, as an MTA is supposed to check
that it has enough room to accept a message of maximum size before
{
sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code,
thismessage_size_limit);
- s = string_cat(s, &size, &ptr, big_buffer);
+ g = string_cat(g, big_buffer);
}
else
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-SIZE\r\n", 7);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-SIZE\r\n", 7);
}
/* Exim does not do protocol conversion or data conversion. It is 8-bit
if (accept_8bitmime)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-8BITMIME\r\n", 11);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-8BITMIME\r\n", 11);
}
/* Advertise DSN support if configured to do so. */
if (verify_check_host(&dsn_advertise_hosts) != FAIL)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-DSN\r\n", 6);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-DSN\r\n", 6);
dsn_advertised = TRUE;
}
if (acl_smtp_etrn)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-ETRN\r\n", 7);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-ETRN\r\n", 7);
}
if (acl_smtp_vrfy)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-VRFY\r\n", 7);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-VRFY\r\n", 7);
}
if (acl_smtp_expn)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-EXPN\r\n", 7);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-EXPN\r\n", 7);
}
/* Exim is quite happy with pipelining, so let the other end know that
if (pipelining_enable &&
verify_check_host(&pipelining_advertise_hosts) == OK)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-PIPELINING\r\n", 13);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-PIPELINING\r\n", 13);
sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
pipelining_advertised = TRUE;
}
auth_instance *au;
BOOL first = TRUE;
for (au = auths; au; au = au->next)
- if (au->server && (au->advertise_condition == NULL ||
- expand_check_condition(au->advertise_condition, au->name,
- US"authenticator")))
+ {
+ au->advertised = FALSE;
+ if (au->server)
{
- int saveptr;
- if (first)
+ DEBUG(D_auth+D_expand) debug_printf_indent(
+ "Evaluating advertise_condition for %s athenticator\n",
+ au->public_name);
+ if ( !au->advertise_condition
+ || expand_check_condition(au->advertise_condition, au->name,
+ US"authenticator")
+ )
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-AUTH", 5);
- first = FALSE;
- auth_advertised = TRUE;
+ int saveptr;
+ if (first)
+ {
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-AUTH", 5);
+ first = FALSE;
+ auth_advertised = TRUE;
+ }
+ saveptr = g->ptr;
+ g = string_catn(g, US" ", 1);
+ g = string_cat (g, au->public_name);
+ while (++saveptr < g->ptr) g->s[saveptr] = toupper(g->s[saveptr]);
+ au->advertised = TRUE;
}
- saveptr = ptr;
- s = string_catn(s, &size, &ptr, US" ", 1);
- s = string_cat (s, &size, &ptr, au->public_name);
- while (++saveptr < ptr) s[saveptr] = toupper(s[saveptr]);
- au->advertised = TRUE;
}
- else
- au->advertised = FALSE;
+ }
- if (!first) s = string_catn(s, &size, &ptr, US"\r\n", 2);
+ if (!first) g = string_catn(g, US"\r\n", 2);
}
/* RFC 3030 CHUNKING */
if (verify_check_host(&chunking_advertise_hosts) != FAIL)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-CHUNKING\r\n", 11);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-CHUNKING\r\n", 11);
chunking_offered = TRUE;
chunking_state = CHUNKING_OFFERED;
}
if (tls_in.active < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-STARTTLS\r\n", 11);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-STARTTLS\r\n", 11);
tls_advertised = TRUE;
}
#endif
/* Per Recipient Data Response, draft by Eric A. Hall extending RFC */
if (prdr_enable)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-PRDR\r\n", 7);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-PRDR\r\n", 7);
}
#endif
if ( accept_8bitmime
&& verify_check_host(&smtputf8_advertise_hosts) != FAIL)
{
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US"-SMTPUTF8\r\n", 11);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US"-SMTPUTF8\r\n", 11);
smtputf8_advertised = TRUE;
}
#endif
/* Finish off the multiline reply with one that is always available. */
- s = string_catn(s, &size, &ptr, smtp_code, 3);
- s = string_catn(s, &size, &ptr, US" HELP\r\n", 7);
+ g = string_catn(g, smtp_code, 3);
+ g = string_catn(g, US" HELP\r\n", 7);
}
/* Terminate the string (for debug), write it, and note that HELO/EHLO
has been seen. */
- s[ptr] = 0;
-
#ifdef SUPPORT_TLS
- if (tls_in.active >= 0) (void)tls_write(TRUE, s, ptr); else
+ if (tls_in.active >= 0) (void)tls_write(TRUE, g->s, g->ptr, FALSE); else
#endif
{
- int i = fwrite(s, 1, ptr, smtp_out); i = i; /* compiler quietening */
+ int i = fwrite(g->s, 1, g->ptr, smtp_out); i = i; /* compiler quietening */
}
DEBUG(D_receive)
{
uschar *cr;
- while ((cr = Ustrchr(s, '\r')) != NULL) /* lose CRs */
- memmove(cr, cr + 1, (ptr--) - (cr - s));
- debug_printf("SMTP>> %s", s);
+
+ (void) string_from_gstring(g);
+ while ((cr = Ustrchr(g->s, '\r')) != NULL) /* lose CRs */
+ memmove(cr, cr + 1, (g->ptr--) - (cr - g->s));
+ debug_printf("SMTP>> %s", g->s);
}
helo_seen = TRUE;
if (helo_required && !helo_seen)
{
- smtp_printf("503 HELO or EHLO required\r\n");
+ smtp_printf("503 HELO or EHLO required\r\n", FALSE);
log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL from %s: no "
"HELO/EHLO given", host_and_ident(FALSE));
break;
if (smtp_accept_max_per_connection > 0 &&
smtp_mailcmd_count > smtp_accept_max_per_connection)
{
- smtp_printf("421 too many messages in this connection\r\n");
+ smtp_printf("421 too many messages in this connection\r\n", FALSE);
log_write(0, LOG_MAIN|LOG_REJECT, "rejected MAIL command %s: too many "
"messages in one connection", host_and_ident(TRUE));
break;
US"invalid data for AUTH");
goto COMMAND_LOOP;
}
- if (acl_smtp_mailauth == NULL)
+ if (!acl_smtp_mailauth)
{
ignore_msg = US"client not authenticated";
- rc = (sender_host_authenticated != NULL)? OK : FAIL;
+ rc = sender_host_authenticated ? OK : FAIL;
}
else
{
if (thismessage_size_limit > 0 && message_size > thismessage_size_limit)
{
- smtp_printf("552 Message size exceeds maximum permitted\r\n");
+ smtp_printf("552 Message size exceeds maximum permitted\r\n", FALSE);
log_write(L_size_reject,
LOG_MAIN|LOG_REJECT, "rejected MAIL FROM:<%s> %s: "
"message too big: size%s=%d max=%d",
(smtp_check_spool_space && message_size >= 0)?
message_size + 5000 : 0))
{
- smtp_printf("452 Space shortage, please try later\r\n");
+ smtp_printf("452 Space shortage, please try later\r\n", FALSE);
sender_address = NULL;
break;
}
}
else
{
- smtp_printf("501 %s: sender address must contain a domain\r\n",
+ smtp_printf("501 %s: sender address must contain a domain\r\n", FALSE,
smtp_cmd_data);
log_write(L_smtp_syntax_error,
LOG_MAIN|LOG_REJECT,
if (rc == OK || rc == DISCARD)
{
+ BOOL more = pipeline_response();
+
if (!user_msg)
- smtp_printf("%s%s%s", US"250 OK",
+ smtp_printf("%s%s%s", more, US"250 OK",
#ifndef DISABLE_PRDR
prdr_requested ? US", PRDR Requested" : US"",
#else
{
if (pipelining_advertised && last_was_rej_mail)
{
- smtp_printf("503 sender not yet given\r\n");
+ smtp_printf("503 sender not yet given\r\n", FALSE);
was_rej_mail = TRUE;
}
else
if (recipients_max_reject)
{
rcpt_fail_count++;
- smtp_printf("552 too many recipients\r\n");
+ smtp_printf("552 too many recipients\r\n", FALSE);
if (!toomany)
log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: message "
"rejected: sender=<%s> %s", sender_address, host_and_ident(TRUE));
else
{
rcpt_defer_count++;
- smtp_printf("452 too many recipients\r\n");
+ smtp_printf("452 too many recipients\r\n", FALSE);
if (!toomany)
log_write(0, LOG_MAIN|LOG_REJECT, "too many recipients: excess "
"temporarily rejected: sender=<%s> %s", sender_address,
if (rc == OK)
{
+ BOOL more = pipeline_response();
+
if (user_msg)
smtp_user_msg(US"250", user_msg);
else
- smtp_printf("250 Accepted\r\n");
+ smtp_printf("250 Accepted\r\n", more);
receive_add_recipient(recipient, -1);
/* Set the dsn flags in the recipients_list */
if (user_msg)
smtp_user_msg(US"250", user_msg);
else
- smtp_printf("250 Accepted\r\n");
+ smtp_printf("250 Accepted\r\n", FALSE);
rcpt_fail_count++;
discarded = TRUE;
log_write(0, LOG_MAIN|LOG_REJECT, "%s F=<%s> RCPT %s: "
DEBUG(D_receive) debug_printf("chunking state %d, %d bytes\n",
(int)chunking_state, chunking_data_left);
+ /* push the current receive_* function on the "stack", and
+ replace them by bdat_getc(), which in turn will use the lwr_receive_*
+ functions to do the dirty work. */
lwr_receive_getc = receive_getc;
lwr_receive_getbuf = receive_getbuf;
lwr_receive_ungetc = receive_ungetc;
+
receive_getc = bdat_getc;
receive_ungetc = bdat_ungetc;
+ dot_ends = FALSE;
+
goto DATA_BDAT;
}
case DATA_CMD:
HAD(SCH_DATA);
+ dot_ends = TRUE;
DATA_BDAT: /* Common code for DATA and BDAT */
if (!discarded && recipients_count <= 0)
smtp_respond(code, 3, FALSE, rcpt_smtp_response);
}
if (pipelining_advertised && last_was_rcpt)
- smtp_printf("503 Valid RCPT command must precede %s\r\n",
+ smtp_printf("503 Valid RCPT command must precede %s\r\n", FALSE,
smtp_names[smtp_connection_had[smtp_ch_index-1]]);
else
done = synprot_error(L_smtp_protocol_error, 503, NULL,
{
sender_address = NULL; /* This will allow a new MAIL without RSET */
sender_address_unrewritten = NULL;
- smtp_printf("554 Too many recipients\r\n");
+ smtp_printf("554 Too many recipients\r\n", FALSE);
break;
}
smtp_user_msg(US"354", user_msg);
else
smtp_printf(
- "354 Enter message, ending with \".\" on a line by itself\r\n");
+ "354 Enter message, ending with \".\" on a line by itself\r\n", FALSE);
}
#ifdef TCP_QUICKACK
if (!(address = parse_extract_address(smtp_cmd_data, &errmess,
&start, &end, &recipient_domain, FALSE)))
{
- smtp_printf("501 %s\r\n", errmess);
+ smtp_printf("501 %s\r\n", FALSE, errmess);
break;
}
break;
}
- smtp_printf("%s\r\n", s);
+ smtp_printf("%s\r\n", FALSE, s);
}
break;
}
cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_AUTH].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_TLS_AUTH].is_mail_cmd = TRUE;
- if (sender_helo_name != NULL)
+ if (sender_helo_name)
{
store_free(sender_helo_name);
sender_helo_name = NULL;
+ (tls_in.active >= 0 ? pcrpted : 0)
];
- sender_host_authenticated = NULL;
+ sender_host_auth_pubname = sender_host_authenticated = NULL;
authenticated_id = NULL;
sync_cmd_limit = NON_SYNC_CMD_NON_PIPELINING;
DEBUG(D_tls) debug_printf("TLS active\n");
if (rc == DEFER)
{
- smtp_printf("454 TLS currently unavailable\r\n");
+ smtp_printf("454 TLS currently unavailable\r\n", FALSE);
break;
}
if (user_msg)
smtp_respond(US"221", 3, TRUE, user_msg);
else
- smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+ smtp_printf("221 %s closing connection\r\n", FALSE, smtp_active_hostname);
log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
smtp_get_connection_info());
done = 2;
break;
default:
- smtp_printf("554 Security failure\r\n");
+ smtp_printf("554 Security failure\r\n", FALSE);
break;
}
- tls_close(TRUE, TRUE);
+ tls_close(TRUE, TLS_SHUTDOWN_NOWAIT);
break;
#endif
case NOOP_CMD:
HAD(SCH_NOOP);
- smtp_printf("250 OK\r\n");
+ smtp_printf("250 OK\r\n", FALSE);
break;
case HELP_CMD:
HAD(SCH_HELP);
- smtp_printf("214-Commands supported:\r\n");
+ smtp_printf("214-Commands supported:\r\n", TRUE);
{
uschar buffer[256];
buffer[0] = 0;
if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN");
if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN");
if (acl_smtp_vrfy != NULL) Ustrcat(buffer, " VRFY");
- smtp_printf("214%s\r\n", buffer);
+ smtp_printf("214%s\r\n", FALSE, buffer);
}
break;
just drop the call rather than sending QUIT, and it clutters up the logs.
*/
- if (sender_address != NULL || recipients_count > 0)
- log_write(L_lost_incoming_connection,
- LOG_MAIN,
- "unexpected %s while reading SMTP command from %s%s",
- sender_host_unknown? "EOF" : "disconnection",
- host_and_ident(FALSE), smtp_read_error);
+ if (sender_address || recipients_count > 0)
+ log_write(L_lost_incoming_connection, LOG_MAIN,
+ "unexpected %s while reading SMTP command from %s%s%s D=%s",
+ sender_host_unknown ? "EOF" : "disconnection",
+ tcp_in_fastopen && !tcp_in_fastopen_logged ? US"TFO " : US"",
+ host_and_ident(FALSE), smtp_read_error,
+ string_timesince(&smtp_connection_start)
+ );
- else log_write(L_smtp_connection, LOG_MAIN, "%s lost%s",
- smtp_get_connection_info(), smtp_read_error);
+ else
+ log_write(L_smtp_connection, LOG_MAIN, "%s %slost%s D=%s",
+ smtp_get_connection_info(),
+ tcp_in_fastopen && !tcp_in_fastopen_logged ? US"TFO " : US"",
+ smtp_read_error,
+ string_timesince(&smtp_connection_start)
+ );
done = 1;
break;
case ETRN_CMD:
HAD(SCH_ETRN);
- if (sender_address != NULL)
+ if (sender_address)
{
done = synprot_error(L_smtp_protocol_error, 503, NULL,
US"ETRN is not permitted inside a transaction");
since that is strictly the only kind of ETRN that can be implemented
according to the RFC. */
- if (smtp_etrn_command != NULL)
+ if (smtp_etrn_command)
{
uschar *error;
BOOL rc;
{
log_write(0, LOG_MAIN|LOG_PANIC, "failed to set up ETRN command: %s",
error);
- smtp_printf("458 Internal failure\r\n");
+ smtp_printf("458 Internal failure\r\n", FALSE);
break;
}
}
debug_printf("ETRN command is: %s\n", etrn_command);
debug_printf("ETRN command execution skipped\n");
}
- if (user_msg == NULL) smtp_printf("250 OK\r\n");
+ if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE);
else smtp_user_msg(US"250", user_msg);
break;
}
if (smtp_etrn_serialize && !enq_start(etrn_serialize_key, 1))
{
- smtp_printf("458 Already processing %s\r\n", smtp_cmd_data);
+ smtp_printf("458 Already processing %s\r\n", FALSE, smtp_cmd_data);
break;
}
{
log_write(0, LOG_MAIN|LOG_PANIC, "fork of process for ETRN failed: %s",
strerror(errno));
- smtp_printf("458 Unable to fork process\r\n");
+ smtp_printf("458 Unable to fork process\r\n", FALSE);
if (smtp_etrn_serialize) enq_end(etrn_serialize_key);
}
else
{
- if (user_msg == NULL) smtp_printf("250 OK\r\n");
+ if (user_msg == NULL) smtp_printf("250 OK\r\n", FALSE);
else smtp_user_msg(US"250", user_msg);
}
case BADCHAR_CMD:
done = synprot_error(L_smtp_syntax_error, 0, NULL, /* Just logs */
- US"NULL character(s) present (shown as '?')");
- smtp_printf("501 NULL characters are not allowed in SMTP commands\r\n");
+ US"NUL character(s) present (shown as '?')");
+ smtp_printf("501 NUL characters are not allowed in SMTP commands\r\n", FALSE);
break;
if (smtp_inend >= smtp_inbuffer + IN_BUFFER_SIZE)
smtp_inend = smtp_inbuffer + IN_BUFFER_SIZE - 1;
c = smtp_inend - smtp_inptr;
- if (c > 150) c = 150;
+ if (c > 150) c = 150; /* limit logged amount */
smtp_inptr[c] = 0;
incomplete_transaction_log(US"sync failure");
log_write(0, LOG_MAIN|LOG_REJECT, "SMTP protocol synchronization error "
#ifdef SUPPORT_PROXY
case PROXY_FAIL_IGNORE_CMD:
- smtp_printf("503 Command refused, required Proxy negotiation failed\r\n");
+ smtp_printf("503 Command refused, required Proxy negotiation failed\r\n", FALSE);
break;
#endif
return done - 2; /* Convert yield values */
}
+
+
+gstring *
+authres_smtpauth(gstring * g)
+{
+if (!sender_host_authenticated)
+ return g;
+
+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);
+else
+ g = string_catn(g, US") reason=x509.auth", 17);
+
+if (authenticated_sender)
+ g = string_append(g, 2, US" smtp.mailfrom=", authenticated_sender);
+return g;
+}
+
+
+
/* vi: aw ai sw=2
*/
/* End of smtp_in.c */