(SMTP) support DATA better in the server and make sure to "escape" CRLF.CRLF

sequences in uploaded data. The test server doesn't "decode" escaped dot-lines
but instead test cases must be written to take them into account. Added test
case 803 to verify dot-escaping.
This commit is contained in:
Daniel Stenberg 2009-12-30 21:52:27 +00:00
parent d7cd761047
commit 5e6ffe353a
7 changed files with 206 additions and 5 deletions

View File

@ -639,6 +639,7 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
struct SessionHandle *data = conn->data;
struct FTP *smtp = data->state.proto.smtp;
CURLcode result=CURLE_OK;
ssize_t bytes_written;
(void)premature;
if(!smtp)
@ -653,6 +654,15 @@ static CURLcode smtp_done(struct connectdata *conn, CURLcode status,
conn->bits.close = TRUE; /* marked for closure */
result = status; /* use the already set error code */
}
else
/* TODO: make this work even when the socket is EWOULDBLOCK in this call! */
/* write to socket (send away data) */
result = Curl_write(conn,
conn->writesockfd, /* socket to send to */
SMTP_EOB, /* buffer pointer */
SMTP_EOB_LEN, /* buffer size */
&bytes_written); /* actually sent away */
/* clear these for next connection */
smtp->transfer = FTPTRANSFER_BODY;

View File

@ -58,4 +58,8 @@ extern const struct Curl_handler Curl_handler_smtps;
#define SMTP_EOB "\x0d\x0a\x2e\x0d\x0a"
#define SMTP_EOB_LEN 5
/* if found in data, replace it with this string instead */
#define SMTP_EOB_REPL "\x0d\x0a\x2e\x2e"
#define SMTP_EOB_REPL_LEN 4
#endif /* __SMTP_H */

View File

