← Documentation Index

NAME

Mailmunge::Filter - base class for Mailmunge filtering

ABSTRACT

This is the base class for all filters. Implement your own filtering policy by subclassing Mailmunge::Filter and overriding various functions to implement your policy.

SYNOPSIS

    package MyFilter;
    use base qw(Mailmunge::Filter);

    sub filter_relay {
        # ... implement your policy
    }

    my $filter = MyFilter->new();
    $filter->run();

    1;

CLASS METHODS

Class methods may be called either as Mailmunge::Filter->method(...) or $filter->method(...), where $filter is an instance of Mailmunge::Filter or some derived class.

Mailmunge::Filter->new

Construct a new Mailmunge::Filter object and parse the command-line arguments to determine whether or not to update status tags and to enter the main loop.

ip_to_hostname ($ip [, $fcrdns])

Converts a human-readable IPv4 or IPv6 to a hostname if reverse-dns exists. If it does not reverse-resolve, returns "[$ip]"

If $fcrdns is true (the default if not supplied), then this function performs Forward-confirmed reverse DNS to make sure that the hostname include $ip in one of its forward-resolving records. If FCrDNS fails, then "[$ip]" is returned.

mailmunge_version

Returns the version of Mailmunge as a string.

log_identifier()

Returns mailmunge-filter and is used as the syslog program identifier. Derived classes can override this if they wish.

log_options()

Returnc pid,ndelay, passed as the option parameter to openlog in Sys::Syslog. Derived classes can override this if they wish.

log_facility()

Returns mail, the default syslog facility. Derived classes can override this to log using a different facility.

canonical_email($email)

Returns $email all lower-case with leading or trailing angle-brackets stripped

domain_of($email)

Return the domain part of $email

mta_is_postfix ()

Returns true if the MTA is Postfix; false if not (or could not be determined)

mta_is_sendmail ()

Returns true if the MTA is Sendmail; false if not (or could not be determined)

INSTANCE METHODS

action_from_response ($ctx, $resp)

Given a Mailmunge::Response object $resp, take the appropriate action. This function operates as follows:

    If $resp->is_tempfail, call $self->action_tempfail($ctx, $resp->message) and return 1

    If $resp->is_reject, call $self->action_bounce($ctx, $resp->message) and return 1

    If $resp->is_discard, call $self->action_discard($ctx) and return 1

    Otherwise, return 0.

log($ctx, $level, $msg)

Log a message to syslog of the specified level. $level must be one of 'emerg', 'alert', 'crit', 'err', 'warning', 'notice', 'info' or 'debug' $ctx is either an Mailmunge::Context object, or a string representing a Queue-ID.

run()

Run the filter. In server mode, starts the server main loop. In embedded mode, registers the filter in a global variable and returns; the multiplexor will start the server main loop at an appropriate time.

action_bounce($ctx, $reply, $code, $dsn)

Ask the MTA to bounce the message. $ctx is the Mailmunge::Context object; $reply is the text of the bounce; code is a 3-digit 5xy reply code, and $dsn is a three-numbered 5.x.y DSN code.

Writes the 'B' line to RESULTS to tell the C code to bounce the message.

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

action_discard($ctx)

Ask the MTA to discard the message. $ctx is the Mailmunge::Context object.

Writes the 'D' line to RESULTS to tell the C code to discard the message.

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

action_tempfail($ctx, $reply, $code, $dsn)

Ask the MTA to tempfail the message. $ctx is the Mailmunge::Context object; $reply is the text of the tempfail response; code is a 3-digit 4xy reply code, and $dsn is a three-numbered 4.x.y DSN code.

Writes the 'T' line to RESULTS to tell the C code to tempfail the message.

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

action_change_header($ctx, $hdr, $value, $idx)

Ask the MTA to change the value of header "$hdr" to "$value". $ctx is the Mailmunge::Context object, and $idx (if supplied) is the 1-based index of the header to change in the case of multiple headers. If "$hdr" was not present, then the MTA is asked to add it.

