PerlMx Enterprise -- Frequently Asked Questions This document answers some of the more frequently asked questions about PerlMx Enterprise version 1.2.2. The first section answers general questions. The second, third, and fourth sections describe the technical details of the PerlMx software, available as part of PerlMx Enterprise. General PerlMx Enterprise Questions What exactly is PerlMx Enterprise? PerlMx Enterprise is a powerful sendmail filtering solution that brings the power of Perl to mail processing. It provides a complete and cost-effective means of managing enterprise-level mail filtering requirements. Enterprises use PerlMx Enterprise to perform automatic processing of incoming and outgoing mail for mission-critical tasks such as spam control, virus protection, mail auditing and archiving, and many others. What do mail filters do? Mail filters, also known as "milters", are generic hooks that plug into sendmail's SMTP connection processing stages. This allows mail filters to automate a wide range of email content management tasks. Here are some common examples of such tasks: * Check executable content or file names in email for known viruses and take various actions * Check email origin against one or more public "blackhole" lists and take various actions * Look for one or more keywords in the message and take various actions * Gather statistics about email processed * Verify integrity of email messages based on various criteria * Convert various file types in email from one format to another * Log and archive email messages based on selective criteria * Remove, replace, or add recipients based on specific criteria * Add specific banners in the header or body of outgoing messages * Annotate email message headers with various kinds of additional information What options do I have for adding filters to my PerlMx Enterprise package? Customizing filtering solutions for the PerlMx Engine requires a solid understanding of the Perl language. To assist administrators with this task, PerlMx Enterprise offers two options for pre-built filters. Standard Filters minimize the requirement for in-depth Perl knowledge; Custom Filters eliminate that requirement. Standard Filters are ready-to-deploy filters written in Perl, and cover a generic range of tasks that fulfill common email content-management needs. Custom Filters are available for more specialized mail filtering requirements. Pre-built to your specifications, Custom Filters provide optimum performance efficiency. What are the available Standard Filters? The following Standard Filters can be added to the basic PerlMx Enterprise package on an individual basis: PerlMx::VirusCheck Check executable content or file names in email for known viruses and take various actions based on hits. Actions include: rejecting, redirecting, cleaning, logging, quarantining, or annotating the message. Scanning for and cleaning any offending content requires purchasing a third-party virus-scanning tool. PerlMx::SpamCheck Check email origin against one or more public "blackhole" lists and take various actions based on hits. Actions include: rejecting, redirecting, logging, or annotating the offending message. PerlMx::KeywordCheck Look for one or more keywords in the message and take various actions based on hits. Actions include: rejecting, redirecting, logging, or annotating the message. PerlMx::MailStats Gather statistics about email processed. Variables include: source IP address, sender, recipient, header size, body size, source mail client, and mail frequency. PerlMx includes a tool to produce summary data in a format that can be readily imported into popular graphing, presentation, and report-generation tools. PerlMx::MailAudit Verify integrity of email messages based on various criteria, including header or body length, 7-bit cleanliness, spellings of corporate keywords, specific types of file attachments, and script tags. Actions include: correcting, disabling, and annotating the offending content; rejecting, redirecting, logging, or quarantining the message itself. PerlMx::MailConvert Convert various file types in email from one format to another. Conversions include: Microsoft Word to HTML, HTML to plain text, quoted-printable to plain text, conversions between image formats, and conversions between compression formats. Some conversion of proprietary formats may require purchasing third-party tools to perform the conversion. PerlMx::MailArchive Log and archive email messages based on selective criteria such as sender, recipient, keywords in mail, and message size. Includes facilities to log messages to a database. PerlMx::MailRedirect Remove, replace, or add recipients based on specific criteria. Includes facilities to interface to LDAP or other conventional databases. PerlMx::AddBanner Add specific banners in the header or body of outgoing messages. More than one type of banner may be selected based on the sender, recipients of the message, or the type of content in the message. PerlMx::InfoHeaders Annotate email message headers with various kinds of additional information. Annotations include: cryptographically strong checksum to the header that can be verified on the receiving end; message statistics such as body size, sender IP address, etc. Can't I just use another package for my mail filtering? You can buy software packages that work with particular servers and perform such basic tasks as verifying that the mail does not contain viral attachments, but these solutions are typically not flexible. Such products often require that you change your organizational email policy based on what the tool can do for you, rather than allow you to customize the tool to suit your mail handling requirements. PerlMx Enterprise is a customizable mail processing solution that uses the "Milter" (mail filter) API functionality in sendmail to hook into the content processing stages of the SMTP transaction. This technique gives you complete control over the mail content. I could write mail filters myself. How are the PerlMx Enterprise mail filters different? Typical mail filters are written in C. PerlMx Enterprise mail filters can be written in Perl instead of C. Perl is one of the most expressive scripting languages optimized for processing text, and Perl allows us to rapidly design, develop and deploy your mail processing solution. Why is Perl better than C for writing sendmail filters? Most of the standard arguments for choosing Perl over C for normal programming apply. * Perl allows you to focus on the task at hand by simplifying or eliminating common chores like memory allocation. This allows you to get the job done, and usually with fewer errors than if you had chosen C. * Perl is a language that has been heavily optimized for text processing. This makes it a natural choice for manipulating email. * Perl provides features like taint mode for explicitly tracking insecure data in your code, allowing programmers to avoid costly errors. * Perl is also your gateway to the immense wealth of freely reusable modules and extensions available through the Comprehensive Perl Archive Network (CPAN). For example, the modules for processing mail or connecting to databases are immediately useful. The bottom line is that Perl allows you to rapidly design, develop and deploy your content management solution. This is usually not the case with a language like C or C++. What are the performance implications of using Perl over C for implementing filters? PerlMx Enterprise has been designed with both scalability and performance in mind. PerlMx Enterprise lets you take advantage of parallelism available in your hardware and operating system by running fully multithreaded. Independent requests are processed on independent threads with very little contention over shared resources. PerlMx Enterprise also precompiles the filter when it is first loaded, so that the execution path while processing messages is highly optimized. The PerlMx Enterprise architecture allows the mail filter to maintain persistent database connections, which improves performance. Standard Perl features such as regular expressions have had years of development to incorporate fine-grained optimizations. All of these text processing features and optimizations are immediately available to you via PerlMx Enterprise. While a C implementation could always be made more optimal, note that it would take much more work to achieve the parallelism and standard Perl text processing optimizations that are available with PerlMx Enterprise. How does the PerlMx Enterprise mail filter engine work? The PerlMx Enterprise mail filter engine is a Perl implementation of sendmail's "Milter" content management API. sendmail supported this API beginning with v8.10.0. The Milter API allows you to have complete control over how sendmail deals with every SMTP transaction, as it is received and processed by sendmail in real time. This means you can reject messages based on their size, content, based on who they came from, or who they're going to. You can customize the headers or message bodies to add your own custom content. You can track mail volume and collect statistics. You can redirect messages that were intended for one recipient to another, or add recipients. The possibilities are endless. The PerlMx Enterprise mail filter engine allows you to implement this functionality in modular, extensible Perl. PerlMx Enterprise is a server side solution meant for email administrators with some Perl background. We do not intend for end users to use PerlMx Enterprise mail filters to filter their mail, although the email administrator can implement this for their users using PerlMx Enterprise. Isn't PerlMx Enterprise a security hole? A PerlMx Enterprise filter runs as a separate, unprivileged process. sendmail communicates with the filter via a socket or named pipe using the Milter protocol. Because PerlMx Enterprise filters don't need to run with any special privileges, you can avoid a large class of security vulnerabilities. When the filter and sendmail run on the same system, you can use a named pipe for communication between sendmail and the PerlMx Enterprise filter. This configuration provides excellent control over who can "talk" to the filter. When you use a socket, PerlMx Enterprise filters should only accept connections on particular network interfaces, such as localhost, or a network interface that connects to the local network. The default setting only accepts connections from localhost, thus avoiding the other large class of security vulnerabilities, denial-of-service attacks. Perl contains mature support for avoiding security issues, more so than a low-level language like C. For example, you can enable taint mode in PerlMx Enterprise filters to verify that external data is not being used in critical operations. This support makes it safer to write sendmail filters in Perl rather than in any other language. Which platforms does PerlMx Enterprise run on? PerlMx Enterprise currently runs on FreeBSD, HP-UX, Linux and Solaris. We will be adding support for other Unix platforms based on demand. If you are interested in other platforms, send mail to support@ActiveState.com to let us know. Can I use PerlMx Enterprise with a non-sendmail mail system? No. PerlMx Enterprise currently only works with the sendmail content management API. We may support other mail systems in future. How can I evaluate PerlMx Enterprise? You can download the PerlMx Enterprise software engine for free, and use it for evaluation purposes for seven days without a license. Our support team can answer your questions during the evaluation period. When you run the PerlMx Enterprise software engine without a valid license, PerlMx adds an "X-Filtered-By" header to each message it processes. This header identifies the name of the filter and the PerlMx software version used to process the message. To suppress this header for your seven-day evaluation, you can install a PerlMx Enterprise trial license, available from ActiveState. After seven days, contact the ActiveState Sales department at 1-866-866-2802 or by email at EnterpriseSales@ActiveState.com to purchase PerlMx Enterprise. How much does PerlMx Enterprise cost? For a price quotation on PerlMx Enterprise, please contact the ActiveState Sales department at 1-866-866-2802 or by email at EnterpriseSales@ActiveState.com. The PerlMx software engine is free for personal or educational use by individual users. Support for installation or customization is not included. Organizational use (whether commercial or not) or use by individuals for commercial purposes requires purchasing a license after the free evaluation period. Perl/sendmail Installation Issues The rest of this document describes the PerlMx software available as part of PerlMx Enterprise. Prerequisites Before you can use PerlMx Enterprise you need to install the correct versions of sendmail and perl. You could use either ActivePerl build 630 or later, or you can use Perl 5.6.1 built with the -Dusethreads Configure option. If you choose to build Perl 5.6.1 yourself, you will need to apply a patch to the source code from CPAN. Please refer to the section on "How do I build Perl to work with PerlMx?" for additional information. You could use either the Sendmail Switch (TM) product from Sendmail Inc., v2.0.6 or later, or Open Source sendmail v8.11.0 or later. The latter must be built with the Milter feature enabled. The Milter feature comes enabled by default in sendmail v8.12.0 or later. Where can I get ActivePerl? You can download the latest version of ActivePerl at: http://www.ActiveState.com/ASPN/Downloads/ActivePerl/ Solaris users - To install ActivePerl on Solaris, you usually need GNU tar, which can handle long path names in tar files correctly. The bundled tar included in most releases of that platform do not handle long path names in tar files correctly. If your platform is not supported by ActivePerl, you can still build perl for your platform from the ActivePerl source package, available here: http://downloads.ActiveState.com/ActivePerl/src/5.6/ Alternatively, you can build perl from the v5.6.1 source package available from CPAN sites worldwide, such as: http://www.cpan.org/src/5.0/ If doing the latter, be sure to apply the latest source code patch to the source code. Please refer to the section on "How do I build Perl to work with PerlMx?" for more information. Where can I get sendmail? Sendmail Inc., provides a commercially supported product called Sendmail Switch that we recommend for use with PerlMx. You need v2.0.6 or later, which you can purchase from: http://www.sendmail.com/ You can also build sendmail yourself with the necessary options. sendmail v8.11.4 or later source code can be obtained at: http://www.sendmail.org/ We recommend using sendmail v8.12.0. How do I build sendmail to work with PerlMx? sendmail communicates with PerlMx by connecting to a port, usually on localhost, but it could be any computer on the network. Ensure your sendmail installation is built to support the mail filtering functionality, "Milter". sendmail v8.12.0 and later include Milter functionality by default. In sendmail versions prior to v8.12.0, the Milter functionality is not enabled by default . If you build such a version, you need to specifically enable the Milter option before building it, as described below. To build sendmail to support the mail filter functionality, follow these steps: 1. Download the sendmail sources and extract them into a directory. For this example, we'll refer to that directory as "sendmail". 2. If you are building a sendmail version prior to v8.12.0, locate the following file: sendmail/devtools/Site/site.config.m4 and add the following lines: dnl Milter APPENDDEF(`conf_sendmail_ENVDEF', `-D_FFR_MILTER=1') APPENDDEF(`conf_libmilter_ENVDEF', `-D_FFR_MILTER=1') If this file does not exist, create it then add the lines above. Note the use of the backtick and apostrophe characters as opening and closing quotes--ensure that the added text exactly matches the example above. 3. Go to sendmail/cf/cf/ and select a configuration file that matches your system configuration. Rename or copy this file to config.mc. We used a Red Hat Linux 6.2 system, so we copied generic-linux.mc to config.mc. 4. If you are building a sendmail version prior to v8.12.0, locate the VERSIONID line in config.mc and add the following line right below it: define(`_FFR_MILTER',`1')dnl This config.mc will be your template for further configuration. 5. Compile and install the sendmail sources, as explained in the sendmail Readme (sendmail/README) and the sendmail Install notes (sendmail/INSTALL). To compile the sendmail sources, you typically run the following command at the top level of the sendmail source distribution: % sh Build -c 6. Copy the sendmail binary to its usual location on your platform, typically /usr/sbin/sendmail and/or /usr/lib/sendmail, then generate the sendmail configuration files as described in sendmail/INSTALL. If you have installed Sendmail Switch v2.0.6 from Sendmail Inc., the Milter functionality is already included. For information on how to install filters, see the section on "How do I configure sendmail to call my filters?". How do I build Perl to work with PerlMx? If ActivePerl supports your operating system, the best way to get PerlMx working quickly is to install ActivePerl. For information on where to get ActivePerl, see the section on "Where can I get ActivePerl?". Solaris users - To install ActivePerl on Solaris, you usually need GNU tar, which can handle long path names in tar files correctly. The bundled tar included in most releases of that platform do not handle long path names in tar files correctly. If you want to build ActivePerl or Perl 5.6.1 from sources using your local C compiler, follow these steps. 1. Prepare the ActivePerl or Perl source files for building. * (Option One) - Download the ActivePerl source package at the bottom of the ActivePerl download page, located at: http://www.activestate.com/ASPN/Downloads/ActivePerl/ The ActivePerl source file usually has a name similar to AP630_source.tgz. You can also download the ActivePerl source file via ftp at: ftp://ftp.ActiveState.com/ActivePerl/src/5.6/ Extract the ActivePerl source file with GNU tar and gzip: gzip -cd AP630_source.tgz | tar xvf - * (Option Two) - Download the Perl 5.6.1 source package from CPAN and manually apply any available ActivePerl "patch" to the CPAN sources. Download Perl v5.6.1 from CPAN and extract the distribution to your local disk: gzip -cd perl-5.6.1.tar.gz | tar xvf - Download the latest ActivePerl source patch for Perl v5.6.1 from: http://www.activestate.com/ASPN/Downloads/ActivePerl/ The patch is usually named something like AP630_diff.txt and is found at the bottom of the download page. Ensure you download the latest file corresponding to the Perl 5.6.1 release. Ensure you download the source patch file with all the whitespace intact--if in doubt, you may also download the source patch file via ftp from the following location: ftp://ftp.ActiveState.com/ActivePerl/src/5.6/ Apply the patch according to the instructions at the top of the patch file. You will need the GNU patch utility to apply the patch. To do so, run the following commands: cd perl-5.6.1 patch -lNp1 < AP630_diff.txt 2. Build the extracted source bundle with the "-Dusethreads -Duseithreads" Configure options. You need an ANSI C compiler to complete this step. GCC works well on most Unix platforms. Run the following commands: % sh Configure -des -Dusethreads -Duseithreads -Dprefix=/usr/local/mxperl % make all test install 3. Add the installed perl to your PATH and add a corresponding line in your ~/.cshrc file. For example, under the C shell run the following commands: % setenv PATH /usr/local/mxperl/bin;$PATH Once you have completed this procedure, you can begin installing PerlMx. Do I need any additional Perl modules? If you want to use the MIME functionality in PerlMx, you need to install the "MIME-tools" and "MailTools" distributions from CPAN. If ActiveState supports a PPM repository for your platform, you should be able to run the following command to install these modules: % ppm install MIME-tools MailTools If ppm is not available on your system, you will need to install these modules manually. Follow the standard CPAN procedure for installing modules after you download them: perl Makefile.PL make all test install The included sample filters may depend on additional modules not mentioned here. Consult the POD documentation in the sample filters to confirm any prerequisites. PerlMx Configuration/Installation Issues How do I install PerlMx? You can follow this procedure to install PerlMx with or without a license. If you purchased a PerlMx license, the PerlMx installer offers to install the license. However, you can still install PerlMx without a license. To install PerlMx: 1. Download the PerlMx distribution from: http://www.activestate.com/ASPN/Downloads/PerlMx/ 2. Extract the files into your local file system, and follow the instructions in the README that accompanies the distribution. Solaris users - To install PerlMx on Solaris, you usually need GNU tar, which can handle long path names in tar files correctly. The bundled tar included in most releases of that platform do not handle long path names in tar files correctly. How do I install a PerlMx license? If you have already installed PerlMx, you can install the license file using "pmx". Use the "-l" switch to do this. For example: % pmx -l PLMX10.S123456789.lic Note that the serial number in your license file name may be different, but the file name should always match the pattern "PLMX10.*.lic". If you haven't installed PerlMx yet, the PerlMx installation process offers to install the license for you. I installed my PerlMx license file, but PerlMx still adds the extra X-Filtered-By header. What's wrong? Your license file probably doesn't match the number of CPUs on your system. You need a single license file that is licensed for as many CPUs as you have on the server. For detailed information about your PerlMx license, you can start the filter with "pmx -v" to display "verbose information" about the license files it reads. Run the following command: % pmx -v PerlMx::Samples::MinFilter If this reports the following line, please contact sales@ActiveState.com to obtain a valid license: pmx: Not enough CPU licenses available, running in unlicensed mode. Where do I put my filters? PerlMx filters are plain Perl modules written to conform to a particular interface. Like any Perl module, they can be installed into the Perl library tree. Your filter should have a .pm extension. If you wrote your filter with a Makefile.PL (see ExtUtils::MakeMaker), you can install your filter with these commands: perl Makefile.PL make make install If you did not write your filter with a Makefile.PL, run "perl -V:installsitelib" to find where to put the module and manually copy the filter module into the specified location. We recommend that you install a filter into the Perl library tree, but this is not mandatory. "pmx" looks for the filter in the current directory if it doesn't find the filter in the Perl module search path. You can edit the search path in the "pmx" command line, with the following command: pmx MyFilter -- -I/look/here/first If you are developing a filter module with a MakeMaker style Makefile.PL and corresponding blib tree, you can edit the search path in the "pmx" command line, with the following command: pmx MyFilter -- -Mblib Note that you do not need to install your filters while you're testing them. How do I start my filters? Once you have added your filter to the Perl library tree, you can start your filter using the "pmx" filter driver. For information on the various options supported by this driver, see the "pmx" documentation. To start your filter, run commands similar to this: pmx -r 60 -p inet:12345@localhost MyFilter >& MyFilter.log & Note that you must specify the filter module name, not the file name. It is important to specify a proper value with the "-r" switch when running "pmx" if your sendmail server normally processes much more that 15 concurrent connections. To ensure good performance, this value should be at least as big as the peak expected number of concurrent sendmail connections. Once you have tested your filter, ensure you start the filter from your system initialization scripts (/etc/rc.d/rc.local or similar). How do I configure sendmail to call my filters? Your sendmail configuration file needs to be updated to specify how sendmail should connect to your filters. To configure sendmail: 1. Locate your config.mc (sometimes also known as sendmail.mc) file corresponding to your system's sendmail.cf. You will need to ensure that it has been configured as discussed in the section on "How do I build sendmail to work with PerlMx?". In particular, note that sendmail versions prior to v8.12.0 will need a line like the following near the beginning: define(`_FFR_MILTER',`1')dnl 2. For each mail filter you intend to use, add a line like the following near the end of the config.mc file: INPUT_MAIL_FILTER(`myfilter', `S=inet:12345@localhost, F=T') 3. Regenerate your sendmail.cf file from the config.mc file. To regenerate your sendmail.cf file, run the following commands (replacing the paths to the files appropriately): cd /etc/mail m4 sendmail_source_dir/cf/m4/cf.m4 /path/to/config.mc > sendmail.cf sendmail calls your filters in the same order specified with INPUT_MAIL_FILTER. The filter name ("myfilter" here) can usually be any placeholder value, usually the same name as you gave to the filter module itself. The second argument to INPUT_MAIL_FILTER specifies the filter configuration parameters. These parameters, explained in the Milter documentation that accompanies sendmail, are reproduced here: +----------------------------------------+ | SPECIFYING FILTERS IN SENDMAIL CONFIGS | +----------------------------------------+ Filters are specified with a key letter ``X'' (for ``eXternal''). For example: Xfilter1, S=local:/var/run/f1.sock, F=R Xfilter2, S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m Xfilter3, S=inet:3333@localhost specifies three filters. Filters can be specified in your .mc file using the following: INPUT_MAIL_FILTER(`filter1', `S=local:/var/run/f1.sock, F=R') INPUT_MAIL_FILTER(`filter2', `S=inet6:999@localhost, F=T, T=C:10m;S:1s;R:1s;E:5m') INPUT_MAIL_FILTER(`filter3', `S=inet:3333@localhost') The first attaches to a Unix-domain socket in the /var/run directory; the second uses an IPv6 socket on port 999 of localhost, and the third uses an IPv4 socket on port 3333 of localhost. The current flags (F=) are: R Reject connection if filter unavailable T Temporary fail connection if filter unavailable If neither F=R nor F=T is specified, the message is passed through sendmail as if the filter were not present. Finally, you can override the default timeouts used by sendmail when talking to the filters using the T= equate. There are three fields inside of the T= equate: Letter Meaning C Timeout for connecting to a filter (if 0, use system timeout) S Timeout for sending information from the MTA to a filter R Timeout for reading reply from the filter E Overall timeout between sending end-of-message to filter and waiting for the final acknowledgment Note the separator between each is a ';' as a ',' already separates equates and therefore can't separate timeouts. The default values (if not set in the config) are: T=C:0m;S:10s;R:10s;E:5m where 's' is seconds and 'm' is minutes. After you have configured your filters, you need to ensure that the filters keep running as long as sendmail is processing messages. If you use a domain socket, ensure that any stale sockets are deleted before you start the filter. If you're running filters on the same server as sendmail, we recommend you start the filter in the same startup file as sendmail. If sendmail and the filters run on different servers, you can set up a cron job or similar to ensure that the filters are running correctly. What if sendmail still does not call my filter? sendmail v8.11.0 moved the default location for mail configuration files to /etc/mail/ rather than /etc. Try copying your configuration files to /etc/mail and restarting sendmail. What timeout values do you recommend to use for filters? A good set of timeout values depends on the kind of email processing your filters perform. There are no rules. For filters that only need to look at the header of the messages, the default timeouts (typically "T=S:10s;R:10s;E:5m") are usually sufficient. For information on what the timeout notation means, or on how to change the default values, see the section on "How do I configure sendmail to call my filters?". For filters that need to look at the entire body content of messages, or perform other relatively slow processing such as connecting to a database before deciding to accept the mail, you may need to increase the timeouts. We recommend you increase the timeouts if you have SMTP peers (i.e., either mail clients and servers) that connect on slow lines, or take a long time (several minutes) to complete the transaction. The following values are pragmatic default timeouts for "average" filters that need to see the body content: T=S:3m;R:3m;E:8m You can progressively increase the timeouts if you're still seeing timeout messages in the sendmail logs. Larger timeouts generally means that sendmail processes may be tied up waiting longer on very large messages. Larger timeouts typically do not have any other performance implications. I still get timeout messages in the sendmail logs. What can I do? If increasing the timeout values as suggested in the section on "What timeout values do you recommend to use for filters?" doesn't seem to help, you will need to make sure you are running "pmx" with the adequate level of concurrency. This is controlled using the "-r" switch passed to "pmx". "pmx" defaults to using 15 interpreters if the "-r" switch is not specified. However, this number of interpreters may be inadequate if your mail volume and sendmail settings result in a larger number of concurrent sendmail processes. If the "MaxDaemonChildren" option in your sendmail.cf file exceeds 15, or is not set (meaning that it is unlimited), you may want to run "pmx" with an "-r" switch argument that matches the estimated peak number of concurrent sendmail processes required to handle your mail load. How much memory or CPU-horsepower do I need on my PerlMx server? We recommend at least 512MB of RAM, and CPUs that are at least as fast as a 500MHz Intel Pentium. If sendmail and the PerlMx filters are running on separate servers, we recommend a 100Mbit Ethernet or better between them. For handling large volumes of mail, you can get very good scalability by increasing the number of CPUs on the PerlMx server and by running different filters on separate PerlMx servers. If you are running "pmx" with the "-r N" switch, make sure you have at least "3 * N" MB of memory on the system. In other words, each interpreter needs at least 3 megabytes of memory, and possibly more if the filter keeps large amounts of data in memory, or loads a large number of modules. Mail Filtering Issues How do I reject messages? Rejecting a message is very simple. Each callback returns a status which can be chosen from SMFIS_ACCEPT, SMFIS_REJECT, SMFIS_CONTINUE, or SMFIS_TEMPFAIL. If you want to reject a message, return SMFIS_REJECT. Example: sub cb_body { my $ctx = shift; my $body = shift; if ($body =~ /badword/) { return SMFIS_REJECT } return SMFIS_CONTINUE; } How to I redirect messages? To redirect a message from "bob" to "susan", simply delete "bob" from the recipients list (with the delrcpt() callback) and add "susan" (using the addrcpt() callback). These calls should be made from the EOM callback. Note that when you delete a recipient, you must specify the recipient exactly as it was in the headers reported by sendmail or it will not be deleted. Example: sub cb_eom { my $ctx = shift; foreach (@{$ctx->{$badrecips}}) { $ctx->delrcpt($_); } foreach (@{$ctx->{$goodrecips}}) { $ctx->addrcpt($_); } return SMFIS_ACCEPT; } How do I bounce messages? Bouncing a message generally means accepting the message for delivery, but sending the message either back to the sender or to an administrator. This could occur because offensive content was found, or any other arbitrary reason. To bounce a message, maintain a list of the headers and recipients as they appear, then at the end-of-message callback, add a copy of the headers into the body (as is normally done for bounces), delete the intended recipients, and add the "bouncee" as a recipient. Example: sub cb_from { my $ctx = shift; $ctx->{from} = shift; return SMFIS_CONTINUE; } sub cb_rcpt { my $ctx = shift; my $rcpt = shift; push (@{$ctx->{rcpts}}, $rcpt); return SMFIS_CONTINUE; } sub cb_header { my $ctx = shift; my $headerf = shift; my $headerv = shift; push (@{$ctx->{headers}}, "$headerf: $headerv\r\n"); return SMFIS_CONTINUE; } sub cb_body { my $ctx = shift; my $body = shift; $ctx->{bodytext} = $ctx->{bodytext} . $body; return SMFIS_CONTINUE; } sub cb_eom { my $ctx = shift; foreach (@{$ctx->{headers}}) { $ctx->{bodytext} = $_ . $ctx->{$bodytext} } $ctx->{bodytext} = "\r\r" . $ctx->{$bodytext} $ctx->replacebody($ctx->{bodytext}); foreach (@{$ctx->{rcpts}}) { $ctx->delrcpt($_); } $ctx->addrcpt($ctx->{from}); } Why does PerlMx call only some of my filter callbacks? If PerlMx calls only some of your callbacks, ensure you are not misusing SMFIS_ACCEPT in your callbacks. The SMFIS_ACCEPT return code tells sendmail to immediately accept the message without processing any other callbacks in the filter. How can I write reusable filter code? Reusable filters should specify their filter callbacks as a method name (string) rather than as a reference to a method. The names should then be documented as the ones that could be overridden by other filters that derive from the base filter class. Derived filters can then use the $ctx->SUPER::cb_body(...) syntax to call the base class methods where needed. How can I connect PerlMx to a database? You can use the hugely popular DBI framework and associated database drivers, such as DBD::Oracle. DBI bindings exist for almost all popular databases in use today. For more information on the DBI, see the DBI home page at http://www.symbolstone.org/technology/perl/DBI/ How do I retain persistent DBI connections? Any code that you put at the top level of your filter will be executed only once, when the filter is loaded. This allows you to do one-time initialization for connecting to a database etc. Cleanup can be done in an END block. An example: package MyFilter; use PerlMx; use base 'PerlMx'; use DBI; my $dbh = DBI->connect("dbi:Oracle:mydsn", "user", "passwd"); END { $dbh->disconnect } ...filter callbacks using $dbh here... How do I block SPAM? Unsolicited email (colloquially known as SPAM) can usually be detected based on either the content of the message, the network address from which it was sent, or other tell-tale information in the headers of the message. The PerlMx::Samples::BlackList sample filter shows how you can implement network address-based SPAM-blocking. Blacklist relies on various public databases that maintain lists of addresses that are known SPAM havens. Note that using this approach might also block a small amount of genuine email. You can also block spam using other heuristic approaches based on the content. For example, messages that contain characters in a foreign encoding are usually SPAM. The following BODY callback denies any messages that contain more than 33% content that is not 7-bit clean. sub cb_body { my $ctx = shift; my $body = shift; if ((length($body) / 3) <= ($body =~ tr[\200-\377][\200-\377])) { return SMFIS_DENY; } return SMFIS_CONTINUE; } How do I detect and block email-borne viruses? Virus creators have been using executable attachments in email to propagate viruses. Filtering out such messages usually involves either blocking all executable content in email, or scanning for known viruses in the body content. The PerlMx::Samples::Evil_Filter sample filter illustrates how to block all executable content in email. The PerlMx::Samples::UVScanner sample filter illustrates how to scan the body content for known viruses. How do I pass data between different callbacks? There are two possible approaches to maintaining state that persists for an entire SMTP transaction and is available to all the callbacks that service that transaction. Both techniques work equally well, but the second one may be somewhat simpler because it takes care of automatically destroying the state that was thus cached, at the end of the transaction. * Option One - Use a lexical variable at the file scope. Here is an example: my $thishost; # host for current connection sub pmx_connect { my $ctx = shift; my $host = shift; my $address = shift; $thishost = $host; # keep track of the host for other callbacks ... } sub pmx_header { my $ctx = shift; ...can use $thishost here... } sub pmx_close { ... undef $thishost; # may want to clear the state explicitly } * Option Two - Attach the state to the filter context hash object that is passed as the first argument of every callback. For example: sub pmx_connect { my $ctx = shift; my $host = shift; my $address = shift; $ctx->{host} = $host; # keep track of the host for other callbacks ... } sub pmx_header { my $ctx = shift; ...can use $ctx->{host} here... } If you use this technique, you should be aware that all key names that start with __pmx or __perlmx are reserved for PerlMx internal use.