@ -784,6 +784,68 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
/* store number of bytes available for upload */
data->req.upload_present = nread;
#ifndef CURL_DISABLE_SMTP
if(conn->protocol & PROT_SMTP) {
/* When sending SMTP payload, we must detect CRLF.CRLF sequences in
* the data and make sure it is sent as CRLF..CRLF instead, as
* otherwise it will wrongly be detected as end of data by the server.
*/
struct smtp_conn *smtpc = &conn->proto.smtpc;
if(data->state.scratch == NULL)
data->state.scratch = malloc(2*BUFSIZE);
if(data->state.scratch == NULL) {
failf (data, "Failed to alloc scratch buffer!");
return CURLE_OUT_OF_MEMORY;
}
/* This loop can be improved by some kind of Boyer-Moore style of
approach but that is saved for later... */
for(i = 0, si = 0; i < nread; i++, si++) {
int left = nread - i;
if(left>= (SMTP_EOB_LEN-smtpc->eob)) {
if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
SMTP_EOB_LEN-smtpc->eob)) {
/* It matched, copy the replacement data to the target buffer
instead. Note that the replacement does not contain the
trailing CRLF but we instead continue to match on that one
to deal with repeated sequences. Like CRLF.CRLF.CRLF etc
*/
memcpy(&data->state.scratch[si], SMTP_EOB_REPL,
SMTP_EOB_REPL_LEN);
si+=SMTP_EOB_REPL_LEN-1; /* minus one since the for() increments
it */
i+=SMTP_EOB_LEN-smtpc->eob-1-2;
smtpc->eob = 0; /* start over */
continue;
}
}
else if(!memcmp(SMTP_EOB+smtpc->eob, &data->req.upload_fromhere[i],
left)) {
/* the last piece of the data matches the EOB so we can't send that
until we know the rest of it */
smtpc->eob += left;
break;
}
data->state.scratch[si] = data->req.upload_fromhere[i];
} /* for() */
if(si != nread) {
/* only use the new buffer if we replaced something */
nread = si;
/* upload from the new (replaced) buffer instead */
data->req.upload_fromhere = data->state.scratch;
/* set the new amount too */
data->req.upload_present = nread;
}
}
else
#endif /* CURL_DISABLE_SMTP */
/* convert LF to CRLF if so asked */
if((!sending_http_headers) &&
#ifdef CURL_DO_LINEEND_CONV
@ -837,10 +899,10 @@ static CURLcode readwrite_upload(struct SessionHandle *data,
/* write to socket (send away data) */
result = Curl_write(conn,
conn->writesockfd, /* socket to send to */
conn->writesockfd, /* socket to send to */
data->req.upload_fromhere, /* buffer pointer */
data->req.upload_present, /* buffer size */
&bytes_written); /* actually send away */
&bytes_written); /* actually sent */
if(result)
return result;

View File

@ -63,7 +63,7 @@ EXTRA_DIST = test1 test108 test117 test127 test20 test27 test34 test46 \
test1089 test1090 test1091 test1092 test1093 test1094 test1095 test1096 \
test1097 test560 test561 test1098 test1099 test562 test563 test1100 \
test564 test1101 test1102 test1103 test1104 test299 test310 test311 \
test312 test1105 test565 test800 test1106 test801 test566 test802
test312 test1105 test565 test800 test1106 test801 test566 test802 test803
filecheck:
@mkdir test-place; \

View File

@ -38,11 +38,15 @@ EHLO user
MAIL FROM:802@from
RCPT TO:802@foo
DATA
QUIT
</protocol>
<upload>
From: different
To: another
body
QUIT
</protocol>
.
</upload>
</verify>
</testcase>

64
tests/data/test803 Normal file
View File

@ -0,0 +1,64 @@
<testcase>
<info>
<keywords>
SMTP
</keywords>
</info>
#
# Server-side
<reply>
</reply>
#
# Client-side
<client>
<server>
smtp
</server>
<name>
SMTP with CRLF-dot-CRLF in data
</name>
<stdin>
From: different
To: another
.
.
.
body
</stdin>
<command>
smtp://%HOSTIP:%SMTPPORT -u user:secret --mail-rcpt 803@foo --mail-from 803@from -T -
</command>
</client>
#
# Verify data after the test has been "shot"
<verify>
<protocol>
EHLO user
MAIL FROM:803@from
RCPT TO:803@foo
DATA
QUIT
</protocol>
<upload>
From: different
To: another
..
..
..
body
.
</upload>
</verify>
</testcase>

View File

@ -473,12 +473,69 @@ sub DATA_smtp {
if($testno eq "verifiedserver") {
sendcontrol "554 WE ROOLZ: $$\r\n";
return 0; # don't wait for data now
}
else {
$testno =~ s/^([0-9]*).*/$1/;
sendcontrol "354 Show me the mail\r\n";
}
logmsg "===> rcpt $testno was $smtp_rcpt\n";
my $filename = "log/upload.$testno";
logmsg "Store test number $testno in $filename\n";
open(FILE, ">$filename") ||
return 0; # failed to open output
my $line;
my $ulsize=0;
my $disc=0;
my $raw;
while (5 == (sysread \*SFREAD, $line, 5)) {
if($line eq "DATA\n") {
my $i;
my $eob;
sysread \*SFREAD, $i, 5;
my $size = 0;
if($i =~ /^([0-9a-fA-F]{4})\n/) {
$size = hex($1);
}
sysread \*SFREAD, $line, $size;
$ulsize += $size;
print FILE $line if(!$nosave);
$raw .= $line;
if($raw =~ /\x0d\x0a\x2e\x0d\x0a\z/) {
# end of data marker!
$eob = 1;
}
logmsg "> Appending $size bytes to file\n";
if($eob) {
logmsg "Found SMTP EOB marker\n";
last;
}
}
elsif($line eq "DISC\n") {
# disconnect!
$disc=1;
last;
}
else {
logmsg "No support for: $line";
last;
}
}
if($nosave) {
print FILE "$ulsize bytes would've been stored here\n";
}
close(FILE);
logmsg "received $ulsize bytes upload\n";
}
sub RCPT_smtp {