1Abstract
2--------
3
4This is a python extension module to enable python scripts to attach to
5Sendmail's libmilter API, enabling filtering of messages as they arrive.
6Since it's a script, you can do anything you want to the message - screen
7out viruses, collect statistics, add or modify headers, etc. You can, at
8any point, tell Sendmail to reject, discard, or accept the message.
9
10
11Requirements
12------------
13
14Python milter extension: http://https://pypi.python.org/pypi/pymilter/
15Python: http://www.python.org
16Sendmail: http://www.sendmail.org
17
18NB: From Sendmail's libmilter/README:
19
20libmilter requires pthread support in the operating system. Moreover, it
21requires that the library functions it uses are thread safe; which is true
22for the operating systems libmilter has been developed and tested on. On
23some operating systems this requires special compile time options (e.g.,
24not just -pthread). libmilter is currently known to work on (modulo problems
25in the pthread support of some specific versions):
26
27FreeBSD 3.x, 4.x
28SunOS 5.x (x >= 5)
29AIX 4.3.x
30HP UX 11.x
31Linux (recent versions/distributions)
32
33libmilter is currently not supported on:
34
35IRIX 6.x
36Ultrix
37
38Quick Installation
39------------------
40
411. Build and install Sendmail, enabling libmilter (see libmilter/README).
422. Build and install Python, enabling threading.
433. Install this module: python setup.py --help
444. Add these two lines to sendmail.cf[*]:
45
46O InputMailFilters=pythonfilter
47Xpythonfilter, S=local:/home/username/pythonsock
48
495. Run the sample.py example milter with: python sample.py
50Note that milters should almost certainly not run as root.
51
52That's it. Incoming mail will cause the milter to print some things, and
53some email will be rejected (see the "header" method). Edit and play.
54See spfmilter.py for a functional SPF milter, or see bms.py for an complex
55milter used in production.
56
57[*] This is for a quick test. Your sendmail.cf in most distros will get
58overwritten whenever sendmail.mc is updated. To make a milter permanent,
59add something like:
60
61INPUT_MAIL_FILTER(`pythonfilter', `S=local:/home/username/pythonsock, F=T, T=C:5m;S:20s;R:5m;E:5m')
62
63to sendmail.mc instead.
64
65Not-so-quick Installation
66-------------------------
67
68First install Sendmail. Make sure you read libmilter/README in the Sendmail
69source directory, and make sure you enable libmilter before you build. The
708.11 series had libmilter marked as FFR (For Future Release); 8.12
71officially supports libmilter, but it's still not built by default.
72
73Install Python, and enable threading in Modules/Setup.
74
75Install this miltermodule package; DistUtils Automatic Installation:
76
77$ python setup.py --help
78
79For versions of python prior to 2.0, you will need to download distutils
80separately or build manually. You will need to download unittest
81separately to run the test programs. The bdist_rpm distutils option seems
82not to work for python 2.0; upgrade to at least 2.1.1.
83
84Now that everything is installed, we need to tell sendmail that we're going
85to filter incoming email. Add lines similar to the following to
86sendmail.cf:
87
88O InputMailFilters=pythonfilter
89Xpythonfilter, S=local:/home/username/pythonsock
90
91The "O" line tells sendmail which filters to use in what order; here we're
92telling sendmail to use the filter named "pythonfilter".
93
94The next line, the "X" line (for "eXternal"), lists that filter along with
95some options associated with it. In this case, we have the "S" option, which
96names the socket that sendmail will use to communicate with this particular
97milter. This milter's socket is a unix-domain socket in the filesystem.
98See libmilter/README for the definitive list of options.
99
100NB: The name is specified in two places: here, in sendmail's cf file, and
101in the milter itself. Make sure the two match.
102
103NB: The above lines can be added in your .mc file with this line:
104
105INPUT_MAIL_FILTER(`pythonfilter', `S=local:/home/username/pythonsock')
106
107For versions of sendmail prior to 8.12, you will need to enable
108_FFR_MILTER for the cf macros. For example,
109
110m4 -D_FFR_MILTER ../m4/cf.m4 myconfig.mc > myconfig.cf
111
112IPv6 Notes
113----------
114
115The IPv6 protocol is supported if your operation system supports it
116and if sendmail was compiled with IPv6 support. To determine if your
117sendmail supports IPv6, run "sendmail -d0" and check for the NETINET6
118compilation option. To compile sendmail with IPv6 support, add this
119declaration to your site.config.m4 before building it:
120
121APPENDDEF(`confENVDEF', `-DNETINET6=1')
122
123IPv6 support can show up in two places; the communications socket
124between the milter and sendmail processes and in the host address
125argument to the connect() callback method.
126
127For sendmail to be able to accept IPv6 SMTP sessions, you must
128configure the daemon to listen on an IPv6 port. Furthermore if you
129want to allow both IPv4 and IPv6 connections, some operating systems
130will require that each listens to different port numbers. For an
131IPv6-only setup, your sendmail configuration should contain a line
132similar to (first line is for sendmail.mc, second is sendmail.cf):
133
134DAEMON_OPTIONS(`Name=MTA-v6, Family=inet6, Modify=C, Port=25')
135O DaemonPortOptions=Name=MTA-v6, Family=inet6, Modify=C, Port=25
136
137To allow sendmail and the milter process to communicate with each
138other over IPv6, you may use the "inet6" socket name prefix, as in:
139
140Xpythonfilter, S=inet6:1234@fec0:0:0:7::5c
141
142The connect() callback method in the milter class will pass the
143IPv6-specific information in the 'hostaddr' argument as a tuple. Note
144that the type of this value is dependent upon the protocol family, and
145is not compatible with IPv4 connections. Therefore you should always
146check the family argument before attempting to use the hostaddr
147argument. A quick example showing this follows:
148
149 import socket
150 ...
151 class ipv6awareMilter(Milter.Milter):
152 ...
153 def connect(self,hostname,family,hostaddr):
154 if family==socket.AF_INET:
155 ipaddress, port = hostaddr
156 elif family==socket.AF_INET6:
157 ip6address, port, flowinfo, scopeid = hostaddr
158 elif family==socket.AF_UNIX:
159 socketpath = hostaddr
160
161The hostname argument is always safe to use without interpreting the
162protocol family. For IPv6 connections for which the hostname can not
163be determined the hostname will appear similar to the string
164"[IPv6:::1]" with the corresponding hostaddr[0] being "::1". Refer to
165RFC 2553 for information on interpreting and using the flowinfo and
166scopeid socket attributes, both of which are integers.
167
168Authors
169-------
170
171Jim Niemira (urmane@urmane.org) wrote the original C module and some quick
172and dirty python to use it. Stuart D. Gathman (stuart@gathman.org) took that
173kludge and added threading and context objects to it, wrote a proper OO
174wrapper (Milter.py) that handles attachments, did lots of testing, packaged
175it with distutils, and generally transformed it from a quick hack to a
176real, usable Python extension.
177