Do not include a colon in the header name.

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

action_delete_header($ctx, $hdr, $idx)

Ask the MTA to delete the header header "$hdr" $ctx is the Mailmunge::Context object, and $idx (if supplied) is the 1-based index of the header to delete in the case of multiple headers.

Do not include a colon in the header name.

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

action_delete_all_headers($ctx, $hdr)

Ask the MTA to delete all headers "$hdr". Do not include a colon in the header name.

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

change_sender($ctx, $sender)

Asks the MTA to change the envelope sender

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

add_recipient($ctx, $recip)

Asks the MTA to add a recipient to the envelope

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

delete_recipient($ctx, $recip)

Asks the MTA to delete $recip from the list of envelope recipients

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

action_add_header($ctx, $hdr, $val)

Add a header to the message

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

action_insert_header($ctx, $hdr, $val, $pos)

Add a header to the message in the specified position.

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

action_sm_quarantine($ctx, $reson)

Ask the MTA to quarantine the message. $reason is the reason for the quarantine.

Note that this is different from Mailmunge's quarantine function. Instead, it ends up calling the Milter function smfi_quarantine.

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

action_quarantine_entire_message($ctx, $reason)

Quarantines the message in the Mailmunge quarantine directory. $reason is the reason for quarantining. Note that calling this function does not affect disposition of the message. If you do not want the original message delivered, you must call action_bounce or action_discard.

On success, returns the directory in which the message was quarantined. On failure, returns undef.

This method may only be called from filter_message or filter_wrapup (or from functions called while they are active.)

Attempt to hard-link the file $src to $dst. $dst must be the full desired path of the destination. If hard-linking fails, copy the file instead.

Returns 1 on success; 0 on failure.

unknown_command($cmd, @args)

Called when we get an unknown command. By default, returns an error to the multiplexor. Subclasses can override this method to do something useful.

inputmsg()

Returns the relative path to the input message file received from the MTA

headers_file()

Returns the relative path to the HEADERS file. This file contains only the top-level headers of the email message. The headers are unwrapped, so this file is guaranteed to contain exactly one header per line.

product_name()

Returns 'Mailmunge'. Derived classes may override this if they wish.

spooldir()

Returns the full path to the Mailmunge spool directory. Exactly equivalent to Mailmunge::Constants->get('Path:SPOOLDIR').

inputmsg_absolute($ctx)

Returns the absolute path to the input message file received from the MTA. This is the path that should be passed to virus-scanners, for example.

tick($tick_number)

This method is called periodically by the multiplexor if given the "-X interval" option. It should be overridden in a derived class and should return 1. The base class implementation does nothing and returns 0, causing a warning to be logged.

Note that the "tick" functionality of Mailmunge is deprecated.

filter_map($map, $key)

Look up the key $key in the map named $map. Return an array that looks like one of the following:

    ('PERM', 'Permanent failure message')
    ('TEMP', 'Temporary failure message')
    ('TIMEOUT', 'Timeout message')
    ('OK', 'Result-Of-Lookup')
    ('NOTFOUND', '')

The base class implementation simply returns:

    ('PERM', 'Filter does not implement map method')

filter_relay ($ctx)

$ctx is an Mailmunge::Context object. This method is called as part of the xxfi_connect Milter callback, when a remote machine attempts to connect. It should be overridden by a derived class.

The following $ctx fields are available:

    $ctx->hostip       IP address of connecting host
    $ctx->hostname     Hostname of the connecting host
    $ctx->client_port  Client TCP port
    $ctx->my_ip        Server's IP address
    $ctx->my_port      Server's TCP port
    $ctx->qid          Queue ID (Note: May be NOQUEUE if queue ID not available)

The function must return an Mailmunge::Response object instructing the MTA how to handle the callback.

The default base class method returns Mailmunge::Response->CONTINUE().

