I found a case where qpsmtpd-async detects the end of data marker incorrectly: the previous packet did not end with CRLF and the current packet starts with .CRLF. The code assumes that the previous packet ended with CRLF.
Attached are a test script and a suggested patch. Thanks, Radu Greab
#!/usr/bin/perl -w use strict; use Net::SMTP; my $smtp = Net::SMTP->new('localhost:25', Debug => 1); $smtp->mail(''); $smtp->to('[EMAIL PROTECTED]'); $smtp->data; $smtp->datasend("Subject: test\n\n"); $smtp->datasend('Chunk that does not end with CRLF'); sleep 1; # let qpsmtpd get the data as a packet $smtp->datasend(".\nAnother line that may get lost.\n"); $smtp->dataend; $smtp->quit;
=== lib/Qpsmtpd/PollServer.pm ================================================================== --- lib/Qpsmtpd/PollServer.pm (revision 147) +++ lib/Qpsmtpd/PollServer.pm (local) @@ -28,6 +28,7 @@ _extras _test_mode _transaction + _prev_crlf # did previous data chunk end with CRLF? ); use Qpsmtpd::Constants; use Qpsmtpd::Address; @@ -85,6 +86,7 @@ }; $self->{mode} = 'cmd'; $self->{_extras} = {}; + $self->{_prev_crlf} = 0; } sub respond { @@ -229,10 +231,15 @@ my $done = 0; my $remainder; - if ($data =~ s/^\.\r\n(.*)\z//ms) { + # end of data: either previous chunk ended with CRLF and + # current chunk starts with .CRLF or current chunk contains + # CRLF.CRLF + if (($self->{_prev_crlf} && $data =~ s/^\.\r\n(.*)\z//ms) || + $data =~ s/\r\n\.\r\n(.*)\z/\r\n/ms) { $remainder = $1; $done = 1; } + $self->{_prev_crlf} = $data =~ /\r\n\z/; # add a transaction->blocked check back here when we have line by line plugin access... unless (($self->{max_size} and $self->{data_size} > $self->{max_size})) {