mailmunge−protocol − Conventions used by "mailmunge" to communicate with filter programs.
mailmunge and mailmunge−multiplexor provide a mechanism for hooking scripts and programs into Sendmail's milter API.
The milter API is multi−threaded and written in C; "mailmunge" lets you write single−threaded filters written in the language of your choice. Some of the flexibility and speed of milter is sacrificed, but the ease of writing filters more than compensates for this slight loss.
This manual describes how "mailmunge" communicates with the filter program, and gives you enough information to write your own filter program as a replacement for the "Mailmunge" Perl modules if you wish.
The protocol is a simple line−based protocol. The filter program runs in a loop, communicating with the "mailmunge−multiplexor" program on its standard input and standard output file descriptors.
Each time through the loop, the server reads a single line from standard input; this line contains a command. The server executes the command and then outputs a single−line response to standard−output.
For the "scan" command, additional information is placed in a temporary working directory. When the filter processes the "scan" command (typically in its "filter_message" function), it is expected to populate the working directory with more files, which communicate the scan results back to "mailmunge".
The filter program may be invoked in one of four ways. As mentioned above, the filter program is expected to run in a loop, accepting commands from standard input and writing responses to standard output. It should exit if it receives end−of−file on standard input.
|
1. |
filter_prog −server |
If the program is invoked with the single argument "−server", it is expected to run as a server as described above.
|
2. |
filter_prog −serveru |
If the program is invoked with the single argument "−serveru", it is expected to run as a server. In addition, anything it prints to file descriptor 3 is used to update the "worker status" field in the multiplexor. This lets the filter inform administrators exactly what it is doing. (See the "−Z" option to "mailmunge−multiplexor".)
|
3. |
filter_prog −embserver |
Similar to "−server", but used by the embedded Perl interpreter. The program should run any initialization routines and then exit. The multiplexor will subsequently call the Perl routine "_mailmunge_do_main_loop" when it is time for the worker to begin running.
|
4. |
filter_prog −embserveru |
Similar to "−embserver" with the additional magic of updating the worker status from data written to file descriptor 3.
If you use the standard "Mailmunge" Perl modules, the "Mailmunge::Filter" class takes care of interpreting command−line arguments and running the filter in the appropriate mode.
"mailmunge−multiplexor" runs the filter program, which is expected to run continuously in a server loop. The filter program reads commands from standard input, and writes results to standard output. The filter program must exit shortly after it sees EOF on its standard input. If it does not exit within 10 seconds, it will be terminated with SIGTERM. If that still does not work, then after a further 10 seconds, it is killed with SIGKILL.
Many parameters in commands and replies are percent−encoded. To percent−encode a sequence of bytes, replace any byte whose value is less than or equal to 32, greater than 126, or one of "%", "\", """, or "'" with the three−byte sequence %XY where "XY" is the hexadecimal value of the byte.
Here are some examples of percent−encoded strings:
Hello ==>
Hello
Hello, there! ==> Hello,%20there!
"How\ are%you?" ==>
%22How%5C%20are%25you?%22
All server
commands are single line commands. Each command is followed
by a space−separated list of arguments; each argument
is percent−encoded. The commands defined are:
ping
Elicits a reply of "PONG" from the server.
scan queue_id dir
Run a scan for the mail identified by the Sendmail queue−ID queue_id in the directory dir. The command is terminated with a newline. The server must write a newline−terminated "ok" if the scan completed successfully, or "error: msg" if something went wrong.
Additional information is passed in a working directory as described in "THE SCAN COMMAND".
relayok ip_addr hostname client_port daemon_ip daemon_port queue_id
Test whether or not to accept a
connection from the specified host. The server must write
the following to standard output:
"ok 1"
Accept the connection.
"ok 0 error_message code dsn"
Reject the connection with the specified code, dsn and text error_message.
"ok −1 error_message code dsn"
Tempfail the connection with the specified code, dsn and text error_message.
In all cases, error_message, code and dsn must be percent−encoded.
Note that even if the connection is accepted, a later scan can still reject the message based on other criteria. "ip_addr" is the IP address of the relay and "hostname" is the hostname (if it could be determined; otherwise, the IP address in square brackets).
helook ip_addr hostname
helo_string client_port daemon_ip daemon_port
queue_id
Test whether or not to accept
the HELO/EHLO command. The server must write the following
to standard output:
"ok 1"
Accept the HELO/EHLO command.
"ok 0 error_message code dsn"
Reject the command with the specified code, dsn and text error_message.
"ok −1 error_message code dsn"
Tempfail the command with the specified code, dsn and text error_message.
In all cases, error_message, code and dsn must be percent−encoded.
senderok sender_addr ip_addr
hostname helo_string dir queue_id
[esmtp_args...]
Test whether or not to accept
mail from the specified sender. The server must write the
following to standard output:
"ok 1"
Accept the sender.
"ok 0 error_message code dsn"
Reject the sender with the specified code, dsn and text error_message.
"ok −1 error_message code dsn"
Tempfail the sender with the specified code, dsn and text error_message.
In all cases, error_message, code and dsn must be percent−encoded.
Note that even if the sender is accepted, a later "scan" can still reject the message based on other criteria. "sender_addr" is the sender's e−mail address. The "ip_addr" and "hostname" arguments are as in "relayok". "helo_string" is the argument to the SMTP HELO/EHLO command. "dir" is the Mailmunge spool directory, and "queue_id" is the Sendmail or Postfix queue identifier.
The optional "esmtp_args" are space−separated, percent−encoded ESMTP arguments supplied with the MAIL FROM: command.
recipok recip_addr
sender_addr ip_addr hostname first_recip helo_string
dir queue_id [esmtp_args...]
Test whether or not to accept
mail for the specified recipient. The server must write the
following to standard output:
"ok 1"
Accept the recipient.
"ok 0 error_message code dsn"
Reject the recipient with the specified code, dsn and text error_message.
"ok −1 error_message code dsn"
Tempfail the recipient with the specified code, dsn and text error_message.
In all cases, error_message, code and dsn must be percent−encoded.
"recip_addr" is the argument to the RCPT TO: command, and "first_recip" is the argument to the first RCPT TO: command for this message. Other arguments are as in "senderok".
map map_name key
If you are using a map socket (the "−N" option to "mailmunge−multiplexor"), then the server should look up the key key in the map map_name. The server should print a single line to standard output. The first word on the line should be one of OK, NOTFOUND, TEMP, TIMEOUT or PERM, indicating a successful lookup, absence of the key, a temporary failure, a timeout or a permanent failure, respectively. This should be followed by a space and a percent−encoded string representing the value of the key (if it was found) or an optional error message (if something went wrong.)
tick band
The filter should execute its "tick" method with the specified band argument. It should print a single line to standard output:
"tock band"
Additional Commands
If you use the standard Mailmunge perl modules, your filter can define a function "filter_unknown_cmd" that can extend the list of server commands. If you do this, make sure all of your commands start with an upper−case letter to avoid conflicts if more built−in commands are defined in the future.
Your filter should not write anything to standard output except reply codes as documented above, or the multiplexor will become confused. You should not terminate the program; simply echo an "error:" reply and return to the server loop.
When you send a reply code back to the multiplexor, be sure to terminate it with a newline, and to flush standard output. If your program uses the Standard I/O library, standard output may not be flushed immediately, and "mailmunge−multiplexor" will wait forever for the filter's reply, and eventually kill the filter on the assumption it has hung up.
If the filter program receives a SIGINT signal, it must terminate. This is used by "mailmunge−multiplexor" to terminate workers after they have processed a given number of e−mail messages.
The "scan" command is different from most commands in that most of the required information is placed in a temporary working directory. Commands are placed in a file in that directory, and results should be written to a different file in that directory, as described below.
When the filter
begins a scan, it should change directories to the working
directory. In that directory, it will find the following
files.
INPUTMSG
A file containing the complete input e−mail message, including headers.
HEADERS
A file containing just the headers, one per line. Headers which are continued over several lines in the original message are collapsed into a single line in this file.
PRISTINE_HEADERS
A file containing just the headers, exactly as they were received from the SMTP client. In contrast the HEADERS, we do not unwrap the headers in this file, so a single header may extend over several lines.
COMMANDS
A file containing a list of commands. Each command is a single letter and may be followed by arguments. Each command is on its own line.
THE COMMANDS FILE
All commands in the COMMANDS file have their arguments percent−encoded.
The commands
from the C to Perl filters are:
Ssender
The sender of the message.
sesmtp_arg
An ESMTP argument associated with the sender (such as SIZE=54432). There is one "s" line for each ESMTP argument.
Usubject
The message subject.
Xmessage_id
The Message−ID.
Rrecipient mailer host addr
A recipient. There is one "R" line for each recipient. The mailer, host and addr parts of the line are the values of the Sendmail {rcpt_mailer}, {rcpt_host} and {rcpt_addr} macros if they are available, or "?" if not.
resmtp_arg
An ESMTP argument associated with the most recent recipient (such as NOTIFY=never). There is one "r" line for each SMTP argument.
|
! |
If this command is present, there are suspicious characters in the message headers. |
|||
|
? |
If this command is present, there are suspicious characters in the message body. |
Ihost_addr
The SMTP relay host's IP address in dotted−quad notation.
iidentifier
An identifier generated by Mailmunge. On a given host, this identifier is very likely to be unique over a timespan of about 24 years.
Hhost_name
The SMTP relay host name.
Eargument
The argument to the SMTP "EHLO" or "HELO" command.
Qqid
The message's Sendmail or Postfix queue−ID.
=macro val
Set the value of the specified Sendmail macro to val. Both macro and val are percent−encoded, but the single space character between them is not.
When the filter performs a scan, it can make use of all the information in the files mentioned previously. If the filter needs temporary working files, it should create a subdirectory under the working directory for its own use. In this case, you do not have to clean up your working files, because "mailmunge" deletes the working directory when the filter returns.
The filter
communicates the results of the scan back to
"mailmunge" by creating additional files in the
working directory. The most important file is called
RESULTS, and it contains a list of one−letter,
one−line commands back to the filter. As usual,
command arguments are percent−escaped. The commands
from the filter back to "mailmunge" are:
Bcode dsn reply_text
Bounce (reject) the message with the specified SMTP reply code, DSN code and reply text.
|
D |
Silently discard the message and pretend it was delivered. |
Tcode dsn reply_text
Return an SMTP temporary failure code with the specified SMTP code, DSN and reply text.
|
C |
Replace the message body. If this command is present, the file NEWBODY must contain the new message body. |
Mheader_val
Replace the MIME Content−Type header with a new value. Used to change MIME boundaries or convert non−MIME to MIME messages.
Hheader val
Add a new header header with value val. The header should not contain a colon. Each of header and val is percent−escaped, but the single space between them is not.
Nheader index val
Adds a new header header with value val in position index. An index of zero specifies that the new header should be prepended before all existing headers.
Iheader index val
Replace the index'th occurrence of header with value val. The index is 1−based. The header should not contain a colon. Each of header, index and val is percent−escaped, but the single space between them is not.
Jheader index
Delete the index'th occurrence of header.
Rrecip
Add a new recipient recip to the message.
Srecip
Delete recip from the list of message recipients.
fsender
Change the envelope sender to sender. This is only supported by Sendmail 8.14.0 and higher.
|
F |
Indicate that we have finished issuing commands. Anything after an F line is ignored. |
In principle, you can replace the usual Mailmunge filter /etc/mailmunge/mailmunge.pl with any program you wish (using mailmunge−multiplexor's −f option) just as long as it obeys the protocol and command−line invocation options documented here. This would require significant effort and it's probably better to stick with the Perl code in most cases.
Obviously, if you want to write a non−Perl filter, you cannot use the embedded Perl interpreter (the −E mailmunge−multiplexor option.)
mailmunge, mailmunge−multiplexor, Mailmunge
"mailmunge" was written by Dianne Skoll <[email protected]>. The "mailmunge" home page is <http://www.mailmunge.org/>.
This code is licensed under the terms of the GNU General Public License, version 2.