summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>2017-02-14 19:38:41 +0100
committerHeiko Schlittermann (HS12-RIPE) <hs@schlittermann.de>2017-02-14 22:12:05 +0100
commitd953610fa77ec9a08fad9c1a183181079a79674e (patch)
treebfbf3d61d23c1e3901b52167cf02ccd4776f9511
parentcff70eb1c1eb494ee5964dca33dc115181baafd7 (diff)
Fix missing line termination on the last received BDAT chunk (Bug 1974)
-rw-r--r--doc/doc-txt/ChangeLog4
-rw-r--r--src/src/receive.c30
2 files changed, 33 insertions, 1 deletions
diff --git a/doc/doc-txt/ChangeLog b/doc/doc-txt/ChangeLog
index 95d822df5..1f1f7aa83 100644
--- a/doc/doc-txt/ChangeLog
+++ b/doc/doc-txt/ChangeLog
@@ -114,6 +114,10 @@ JH/17 Fix inbound CHUNKING when DKIM disabled at runtime.
HS/01 Fix portability problems introduced by PP/08 for platforms where
realloc(NULL) is not equivalent to malloc() [SunOS et al].
+HS/02 Bug 1974: Fix missing line terminator on the last received BDAT
+ chunk. This allows us to accept broken chunked messages. We need a more
+ general solution here.
+
Exim version 4.88
-----------------
diff --git a/src/src/receive.c b/src/src/receive.c
index b152ceefb..5125a4f47 100644
--- a/src/src/receive.c
+++ b/src/src/receive.c
@@ -916,14 +916,41 @@ read_message_bdat_smtp(FILE *fout)
{
int linelength = 0, ch;
enum CH_STATE ch_state = LF_SEEN;
+BOOL fix_nl = FALSE;
for(;;)
{
switch ((ch = (bdat_getc)(GETC_BUFFER_UNLIMITED)))
{
case EOF: return END_EOF;
- case EOD: return END_DOT; /* normal exit */
case ERR: return END_PROTOCOL;
+ case EOD:
+ /* Nothing to get from the sender anymore. We check the last
+ character written to the spool.
+
+ RFC 3030 states, that BDAT chunks are normal text, terminated by CRLF.
+ If we would be strict, we would refuse such broken messages.
+ But we are liberal, so we fix it. It would be easy just to append
+ the "\n" to the spool.
+
+ But there are some more things (line counting, message size calculation and such),
+ that would need to be duplicated here. So we simply do some ungetc
+ trickery.
+ */
+ fseek(fout, -1, SEEK_CUR);
+ if (fgetc(fout) == '\n') return END_DOT;
+
+ if (linelength == -1) /* \r already seen (see below) */
+ {
+ DEBUG(D_receive) debug_printf("Add missing LF\n");
+ bdat_ungetc('\n');
+ continue;
+ }
+ DEBUG(D_receive) debug_printf("Add missing CRLF\n");
+ bdat_ungetc('\r'); /* not even \r was seen */
+ fix_nl = TRUE;
+
+ continue;
case '\0': body_zerocount++; break;
}
switch (ch_state)
@@ -944,6 +971,7 @@ for(;;)
else if (ch == '\r')
{
ch_state = CR_SEEN;
+ if (fix_nl) bdat_ungetc('\n');
continue; /* don't write CR */
}
break;