* Exim - an Internet mail transport agent *
*************************************************/
-/* Copyright (c) University of Cambridge 1995 - 2015 */
+/* Copyright (c) University of Cambridge 1995 - 2016 */
/* See the file NOTICE for conditions of use and distribution. */
/* Functions for handling an incoming SMTP call. */
ETRN_CMD, /* This by analogy with TURN from the RFC */
STARTTLS_CMD, /* Required by the STARTTLS RFC */
TLS_AUTH_CMD, /* auto-command at start of SSL */
+ BDAT_CMD, /* Implied by RFC3030 "After all MAIL and..." */
/* This is a dummy to identify the non-sync commands when pipelining */
{ "mail from:", sizeof("mail from:")-1, MAIL_CMD, TRUE, TRUE },
{ "rcpt to:", sizeof("rcpt to:")-1, RCPT_CMD, TRUE, TRUE },
{ "data", sizeof("data")-1, DATA_CMD, FALSE, TRUE },
+ { "bdat", sizeof("bdat")-1, BDAT_CMD, TRUE, TRUE },
{ "quit", sizeof("quit")-1, QUIT_CMD, FALSE, TRUE },
{ "noop", sizeof("noop")-1, NOOP_CMD, TRUE, FALSE },
{ "etrn", sizeof("etrn")-1, ETRN_CMD, TRUE, FALSE },
static uschar *smtp_names[] =
{
- US"NONE", US"AUTH", US"DATA", US"EHLO", US"ETRN", US"EXPN", US"HELO",
- US"HELP", US"MAIL", US"NOOP", US"QUIT", US"RCPT", US"RSET", US"STARTTLS",
- US"VRFY" };
+ 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",
+ US"STARTTLS", US"VRFY" };
static uschar *protocols_local[] = {
US"local-smtp", /* HELO */
memset(sender_address_cache, 0, sizeof(sender_address_cache));
memset(sender_domain_cache, 0, sizeof(sender_domain_cache));
-#ifndef DISABLE_PRDR
-prdr_requested = FALSE;
-#endif
-
-/* Reset the DSN flags */
-dsn_ret = 0;
-dsn_envid = NULL;
-
authenticated_sender = NULL;
#ifdef EXPERIMENTAL_BRIGHTMAIL
bmi_run = 0;
bmi_verdicts = NULL;
#endif
+chunking_state = CHUNKING_NOT_OFFERED;
#ifndef DISABLE_DKIM
dkim_signers = NULL;
dkim_disable_verify = FALSE;
dkim_collect_input = FALSE;
#endif
+dsn_ret = 0;
+dsn_envid = NULL;
+#ifndef DISABLE_PRDR
+prdr_requested = FALSE;
+#endif
#ifdef EXPERIMENTAL_SPF
spf_header_comment = NULL;
spf_received = NULL;
/* Apply SMTP rewrite, then extract address. Don't allow "<>" as a
recipient address */
- recipient = ((rewrite_existflags & rewrite_smtp) != 0)?
- 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;
- /* rfc821_domains = TRUE; << no longer needed */
recipient = parse_extract_address(recipient, &errmess, &start, &end,
&recipient_domain, FALSE);
- /* rfc821_domains = FALSE; << no longer needed */
- if (recipient == NULL)
+ if (!recipient)
/* The function moan_smtp_batch() does not return. */
moan_smtp_batch(smtp_cmd_buffer, "501 %s", errmess);
esc = US""; /* Default extended status code */
esclen = 0; /* Length of esc */
-if (user_msg == NULL)
+if (!user_msg)
{
- s = expand_string(smtp_banner);
- if (s == NULL)
+ if (!(s = expand_string(smtp_banner)))
log_write(0, LOG_MAIN|LOG_PANIC_DIE, "Expansion of \"%s\" (smtp_banner) "
"failed: %s", smtp_banner, expand_string_message);
}
{
int len;
uschar *linebreak = Ustrchr(p, '\n');
- ss = string_cat(ss, &size, &ptr, code, 3);
+ ss = string_catn(ss, &size, &ptr, code, 3);
if (linebreak == NULL)
{
len = Ustrlen(p);
- ss = string_cat(ss, &size, &ptr, US" ", 1);
+ ss = string_catn(ss, &size, &ptr, US" ", 1);
}
else
{
len = linebreak - p;
- ss = string_cat(ss, &size, &ptr, US"-", 1);
+ ss = string_catn(ss, &size, &ptr, US"-", 1);
}
- ss = string_cat(ss, &size, &ptr, esc, esclen);
- ss = string_cat(ss, &size, &ptr, p, len);
- ss = string_cat(ss, &size, &ptr, US"\r\n", 2);
+ 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);
p += len;
if (linebreak != NULL) p++;
}
if (log_reject_target != 0)
{
#ifdef SUPPORT_TLS
- uschar * s = s_tlslog(NULL, NULL, NULL);
- if (!s) s = US"";
+ uschar * tls = s_tlslog(NULL, NULL, NULL);
+ if (!tls) tls = US"";
#else
- uschar * s = US"";
+ uschar * tls = US"";
#endif
- log_write(0, log_reject_target, "%s%s %s%srejected %s%s",
- host_and_ident(TRUE), s,
- sender_info, (rc == FAIL)? US"" : US"temporarily ", what, log_msg);
+ log_write(0, log_reject_target, "%s%s%s %s%srejected %s%s",
+ LOGGING(dnssec) && sender_host_dnssec ? US" DS" : US"",
+ host_and_ident(TRUE),
+ tls,
+ sender_info,
+ rc == FAIL ? US"" : US"temporarily ",
+ what, log_msg);
}
if (!drop) return 0;
+
+
+static int
+qualify_recipient(uschar ** recipient, uschar * smtp_cmd_data, uschar * tag)
+{
+int rd;
+if (allow_unqualified_recipient || strcmpic(*recipient, US"postmaster") == 0)
+ {
+ DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
+ *recipient);
+ rd = Ustrlen(recipient) + 1;
+ *recipient = rewrite_address_qualify(*recipient, TRUE);
+ return rd;
+ }
+smtp_printf("501 %s: recipient address must contain a domain\r\n",
+ smtp_cmd_data);
+log_write(L_smtp_syntax_error,
+ LOG_MAIN|LOG_REJECT, "unqualified %s rejected: <%s> %s%s",
+ tag, *recipient, host_and_ident(TRUE), host_lookup_msg);
+return 0;
+}
+
+
+
+
/*************************************************
* Initialize for SMTP incoming message *
*************************************************/
if (sender_host_address != NULL)
{
- s = string_cat(s, &size, &ptr, US" [", 2);
- s = string_cat(s, &size, &ptr, sender_host_address,
- Ustrlen(sender_host_address));
- s = string_cat(s, &size, &ptr, US"]", 1);
+ 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);
}
}
size = ptr + 1;
}
- s = string_cat(s, &size, &ptr, US"\r\n", 2);
+ s = string_catn(s, &size, &ptr, US"\r\n", 2);
/* If we received EHLO, we must create a multiline response which includes
the functions supported. */
{
sprintf(CS big_buffer, "%.3s-SIZE %d\r\n", smtp_code,
thismessage_size_limit);
- s = string_cat(s, &size, &ptr, big_buffer, Ustrlen(big_buffer));
+ s = string_cat(s, &size, &ptr, big_buffer);
}
else
{
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-SIZE\r\n", 7);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, US"-SIZE\r\n", 7);
}
/* Exim does not do protocol conversion or data conversion. It is 8-bit
if (accept_8bitmime)
{
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-8BITMIME\r\n", 11);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, US"-8BITMIME\r\n", 11);
}
/* Advertise DSN support if configured to do so. */
if (verify_check_host(&dsn_advertise_hosts) != FAIL)
{
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-DSN\r\n", 6);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, US"-DSN\r\n", 6);
dsn_advertised = TRUE;
}
if (acl_smtp_etrn != NULL)
{
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-ETRN\r\n", 7);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, US"-ETRN\r\n", 7);
}
/* Advertise EXPN if there's an ACL checking whether a host is
if (acl_smtp_expn != NULL)
{
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-EXPN\r\n", 7);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, 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_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-PIPELINING\r\n", 13);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, US"-PIPELINING\r\n", 13);
sync_cmd_limit = NON_SYNC_CMD_PIPELINING;
pipelining_advertised = TRUE;
}
int saveptr;
if (first)
{
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-AUTH", 5);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, US"-AUTH", 5);
first = FALSE;
auth_advertised = TRUE;
}
saveptr = ptr;
- s = string_cat(s, &size, &ptr, US" ", 1);
- s = string_cat(s, &size, &ptr, au->public_name,
- Ustrlen(au->public_name));
+ 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_cat(s, &size, &ptr, US"\r\n", 2);
+ if (!first) s = string_catn(s, &size, &ptr, 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);
+ chunking_state = CHUNKING_OFFERED;
+ }
+
/* Advertise TLS (Transport Level Security) aka SSL (Secure Socket Layer)
if it has been included in the binary, and the host matches
tls_advertise_hosts. We must *not* advertise if we are already in a
if (tls_in.active < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
{
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-STARTTLS\r\n", 11);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, 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_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-PRDR\r\n", 7);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, US"-PRDR\r\n", 7);
}
#endif
if ( accept_8bitmime
&& verify_check_host(&smtputf8_advertise_hosts) != FAIL)
{
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US"-SMTPUTF8\r\n", 11);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, US"-SMTPUTF8\r\n", 11);
smtputf8_advertised = TRUE;
}
#endif
/* Finish off the multiline reply with one that is always available. */
- s = string_cat(s, &size, &ptr, smtp_code, 3);
- s = string_cat(s, &size, &ptr, US" HELP\r\n", 7);
+ s = string_catn(s, &size, &ptr, smtp_code, 3);
+ s = string_catn(s, &size, &ptr, US" HELP\r\n", 7);
}
/* Terminate the string (for debug), write it, and note that HELO/EHLO
global_rewrite_rules)
: smtp_cmd_data;
- /* rfc821_domains = TRUE; << no longer needed */
raw_sender =
parse_extract_address(raw_sender, &errmess, &start, &end, &sender_domain,
TRUE);
- /* rfc821_domains = FALSE; << no longer needed */
- if (raw_sender == NULL)
+ if (!raw_sender)
{
done = synprot_error(L_smtp_syntax_error, 501, smtp_cmd_data, errmess);
break;
/* Apply SMTP rewriting then extract the working address. Don't allow "<>"
as a recipient address */
- recipient = ((rewrite_existflags & rewrite_smtp) != 0)?
- rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
- global_rewrite_rules) : smtp_cmd_data;
-
- /* rfc821_domains = TRUE; << no longer needed */
- recipient = parse_extract_address(recipient, &errmess, &start, &end,
- &recipient_domain, FALSE);
- /* rfc821_domains = FALSE; << no longer needed */
+ recipient = rewrite_existflags & rewrite_smtp
+ ? rewrite_one(smtp_cmd_data, rewrite_smtp, NULL, FALSE, US"",
+ global_rewrite_rules)
+ : smtp_cmd_data;
- if (recipient == NULL)
+ if (!(recipient = parse_extract_address(recipient, &errmess, &start, &end,
+ &recipient_domain, FALSE)))
{
done = synprot_error(L_smtp_syntax_error, 501, smtp_cmd_data, errmess);
rcpt_fail_count++;
we must always qualify this address, regardless. */
if (recipient_domain == 0)
- {
- if (allow_unqualified_recipient ||
- strcmpic(recipient, US"postmaster") == 0)
- {
- DEBUG(D_receive) debug_printf("unqualified address %s accepted\n",
- recipient);
- recipient_domain = Ustrlen(recipient) + 1;
- recipient = rewrite_address_qualify(recipient, TRUE);
- }
- else
+ if (!(recipient_domain = qualify_recipient(&recipient, smtp_cmd_data,
+ US"recipient")))
{
rcpt_fail_count++;
- smtp_printf("501 %s: recipient address must contain a domain\r\n",
- smtp_cmd_data);
- log_write(L_smtp_syntax_error,
- LOG_MAIN|LOG_REJECT, "unqualified recipient rejected: "
- "<%s> %s%s", recipient, host_and_ident(TRUE),
- host_lookup_msg);
break;
}
- }
/* Check maximum allowed */
(often indicating some kind of system error), it is helpful to include it
with the DATA rejection (an idea suggested by Tony Finch). */
+ case BDAT_CMD:
+ HAD(SCH_BDAT);
+ {
+ int n;
+
+ if (chunking_state != CHUNKING_OFFERED)
+ {
+ done = synprot_error(L_smtp_protocol_error, 503, NULL,
+ US"BDAT command used when CHUNKING not advertised");
+ break;
+ }
+
+ /* grab size, endmarker */
+
+ if (sscanf(CS smtp_cmd_data, "%u %n", &chunking_datasize, &n) < 1)
+ {
+ done = synprot_error(L_smtp_protocol_error, 503, NULL,
+ US"missing size for BDAT command");
+ break;
+ }
+ chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0
+ ? CHUNKING_LAST : CHUNKING_ACTIVE;
+
+ DEBUG(D_any)
+ debug_printf("chunking state %d\n", (int)chunking_state);
+ goto DATA_BDAT;
+ }
+
case DATA_CMD:
HAD(SCH_DATA);
+
+ DATA_BDAT: /* Common code for DATA and BDAT */
if (!discarded && recipients_count <= 0)
{
if (rcpt_smtp_response_same && rcpt_smtp_response != NULL)
smtp_respond(code, 3, FALSE, rcpt_smtp_response);
}
if (pipelining_advertised && last_was_rcpt)
- smtp_printf("503 Valid RCPT command must precede DATA\r\n");
+ smtp_printf("503 Valid RCPT command must precede %s\r\n",
+ smtp_names[smtp_connection_had[smtp_ch_index-1]]);
else
done = synprot_error(L_smtp_protocol_error, 503, NULL,
- US"valid RCPT command must precede DATA");
+ smtp_connection_had[smtp_ch_index-1] == SCH_DATA
+ ? US"valid RCPT command must precede DATA"
+ : US"valid RCPT command must precede BDAT");
break;
}
break;
}
+ /* No go-ahead output for BDAT */
+
+ if (smtp_connection_had[smtp_ch_index-1] == SCH_BDAT)
+ {
+ rc = OK;
+ break;
+ }
+
/* If there is an ACL, re-check the synchronization afterwards, since the
ACL may have delayed. To handle cutthrough delivery enforce a dummy call
to get the DATA command sent. */
- if (acl_smtp_predata == NULL && cutthrough.fd < 0) rc = OK; else
+ if (acl_smtp_predata == NULL && cutthrough.fd < 0)
+ rc = OK;
+ else
{
uschar * acl= acl_smtp_predata ? acl_smtp_predata : US"accept";
enable_dollar_recipients = TRUE;
HAD(SCH_VRFY);
- if(!(address = parse_extract_address(smtp_cmd_data, &errmess, &start, &end,
- &recipient_domain, FALSE)))
+ if (!(address = parse_extract_address(smtp_cmd_data, &errmess,
+ &start, &end, &recipient_domain, FALSE)))
+ {
smtp_printf("501 %s\r\n", errmess);
+ break;
+ }
+
+ if (recipient_domain == 0)
+ if (!(recipient_domain = qualify_recipient(&address, smtp_cmd_data,
+ US"verify")))
+ break;
- else if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy,
+ if ((rc = acl_check(ACL_WHERE_VRFY, address, acl_smtp_vrfy,
&user_msg, &log_msg)) != OK)
done = smtp_handle_acl_fail(ACL_WHERE_VRFY, rc, user_msg, log_msg);
else
{
- uschar *s = NULL;
+ uschar * s = NULL;
+ address_item * addr = deliver_make_addr(address, FALSE);
- address_item *addr = deliver_make_addr(address, FALSE);
switch(verify_address(addr, NULL, vopt_is_recipient | vopt_qualify, -1,
-1, -1, NULL, NULL, NULL))
{
if (receive_smtp_buffered())
{
DEBUG(D_any)
- debug_printf("Non-empty input buffer after STARTTLS; naive attack?");
+ debug_printf("Non-empty input buffer after STARTTLS; naive attack?\n");
if (tls_in.active < 0)
smtp_inend = smtp_inptr = smtp_inbuffer;
/* and if TLS is already active, tls_server_start() should fail */
break;
}
etrn_command = US"exim -R";
- argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE, 2, US"-R",
- smtp_cmd_data);
+ argv = CUSS child_exec_exim(CEE_RETURN_ARGV, TRUE, NULL, TRUE,
+ *queue_name ? 4 : 2,
+ US"-R", smtp_cmd_data,
+ US"-MCG", queue_name);
}
/* If we are host-testing, don't actually do anything. */