filter_helo ($ctx)

$ctx is an Mailmunge::Context object. This method is called as part of the xxfi_helo Milter callback, when a remote machine issues a HELO/EHLO command. It should be overridden by a derived class.

The following $ctx fields are available:

    $ctx->hostip       IP address of connecting host
    $ctx->hostname     Hostname of the connecting host
    $ctx->helo         The argument to the EHLO/HELO command
    $ctx->client_port  Client TCP port
    $ctx->my_ip        Server's IP address
    $ctx->my_port      Server's TCP port
    $ctx->qid          Queue ID (Note: May be NOQUEUE if queue ID not available)

The function must return an Mailmunge::Response object instructing the MTA how to handle the callback.

The default base class method returns Mailmunge::Response->CONTINUE().

filter_sender ($ctx)

$ctx is an Mailmunge::Context object. This method is called as part of the xxfi_envfrom Milter callback, when a remote machine issues a MAIL From: command. It should be overridden by a derived class.

The following $ctx fields are available:

    $ctx->sender       Envelope sender address
    $ctx->hostip       IP address of connecting host
    $ctx->hostname     Hostname of the connecting host
    $ctx->helo         The argument to the EHLO/HELO command
    $ctx->qid          Queue ID (Note: May be NOQUEUE if queue ID not available)
    $ctx->esmtp_args   Arrayref of ESMTP arguments to MAIL From:
    $ctx->cwd          The current working directory

The function must return an Mailmunge::Response object instructing the MTA how to handle the callback.

The default base class method returns Mailmunge::Response->CONTINUE().

filter_recipient ($ctx)

$ctx is an Mailmunge::Context object. This method is called as part of the xxfi_envrcpt Milter callback, when a remote machine issues a RCPT To: command. It should be overridden by a derived class.

The following $ctx fields are available:

    $ctx->recipients   An arrayref consisting of a single recipient
    $ctx->sender       Envelope sender address
    $ctx->hostip       IP address of connecting host
    $ctx->hostname     Hostname of the connecting host
    $ctx->first_recip  The recipient from the I<first> RCPT To: command
    $ctx->helo         The argument to the EHLO/HELO command
    $ctx->cwd          The current working directory
    $ctx->qid          Queue ID
    $ctx->rcpt_mailer  The ${rcpt_mailer} macro value for this recipient
    $ctx->rcpt_host    The ${rcpt_host} macro value for this recipient
    $ctx->rcpt_addr    The ${rcpt_addr} macro value for this recipient
    $ctx->esmtp_args   Arrayref of ESMTP arguments to MAIL From:

The function must return an Mailmunge::Response object instructing the MTA how to handle the callback.

The default base class method returns Mailmunge::Response->CONTINUE().

filter_message ($ctx)

$ctx is an Mailmunge::Context object. This method is called when a message is to be scanned. The return value of this method is ignored; it indicates disposition of the message by calling one of the action_ methods. If no disposition is specified, then the message is delivered.

The following $ctx fields are available; see Mailmunge::Context for details.

    $ctx->connecting_ip
    $ctx->connecting_name
    $ctx->esmtp_args
    $ctx->helo
    $ctx->hostip
    $ctx->hostname
    $ctx->message_id
    $ctx->mime_entity
    $ctx->mailmunge_id
    $ctx->qid
    $ctx->recipients
    $ctx->recipient_esmtp_args
    $ctx->sender
    $ctx->subject
    $ctx->subject_count
    $ctx->suspicious_chars_in_body
    $ctx->suspicious_chars_in_headers
    $ctx->was_resent
    $ctx->cwd

The most important field is probably $ctx->mime_entity, which is the MIME::Entity representing the message being filtered. If you replace the entity by calling:

    $ctx->new_mime_entity($new_entity);

then the MTA will replace the body of the message with the body of $new_entity. Setting new_mime_entity also updates mime_entity.

