I've knocked together a little Perl script to wrap the Anomy Sanitizer,
and sit in between an LMTP server and the MTA.
For those who haven't met LMTP yet, it's a cut down version of SMTP
designed to handle a generic linking between the MTA and the MDA. Among
the MDAs currently using it is the Cyrus IMAP server.
Because this is Perl, and uses references, it may prove somewhat nasty
to follow, so a quick and rapid description follows:
This script sits in between the system's LMTP server and the system's
MTA. It passes commands to the "real" LMTP server, and passes responses
back. Minimal parsing happens along the way, enough to know when we're
closing for real, and when we're closing because of some kind of error.
When we're given the DATA command, we pass that on, too, but pass the
actual data to an open2()ed Sanitizer.pl script. The output from that
goes into the system LMTP server connection we have open.
Making the script work as a SMTP filter would be trivial, in as much as
it works at all, however, SMTP may use extensions which would balk at th
man-in-the-middle attack we're effectively doing, and besides which, we
want to sanitize delivered email, and not submitted.
Most errors are handled naively - if things close on us, we issue a 421
and close the connection. This is poor, but enough, with the exception
that if the sanitizer.pl script actually crashes, we may generate a
SIGPIPE, and we die ourselves. More on that later.
Because a typical sanitizer.pl intervention, according to the documented
sendmail setup, currently uses more processes than I can reasonably
count to deliver the email (sendmail->sanitizer->sendmail->MDA -
obviously I can't count very high), the only time this process forks it
to create the sanitizer.pl process. Everything else is done in a single
process, cutting down the dedicated processes used for delivery by a
massive 1. On busy mailservers this might make a difference. This will
only run on one CPU, though there'll be plenty of other processes to tie
up your other CPU(s), if you have any. If you do end up with a spare
CPU, send it to me.
All state information about a connection is held within the map (sorry,
hash) that the main select loop uses, and is deleted on close(). In
theory, therefore, I shouldn't have any memory leaks with this design,
unless Perl doesn't free up references as I think it does.
I'd like to merge/incorporate the sanitizer.pl code. I'll work on this
later, however, since the code in this script runs on buffers only, and
the code in the sanitizer.pl script works on STDIN. Getting the two
scripts merged/melded would, of course, cut out one more process.
The coding style used in the script is to my own taste. I make no
Current Code Level:
This script is ALPHA quality. And that's being generous. It has not been
used in any production system. It has been tested, to an extent, and
actually runs, much to my surprise.
1) Open up the script in your least favourite text editor. Struggle to
find the line '## CONFIG START'.
2) Randomly change the values you find there until things might work.
What the values mean should be explained on the previous line. What the
values as supplied are is supplied at the end of the line.
3) Experiment with different values for "preopen" and "max", until you
realise they're unimplemented anyway.
4) Save the script, and run it.
This software has no warranty including fitness for purpose, and the
author disclaims all responsibility in so far as he is able. In fact,
the author would like to suggest that calling this software is a very
charitable act, and if it runs at all, let alone does what you might
expect it to, then the author would be really interested.
The software is copyright 2001 Dave Cridland.
You may distribute it under the GPL, version 2, or at your discretion
any later version. If the GPL is too restrictive, please contact the
author, who will consider LGPL terms instead.
Dave Cridland <email@example.com>