Hi,
I have a locally developed milter using the python-milter bindings which
seems to trigger a Postfix bug.
The milter in question uses the smfi_setmlreply() command to set a
multiline response as defined in rfc5321.
Multiline replies should result in the smtpd replying with something like
the following to e.g. a rejected DATA command:
450-4.7.1 Line 1
450-4.7.1 Line 2
450 4.7.1 Line 3
Postfix however seems to fold the reply into a single line:
450-4.7.1 Line 1 450-4.7.1 Line 2 450 4.7.1 Line 3
SMTP clients like swaks will now run into a timeout as the response
indicates a multiline response (XXX-) but no further lines are coming.
I did test my milter using the Net::Milter Perl package which simulates a
Milter-enabled MTA as well as actually running it against sendmail as well
as postfix.
Net::Milter and sendmail to reply correctly using well-formed multiline
responses while postfix does line-folding and thus breaks the SMTP specs.
The python code to trigger such a multiline response is super simple:
def eom(self):
self.setreply('550', '5.7.1', 'Test Milter rejection Reason',
'Test Milter rejection Reason 01',
'Test Milter rejection Reason 02')
return Milter.TEMPFAIL
In order to rule out a problem in the Python implementation, I also tested
a sample milter written in C using the libmilter library directly.
Modifying the sendmail-8.15.2/libmilter/example.c file to have the
following eom handler:
sfsistat
mlfi_eom(ctx)
SMFICTX *ctx;
{
char *rcode = "450";
char *xcode = "4.7.1";
char *reason = "Test Milter rejection Reason";
char *reason01 = "Test Milter rejection Reason 01";
char *reason02 = "Test Milter rejection Reason 02";
(void) mlfi_cleanup(ctx, false);
smfi_setmlreply(ctx, rcode, xcode, reason, reason01, reason02);
return SMFIS_TEMPFAIL;
}
Compiling the milter using gcc -o sample_milter example.c -lmilter
-lpthread and then starting it using ./sample_milter -p
inet:10020@localhost gives the following output in sendmail:
-> DATA
> 0000: 44 41 54 41 0D 0A DATA..
< 0000: 33 35 34 20 45 6E 74 65 72 20 6D 61 69 6C 2C 20 354.Enter.mail,.
< 0016: 65 6E 64 20 77 69 74 68 20 22 2E 22 20 6F 6E 20 end.with.".".on.
< 0032: 61 20 6C 69 6E 65 20 62 79 20 69 74 73 65 6C 66 a.line.by.itself
< 0048: 0D 0A ..
<- 354 Enter mail, end with "." on a line by itself
->
-> .
> 0000: 0D 0A 2E 0D 0A .....
< 0000: 34 35 30 2D 34 2E 37 2E 31 20 54 65 73 74 20 4D 450-4.7.1.Test.M
< 0016: 69 6C 74 65 72 20 72 65 6A 65 63 74 69 6F 6E 20 ilter.rejection.
< 0032: 52 65 61 73 6F 6E 0D 0A 34 35 30 2D 34 2E 37 2E Reason..450-4.7.
< 0048: 31 20 54 65 73 74 20 4D 69 6C 74 65 72 20 72 65 1.Test.Milter.re
< 0064: 6A 65 63 74 69 6F 6E 20 52 65 61 73 6F 6E 20 30 jection.Reason.0
< 0080: 31 0D 0A 34 35 30 20 34 2E 37 2E 31 20 54 65 73 1..450.4.7.1.Tes
< 0096: 74 20 4D 69 6C 74 65 72 20 72 65 6A 65 63 74 69 t.Milter.rejecti
< 0112: 6F 6E 20 52 65 61 73 6F 6E 20 30 32 0D 0A on.Reason.02..
<** 450-4.7.1 Test Milter rejection Reason
<** 450-4.7.1 Test Milter rejection Reason 01
<** 450 4.7.1 Test Milter rejection Reason 02
-> QUIT
> 0000: 51 55 49 54 0D 0A QUIT..
< 0000: 32 32 31 20 32 2E 30 2E 30 20 6D 61 69 6C 69 6E 221.2.0.0.mailin
< 0016: 30 31 2E 6D 78 2E 62 61 77 75 65 2E 6E 65 74 20 01.mx.bawue.net.
< 0032: 63 6C 6F 73 69 6E 67 20 63 6F 6E 6E 65 63 74 69 closing.connecti
< 0048: 6F 6E 0D 0A on..
<- 221 2.0.0 mailin01.mx.bawue.net closing connection
=== Connection closed with remote host.
The comparable postfix output:
$ swaks --server localhost --from '<>' --to test@localhost -d '.' --raw
[...]
-> DATA
> 0000: 44 41 54 41 0D 0A DATA..
< 0000: 33 35 34 20 45 6E 64 20 64 61 74 61 20 77 69 74 354.End.data.wit
< 0016: 68 20 3C 43 52 3E 3C 4C 46 3E 2E 3C 43 52 3E 3C h.<CR><LF>.<CR><
< 0032: 4C 46 3E 0D 0A LF>..
<- 354 End data with <CR><LF>.<CR><LF>
->
-> .
> 0000: 0D 0A 2E 0D 0A .....
< 0000: 34 35 30 2D 34 2E 37 2E 31 20 54 65 73 74 20 4D 450-4.7.1.Test.M
< 0016: 69 6C 74 65 72 20 72 65 6A 65 63 74 69 6F 6E 20 ilter.rejection.
< 0032: 52 65 61 73 6F 6E 20 20 34 35 30 2D 34 2E 37 2E Reason..450-4.7.
< 0048: 31 20 54 65 73 74 20 4D 69 6C 74 65 72 20 72 65 1.Test.Milter.re
< 0064: 6A 65 63 74 69 6F 6E 20 52 65 61 73 6F 6E 20 30 jection.Reason.0
< 0080: 31 20 20 34 35 30 20 34 2E 37 2E 31 20 54 65 73 1..450.4.7.1.Tes
< 0096: 74 20 4D 69 6C 74 65 72 20 72 65 6A 65 63 74 69 t.Milter.rejecti
< 0112: 6F 6E 20 52 65 61 73 6F 6E 20 30 32 0D 0A on.Reason.02..
<** Timeout (30 secs) waiting for server response
-> QUIT
> 0000: 51 55 49 54 0D 0A QUIT..
< 0000: 32 32 31 20 32 2E 30 2E 30 20 42 79 65 0D 0A
221.2.0.0.Bye..
<- 221 2.0.0 Bye
=== Connection closed with remote host.
The lines are separated by 0x20, 0x20 ([space][space]) rather than 0x0d,
0x0a (\n\r).
I had a quick look at the postfix source but did not find the right
codepath where this happens.
Any ideas if this really is a bug in Postfix or am I making a mistake
somewhere in my milter?
cheers,
Andreas