summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--src/src/globals.c4
-rw-r--r--src/src/globals.h2
-rw-r--r--src/src/macros.h3
-rw-r--r--src/src/receive.c6
-rw-r--r--src/src/smtp_in.c71
-rw-r--r--src/src/structs.h2
6 files changed, 76 insertions, 12 deletions
diff --git a/src/src/globals.c b/src/src/globals.c
index 5ff0f844b..2e3fe4074 100644
--- a/src/src/globals.c
+++ b/src/src/globals.c
@@ -492,7 +492,11 @@ int check_log_space = 0;
BOOL check_rfc2047_length = TRUE;
int check_spool_inodes = 0;
int check_spool_space = 0;
+
uschar *chunking_advertise_hosts = US"*";
+unsigned chunking_datasize = 0;
+chunking_state_t chunking_state= CHUNKING_NOT_OFFERED;
+
uschar *client_authenticator = NULL;
uschar *client_authenticated_id = NULL;
uschar *client_authenticated_sender = NULL;
diff --git a/src/src/globals.h b/src/src/globals.h
index e5bdec4a2..184a144f2 100644
--- a/src/src/globals.h
+++ b/src/src/globals.h
@@ -268,6 +268,8 @@ extern BOOL check_rfc2047_length; /* Check RFC 2047 encoded string length *
extern int check_spool_inodes; /* Minimum for message acceptance */
extern int check_spool_space; /* Minimum for message acceptance */
extern uschar *chunking_advertise_hosts; /* RFC 3030 CHUNKING */
+extern unsigned chunking_datasize;
+extern chunking_state_t chunking_state;
extern uschar *client_authenticator; /* Authenticator name used for smtp delivery */
extern uschar *client_authenticated_id; /* "login" name used for SMTP AUTH */
extern uschar *client_authenticated_sender; /* AUTH option to SMTP MAIL FROM (not yet used) */
diff --git a/src/src/macros.h b/src/src/macros.h
index 53abeb5c2..dbc49f01e 100644
--- a/src/src/macros.h
+++ b/src/src/macros.h
@@ -787,7 +787,8 @@ most recent SMTP commands. Must be kept in step with the list of names in
smtp_in.c that is used for creating the smtp_no_mail logging action. SCH_NONE
is "empty". */
-enum { SCH_NONE, SCH_AUTH, SCH_DATA, SCH_EHLO, SCH_ETRN, SCH_EXPN, SCH_HELO,
+enum { SCH_NONE, SCH_AUTH, SCH_DATA, SCH_BDAT,
+ SCH_EHLO, SCH_ETRN, SCH_EXPN, SCH_HELO,
SCH_HELP, SCH_MAIL, SCH_NOOP, SCH_QUIT, SCH_RCPT, SCH_RSET, SCH_STARTTLS,
SCH_VRFY };
diff --git a/src/src/receive.c b/src/src/receive.c
index 52e041c90..f6bdf4742 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -770,6 +770,9 @@ Arguments:
Returns: One of the END_xxx values indicating why it stopped reading
*/
+/*XXX CHUNKING: maybe a variant routing specialised for BDAT, assuming
+string RFC compliance ie. CRLF always? We still have to strip the CR
+but we are not dealing with variant lunacy or looking for the end-dot */
static int
read_message_data_smtp(FILE *fout)
@@ -1613,6 +1616,7 @@ next->text. */
for (;;)
{
+/*XXX CHUNKING: account for BDAT size & last, and do more chunks as needed */
int ch = (receive_getc)();
/* If we hit EOF on a SMTP connection, it's an error, since incoming
@@ -2831,6 +2835,7 @@ if (filter_test != FTEST_NONE)
return message_ended == END_DOT;
}
+/*XXX CHUNKING: need to cancel cutthrough under BDAT, for now */
/* Cutthrough delivery:
We have to create the Received header now rather than at the end of reception,
so the timestamp behaviour is a change to the normal case.
@@ -2928,6 +2933,7 @@ if (!ferror(data_file) && !(receive_feof)() && message_ended != END_DOT)
{
if (smtp_input)
{
+/*XXX CHUNKING: main data read, for message body */
message_ended = read_message_data_smtp(data_file);
receive_linecount++; /* The terminating "." line */
}
diff --git a/src/src/smtp_in.c b/src/src/smtp_in.c
index bc53166e5..b00537eb5 100644
--- a/src/src/smtp_in.c
+++ b/src/src/smtp_in.c
@@ -73,6 +73,7 @@ enum {
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 */
@@ -182,6 +183,7 @@ static smtp_cmd_list cmd_list[] = {
{ "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 },
@@ -205,9 +207,9 @@ It must be kept in step with the SCH_xxx enumerations. */
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 */
@@ -1525,6 +1527,7 @@ authenticated_sender = NULL;
bmi_run = 0;
bmi_verdicts = NULL;
#endif
+chunking_state = CHUNKING_NOT_OFFERED;
#ifndef DISABLE_DKIM
dkim_signers = NULL;
dkim_disable_verify = FALSE;
@@ -3766,17 +3769,20 @@ while (done <= 0)
if (!first) s = string_catn(s, &size, &ptr, US"\r\n", 2);
}
- /* 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
- secure connection. */
+ /* 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
+ secure connection. */
+
#ifdef SUPPORT_TLS
if (tls_in.active < 0 &&
verify_check_host(&tls_advertise_hosts) != FAIL)
@@ -4524,8 +4530,38 @@ while (done <= 0)
(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)
@@ -4540,10 +4576,13 @@ while (done <= 0)
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;
}
@@ -4555,11 +4594,21 @@ while (done <= 0)
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;
@@ -4702,7 +4751,7 @@ while (done <= 0)
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 */
diff --git a/src/src/structs.h b/src/src/structs.h
index 1dd363a5c..2b449a648 100644
--- a/src/src/structs.h
+++ b/src/src/structs.h
@@ -51,6 +51,8 @@ typedef struct ugid_block {
BOOL initgroups;
} ugid_block;
+typedef enum {CHUNKING_NOT_OFFERED, CHUNKING_OFFERED, CHUNKING_ACTIVE, CHUNKING_LAST} chunking_state_t;
+
/* Structure for holding information about a host for use mainly by routers,
but also used when checking lists of hosts and when transporting. Looking up
host addresses is done using this structure. */