TclMilter(n) 1.0 "Tcl interface to SendMail's Milter API"
TclMilter - Tcl interface to SendMail's Milter API
TABLE OF CONTENTS
SYNOPSIS
DESCRIPTION
RETURN CODES
MILTER CONTROL FUNCTIONS
DATA ACCESS FUNCTIONS
MESSAGE MODIFICATION FUNCTIONS
OTHER MESSAGE HANDLING FUNCTIONS
EXAMPLE
LICENSE
ACKNOWLEDGEMENTS
SEE ALSO
KEYWORDS
COPYRIGHT
package require Tcl 8.1
package require TclMilter ?1.0?
TclMilter is a package
for Tcl (written in C) that implements an
interface to SendMail's Milter (Mail Filter) API for developing
custom scripted message rewriting and spam filtering processes. A
thread-enabled Tcl build is required due to Milter's threading
requirements. However, you can only run, configure, or register
callbacks for a milter from the first thread/interpreter the package
is loaded into. If you need to be able to stop the milter from
within Tcl, you can load the package in another thread to do so.
Only the "milter stop" command will function in secondary threads.
TclMilter follows libmilter's APIs very closely, with the following
exceptions for readability and flexibility:
-
The smfi_opensocket command is not provided, only milter main
as an interface to smfi_main.
-
The smfi_setconn, smfi_settimeout, smfi_setbacklog and
smfi_setdbg commands, as well as settings normally provided
through smfi_main are instead provided as milter configure
options.
-
The smfi_register command is provided as milter register;
however, unlike libmilter it's possible to register and unregister
callbacks at any time, rather than just once prior to calling main.
-
There isn't a single data storage value for smfi_setpriv; rather,
TclMilter's milter setpriv command uses a hash table so you can
set individual, custom variables with data to associate with a context.
-
The context provided to callbacks is not simply a pointer, but is
an actual Tcl command that can be used to interact with the context.
Callbacks are invoked by TclMilter at various stages of message processing
in order to provide a filter with all of the information about the
MTA connection and message being sent. These callbacks may return one
of five values to indicate to SendMail whether to continue processing
the message or whether to reject it for some reason.
-
SMFIS_CONTINUE - Indicates that message processing should continue,
and that other callbacks should continue to be called (i.e., the message
is neither accepted or rejected yet). Callbacks should always return
this value by default.
-
SMFIS_REJECT - Indicates that the message should be rejected
immediately. The milter may use context setreply prior
to returning this value to indicate the status code and reason for the
rejection.
-
SMFIS_DISCARD - Indicates that the message should be discared
immediately.
-
SMFIS_ACCEPT - Indicates that the message should be accepted
as far as this milter is concerned. No other callbacks will be
invoked. However, the message may still be rejected for other
reasons - either by other milters or by SendMail itself.
-
SMFIS_TEMPFAIL - Indicates that the message should be rejected
immediately with a temporary failure. The milter may use context
setreply prior to returning this value to indicate the status
code and reason for the rejection.
- milter configure ?option ?value?? ...
-
Configure Milter session parameters. Given no options, returns a list
of option/value pairs indicating current milter configuration. Given
an option and no values, returns the current value of the
specified option. Otherwise, each option is set to the given value.
- -name name
-
Specifies the name for the milter. This should be a unique name among
all the milters being used by the MTA, and it must be set before calling
milter main if at all. The default is "TclMilter".
- -connection foo
-
Specifies the type of connection to use for communicating with SendMail,
as well as the appropriate parameters for the connection type. It must be
set before calling milter main if at all. Per libmilter's
smfi_setconn
documentation, the format is "proto:address":
-
{unix|local}:/path/to/file -- A named pipe.
-
inet:port@{hostname|ip-address} -- An IPV4 socket.
-
inet6:port@{hostname|ip-address} -- An IPV6 socket.
The default is "unix:/var/run/tclmilter.sock" - a pipe socket using the
file "/var/run/tclmilter.sock".
- -timeout seconds
-
Specifies the number of seconds that the Milter should wait for an MTA
connection before timing out. The timer can be reset using the context
progress command. This setting may only be changed
prior to calling milter main. Default: 7210 seconds.
- -backlog count
-
Configures the number of incoming connections that the milter will
allow in the listen queue. This setting may only be changed prior to
milter main. The default for the milter is the system default.
- -debuglevel level
-
Configures the level of debug information that will be logged by libmilter.
This setting may be changed at any time. Default: 0.
- milter main
-
Opens the socket to allow connection to SendMail and to begin filtering mail.
This command will not return unless milter stop is called or the
milter receives a signal requiring a shutdown, such as SIGHUP.
- milter register event ?callback?
-
Register a callback for TclMilter to invoke at the specified stage on
mail processing. Callbacks are supported corresponding to each of
those provided by libmilter its self. Unlike libmilter, which must
have callbacks specified by the call to smfi_main(), callbacks for
TclMilter may be registered or unregistered at any time.
The callback argument specifies the name of the procedure
for TclMilter to invoke for the specified event. If this argument
is not provided, the command will return the name of the currently
registered callback procedure for the specified event.
The type and arguments that the callback receives depend on the
event, although every callback is provided with a unique context
ID that can be used to manipulate the message being processed.
Supported event types are as follows:
- milter register connect connectProc
-
Arranges for proc connectProc to be called when a connection is
established to sendmail, before the exchange of HELO greetings. The
callback is appended with three arguments: the context command,
the hostname and a list containing a dictionary of information from
the sockaddr stucture (e.g., address and port).
proc connectProc { context hostname addrInfo } { ... }
- milter register helo heloProc
-
Arranges for proc heloProc to be called when the client sends
its HELO greeting command. The command is appended with two arguments:
the context and the hostname as provided in the HELO command.
proc heloProc { context helohost } { ... }
- milter register envfrom envfromProc
-
Arranges for proc envfromProc to be called when the client sends
the MAIL FROM command. The command is appended with two elements:
the context command and a list of all of the addresses specified
in the MAIL FROM envelope.
proc envfromProc { context fromList } { ... }
- milter register envrcpt envrcptProc
-
Arranges for proc envrcptProc to be called when the client sends
the RCPT TO command. The command is appended with two elements:
teh context command and a list of all of the addresses specified
in the RCPT TO envelope.
proc envrcptProc { context rcptList } { ... }
- milter register header headerProc
-
Arranges for proc headerProc to be called for each header sent
by the client. The command is appended with three elements: the
context command, the header name and the contents of the header.
proc headerProc { context name content } { ... }
- milter register eoh eohProc
-
Arranges for proc eohProc to be called once all headers have
been sent and handled by the milter's "header" callback, before the
message body is sent. The command is appended with one argument:
the context command.
proc eohProc { context } { ... }
- milter register body bodyProc
-
Arranges for proc bodyProc to be called for each block of
data in the message body. The command is appended with two elements:
the context command and the body text. Each message has only one
body, but this body may be sent in multiple blocks. If the milter
intends to do content filtering on the message body, it is recommended
to use context setpriv to build and store the complete
message body and perform content filtering in the eom callback.
proc bodyProc { context block } { ... }
- milter register eom eomProc
-
Arranges for proc eomProc to be called once once all message
information has been received, including the entire message body,
and processed by the milter. All message rewriting, such as adding
and removing headers or changing the body, must be performed
within this callback. The command is appended with one element: the
context command.
proc eomProc { context } { ... }
- milter register abort abortProc
-
Arranges for proc abortProc to be called if message transmission
is aborted, such as due to a dropped connection. This callback is not
invoked if message transmission is completed. The command is appended
with one element: the context command.
proc abortProc { context } { ... }
- milter register close closeProc
-
Arranges for proc closeProc to be called when the client connection
has been closed. This callback is not invoked if message transmission
is aborted. The command is appended with one element: the context command.
proc closeProc { context } { ... }
- milter stop
-
Stop the milter, causing milter main to return. Note that,
due to libmilter limitations, it is currently not possible to restart
the milter once stopped. You will have to exit Tcl and then restart,
reload the package, etc. in order to start the milter up again.
The following context functions may be invoked from any callback to get
or store information about the message context or sendmail configuration.
Each context has a hash table associated with it, and a set of accessor
functions that work similar to Tcl's own set command, allowing
the milter to associate data with each message that can be passed around
between callbacks and automatically garbage collected when the context
is deleted.
- context getpriv ?name?
-
If name is provided, this command returns the value from the hash
table with the given key name. If name is unspecified, a
list of name value pairs is returned with all of the private
data associated with the context, in dictionary format (i.e., suitable
for use with array set).
- context getsymval symbol
-
Returns the value for the specified symbol in the SendMail configuration.
The symbol may be either a single letter or a keyword surrounded
in {braces}. Consult the libmilter and/or SendMail documentation for
more details.
- context setpriv name ?value?
-
Sets the specified value within the context's private hash table using
the given variable name/key name. If value is not specified,
returns the current value for the specified key, as with getpriv.
- context setreply rcode ??xcode? message?
-
Sets the specific reply/status code for the message to be returned to the
client MTA and, optionally, the extended reply code and a message (such
as a reason for rejecting the message).
- context unsetpriv ?pattern?
-
This command could be considered a hybrid of Tcl's unset and
array unset commands. It removes each variable whose name
matches the specified glob-style pattern from the context's private
data hash table. If no pattern is specified then all private
data is deleted from the context.
The following context functions may be invoked ONLY from the eom callback
to modify message contents. The result of invoking these commands in
other callbacks is undefined.
- context addheader name data
-
Adds a new header to the message with the given header name and contents data.
- context addrcpt rcpt
-
Instructs sendmail to deliver the message to the specified recipient
in addition to any others the message is already being sent to, provided
the message is not rejected.
- context chgheader name ?index? data
-
Changes an existing header in the message with the given header name
to the contents data. The index value specifies which
instance of the header is to be modified. If unspecified, the first
instance of the header will be modified. If no such header exists, it
will be added.
- context delrcpt rcpt
-
Instructs sendmail NOT to deliver the message to specified recipient. The
message may still be delivered to other recipients, provided the message
is not rejected.
- context insheader name ?index? data
-
Inserts a new header into the message with the given header name
and contents data. By default, headers are inserted
with index 0 - that is, they are inserted as the first header.
Otherwise, they are inserted before the indexth header. If
index is greater than the number of headers in the message,
the new header is added at the end, as with addheader.
- context replacebody data
-
Replaces the entire message body with data. This can be useful,
for example, to remove questionable attachments or change message encoding.
The following context functions may be invoked ONLY from the eom callback.
The result of invoking these commands in other callbacks is undefined.
- context progress
-
This command may be called during long processes to inform SendMail/libmilter
that the message is still being processed. When called, the timeout
clock is reset.
- context quarantine reason
-
This command may be called to quarantine the message. The message is
neither rejected nor delivered, but is left in the mail queue, requiring
administrator action to be delivered or unqueued.
|
#!/usr/local/bin/tclsh
#
# A simple email address-based blacklist milter
#
package require Tcl 8.4
package require TclMilter 1.0
# Configuration
set blacklist {
friend@public.com
spammer@spamdomain.com
}
milter configure -name Blacklist
milter configure -connection {unix:/var/run/blmilter.sock}
# Check to see if an address is on the blacklist
proc is_blacklisted { address } {
set address [string trim $address <>]
if {[lsearch -exact $::blacklist $address] != -1} {
return 1
}
return 0
}
# Callbacks
proc bl_header { context name data } {
if {($name == "From") && [is_blacklisted $data]} {
return SMFIS_REJECT
}
return SMFIS_CONTINUE
}
proc bl_envfrom { context addressList } {
foreach address $addressList {
if {[is_blacklisted $address]} {
return SMFIS_REJECT
}
}
return SMFIS_CONTINUE
}
# Register the callbacks and start up the filter
milter register header bl_header
milter register envfrom bl_envfrom
milter main
file delete /var/run/blmilter.sock
|
TclMilter is provided under the GNU General Public License
(GPL) (see the file "license.terms" for details).
Portions of this document, as well as the package its self, are based on the
Milter API specification.
Milter API, RFC 1893, RFC 2034, RFC 2821, RFC 821
email, filter, milter, sendmail, spam, tcl
Copyright © 2005 Michael Kirkham & Muonics