The base class implementation of filter_message does nothing.

filter_wrapup ($ctx)

$ctx is an Mailmunge::Context object. This method is called immediately after filter_message() and the $ctx object has the same available fields as in filter_message().

In filter_wrapup, it is not possible to change the message body (that is, calling $ctx->new_mime_entity($new_entity) will have no effect.)

You can only change headers or change the delivery disposition of the message. Typically, filter_wrapup is used for something like DKIM-signing a message.

The base class implementation does nothing.

initialize()

This method is called once when the filter process starts up. It can be used to establish per-process resources such as database connections.

If you are using an embedded Perl interpreter in mailmunge-multiplexor, then this function is called after a new scanning process has forked.

The base class implementation does nothing.

NOTE: you should do all per-process initialization in initialize() and not in top-level Perl functions outside of any method. The reason is that if you run the multiplexor using embedded Perl, then initialize() is called each time a new scanner is forked. Code outside of methods is called just once, which may lead to inappropriate sharing of resources such as filehandles between scanner processes.

To reiterate: Do all per-process initialization in $filter->initialize()

prefork_initialize ()

This function is called once when the run() method is called. If you are not using an embedded Perl interpreter in mailmunge-multiplexor, then prefork_initialize() is called in exactly the same circumstances as initialize() and there's no point in using it.

If you are using an embedded Perl interpreter, then prefork_initialize() is called once, before any workers are forked, and can be used to initialize resources that can be shared across a fork.

The base class implementation does nothing.

cleanup

This method is called just before the filter process exits. It is the cleanup counterpart to initialize(); the return value of this filter is used as the argument to exit().

The base class implementation does nothing and returns 0

push_tag($qid, $tag)

Updates the worker status in the multiplexor to be "$tag" with the given queue-ID. Only has an effect if the multiplexor was invoked with the "-Z" option

pop_tag($qid)

Restores the previous tag (if any) in effect prior to the corresponding push_tag call.

read_commands_file($ctx, $need_f)

Reads the COMMANDS file and fills in fields in the Mailmunge::Context object $ctx. If $ctx is supplied as undef, then read_commands_file allocates a new one.

If $need_f is true, then the function fails if the COMMANDS file does not end with an 'F' command.

Returns $ctx on success (or a newly-allocated Mailmunge::Context if $ctx was undef); undef on failure.

rfc2822_date([$now])

Returns an RFC2822-formatted date for the Unix time $now (defaults to time() if $now is nto supplied.) An RFC2822-formatted date looks something like:

    Fri, 1 Jan 2021 15:49:21 -0500

header_timezone ()

Returns the appropriate value to use for this host's timezone in a mail header. Returns something of the form "+HHMM" or "-HHMM" depending on your local time zone.

synthesize_received_header ($ctx)

Returns a Received: header similar to the one that would be added by the MTA if it delivered the message currently being processed.

Use this for message scanners such as SpamAssassin that expect to find the most-recent MTA header at the beginning of the message.

get_host_name()

(Attempt to) get the host's fully-qualified name. Returns the best guess at the hostname.

privdata($key [,$val)

Get or set private data. The data's lifetime is the lifetime of the filter process. One use of this, for example, could be to connect to a database and store the handle. In initialize(), you could say:

    my $dbh = DBI->connect($connect_string, $username, $password);
    $self->privdata('dbh', $dbh);

and then in the remaining functions you could retrieve the database handle:

    my $dbh = $self->privdata('dbh');

Using privdata ensures that you won't interfere with any built-in state stored by the filter for internal purposes, and it is cleaner than littering your code with global variables.

SEE ALSO

Mailmunge::Filter::Compat, Mailmunge::Context, Mailmunge::Response, Mailmunge

AUTHOR

Dianne Skoll <dianne@skollsoft.com>

LICENSE

This code is licensed under the terms of the GNU General Public License, version 2.

Copyright © 2021 Skoll Software Consulting