* 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. */
MAIL_CMD, RCPT_CMD, RSET_CMD,
+ /* RFC3030 section 2: "After all MAIL and RCPT responses are collected and
+ processed the message is sent using a series of BDAT commands"
+ implies that BDAT should be synchronized. However, we see Google, at least,
+ sending MAIL,RCPT,BDAT-LAST in a single packet, clearly not waiting for
+ processing of the RPCT response(s). We shall do the same, and not require
+ synch for BDAT. */
+
+ BDAT_CMD,
+
/* This is a dummy to identify the non-sync commands when not pipelining */
NON_SYNC_CMD_NON_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 */
static int smtp_had_error;
+/* forward declarations */
+int bdat_ungetc(int ch);
+static int smtp_read_command(BOOL check_sync);
+static int synprot_error(int type, int code, uschar *data, uschar *errmess);
+static void smtp_quit_handler(uschar **, uschar **);
+static void smtp_rset_handler(void);
+
/*************************************************
* SMTP version of getc() *
*************************************************/
+/* Get a byte from the smtp input, in CHUNKING mode. Handle ack of the
+previous BDAT chunk and getting new ones when we run out. Uses the
+underlying smtp_getc or tls_getc both for that and for getting the
+(buffered) data byte. EOD signals (an expected) no further data.
+ERR signals a protocol error, and EOF a closed input stream.
+
+Called from read_bdat_smtp() in receive.c for the message body, but also
+by the headers read loop in receive_msg(); manipulates chunking_state
+to handle the BDAT command/response.
+Placed here due to the correlation with the above smtp_getc(), which it wraps,
+and also by the need to do smtp command/response handling.
+
+Arguments: none
+Returns: the next character or ERR, EOD or EOF
+*/
+
+int
+bdat_getc(void)
+{
+uschar * user_msg = NULL;
+uschar * log_msg;
+
+for(;;)
+ {
+ if (chunking_data_left-- > 0)
+ return lwr_receive_getc();
+
+ receive_getc = lwr_receive_getc;
+ receive_ungetc = lwr_receive_ungetc;
+
+ /* If not the last, ack the received chunk. The last response is delayed
+ until after the data ACL decides on it */
+ /*XXX find that "last response" and append the chunk size */
+
+ if (chunking_state == CHUNKING_LAST)
+ return EOD;
+
+ chunking_state = CHUNKING_OFFERED;
+ smtp_printf("250 %u byte chunk received\r\n", chunking_datasize);
+
+ /* Expect another BDAT cmd from input. RFC 3030 says nothing about
+ QUIT, RSET or NOOP but handling them seems obvious */
+
+next_cmd:
+ switch(smtp_read_command(TRUE))
+ {
+ default:
+ (void) synprot_error(L_smtp_protocol_error, 503, NULL,
+ US"only BDAT permissible after non-LAST BDAT");
+
+ repeat_until_rset:
+ switch(smtp_read_command(TRUE))
+ {
+ case QUIT_CMD: smtp_quit_handler(&user_msg, &log_msg); /*FALLTHROUGH */
+ case EOF_CMD: return EOF;
+ case RSET_CMD: smtp_rset_handler(); return ERR;
+ default: if (synprot_error(L_smtp_protocol_error, 503, NULL,
+ US"only RSET accepted now") > 0)
+ return EOF;
+ goto repeat_until_rset;
+ }
+
+ case QUIT_CMD:
+ smtp_quit_handler(&user_msg, &log_msg);
+ /*FALLTHROUGH*/
+ case EOF_CMD:
+ return EOF;
+
+ case RSET_CMD:
+ smtp_rset_handler();
+ return ERR;
+
+ case NOOP_CMD:
+ HAD(SCH_NOOP);
+ smtp_printf("250 OK\r\n");
+ goto next_cmd;
+
+ case BDAT_CMD:
+ {
+ int n;
+
+ if (sscanf(CS smtp_cmd_data, "%u %n", &chunking_datasize, &n) < 1)
+ {
+ (void) synprot_error(L_smtp_protocol_error, 501, NULL,
+ US"missing size for BDAT command");
+ return ERR;
+ }
+ chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0
+ ? CHUNKING_LAST : CHUNKING_ACTIVE;
+ chunking_data_left = chunking_datasize;
+
+ if (chunking_datasize == 0)
+ if (chunking_state == CHUNKING_LAST)
+ return EOD;
+ else
+ {
+ (void) synprot_error(L_smtp_protocol_error, 504, NULL,
+ US"zero size for BDAT command");
+ goto repeat_until_rset;
+ }
+
+ receive_getc = bdat_getc;
+ receive_ungetc = bdat_ungetc;
+ break; /* to top of main loop */
+ }
+ }
+ }
+}
+
+
+
+
/*************************************************
* SMTP version of ungetc() *
*************************************************/
int
smtp_ungetc(int ch)
{
-*(--smtp_inptr) = ch;
+*--smtp_inptr = ch;
return ch;
}
+int
+bdat_ungetc(int ch)
+{
+chunking_data_left++;
+return lwr_receive_ungetc(ch);
+}
+
/*************************************************
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;
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;
+}
+
+
+
+
+static void
+smtp_quit_handler(uschar ** user_msgp, uschar ** log_msgp)
+{
+HAD(SCH_QUIT);
+incomplete_transaction_log(US"QUIT");
+if (acl_smtp_quit != NULL)
+ {
+ int rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, user_msgp, log_msgp);
+ if (rc == ERROR)
+ log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
+ *log_msgp);
+ }
+if (*user_msgp)
+ smtp_respond(US"221", 3, TRUE, *user_msgp);
+else
+ smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
+
+#ifdef SUPPORT_TLS
+tls_close(TRUE, TRUE);
+#endif
+
+log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
+ smtp_get_connection_info());
+}
+
+
+static void
+smtp_rset_handler(void)
+{
+HAD(SCH_RSET);
+incomplete_transaction_log(US"RSET");
+smtp_printf("250 Reset OK\r\n");
+cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE;
+}
+
+
+
/*************************************************
* Initialize for SMTP incoming message *
*************************************************/
smtp_reset(reset_point);
message_ended = END_NOTSTARTED;
+chunking_state = chunking_offered ? CHUNKING_OFFERED : CHUNKING_NOT_OFFERED;
+
cmd_list[CMD_LIST_RSET].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_HELO].is_mail_cmd = TRUE;
cmd_list[CMD_LIST_EHLO].is_mail_cmd = TRUE;
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_offered = TRUE;
+ 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, 501, NULL,
+ US"missing size for BDAT command");
+ break;
+ }
+ chunking_state = strcmpic(smtp_cmd_data+n, US"LAST") == 0
+ ? CHUNKING_LAST : CHUNKING_ACTIVE;
+ chunking_data_left = chunking_datasize;
+
+ lwr_receive_getc = receive_getc;
+ lwr_receive_ungetc = receive_ungetc;
+ receive_getc = bdat_getc;
+ receive_ungetc = bdat_ungetc;
+
+ 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;
}
- /* 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
- {
- uschar * acl= acl_smtp_predata ? acl_smtp_predata : US"accept";
- enable_dollar_recipients = TRUE;
- rc = acl_check(ACL_WHERE_PREDATA, NULL, acl, &user_msg,
- &log_msg);
- enable_dollar_recipients = FALSE;
- if (rc == OK && !check_sync()) goto SYNC_FAILURE;
+ if (chunking_state > CHUNKING_OFFERED)
+ { /* No predata ACL or go-ahead output for BDAT */
+ rc = OK;
}
-
- if (rc == OK)
+ else
{
- uschar * code;
- code = US"354";
- if (user_msg == NULL)
- smtp_printf("%s Enter message, ending with \".\" on a line by itself\r\n", code);
- else smtp_user_msg(code, user_msg);
- done = 3;
- message_ended = END_NOTENDED; /* Indicate in middle of data */
+ /* 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
+ {
+ uschar * acl = acl_smtp_predata ? acl_smtp_predata : US"accept";
+ enable_dollar_recipients = TRUE;
+ rc = acl_check(ACL_WHERE_PREDATA, NULL, acl, &user_msg,
+ &log_msg);
+ enable_dollar_recipients = FALSE;
+ if (rc == OK && !check_sync())
+ goto SYNC_FAILURE;
+
+ if (rc != OK)
+ { /* Either the ACL failed the address, or it was deferred. */
+ done = smtp_handle_acl_fail(ACL_WHERE_PREDATA, rc, user_msg, log_msg);
+ break;
+ }
+ }
+
+ if (user_msg)
+ smtp_user_msg(US"354", user_msg);
+ else
+ smtp_printf(
+ "354 Enter message, ending with \".\" on a line by itself\r\n");
}
- /* Either the ACL failed the address, or it was deferred. */
+ done = 3;
+ message_ended = END_NOTENDED; /* Indicate in middle of data */
- else
- done = smtp_handle_acl_fail(ACL_WHERE_PREDATA, rc, user_msg, log_msg);
break;
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 */
message. */
case QUIT_CMD:
- HAD(SCH_QUIT);
- incomplete_transaction_log(US"QUIT");
- if (acl_smtp_quit != NULL)
- {
- rc = acl_check(ACL_WHERE_QUIT, NULL, acl_smtp_quit, &user_msg, &log_msg);
- if (rc == ERROR)
- log_write(0, LOG_MAIN|LOG_PANIC, "ACL for QUIT returned ERROR: %s",
- log_msg);
- }
- if (user_msg == NULL)
- smtp_printf("221 %s closing connection\r\n", smtp_active_hostname);
- else
- smtp_respond(US"221", 3, TRUE, user_msg);
-
- #ifdef SUPPORT_TLS
- tls_close(TRUE, TRUE);
- #endif
-
+ smtp_quit_handler(&user_msg, &log_msg);
done = 2;
- log_write(L_smtp_connection, LOG_MAIN, "%s closed by QUIT",
- smtp_get_connection_info());
break;
case RSET_CMD:
- HAD(SCH_RSET);
- incomplete_transaction_log(US"RSET");
+ smtp_rset_handler();
smtp_reset(reset_point);
toomany = FALSE;
- smtp_printf("250 Reset OK\r\n");
- cmd_list[CMD_LIST_RSET].is_mail_cmd = FALSE;
break;
verify_check_host(&tls_advertise_hosts) != FAIL)
Ustrcat(buffer, " STARTTLS");
#endif
- Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA");
+ Ustrcat(buffer, " HELO EHLO MAIL RCPT DATA BDAT");
Ustrcat(buffer, " NOOP QUIT RSET HELP");
if (acl_smtp_etrn != NULL) Ustrcat(buffer, " ETRN");
if (acl_smtp_expn != NULL) Ustrcat(buffer, " EXPN");
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. */