[][src]Crate milter

A library for writing milters: mail filtering applications that can be integrated with MTAs (mail servers) over the sendmail milter protocol.

This crate contains the Rust bindings for libmilter, that is, the sendmail mail filter API. As such, it does not try to hide the characteristics of that venerable C library, but exposes its capabilities faithfully with all its quirks. The goal is to make milter implementation easier and (as they say) safer. The functionality exposed through the Context and ActionContext structs as well as flags such as Actions should be immediately familiar – though the names have been adapted – if you have used libmilter before.

Once it has started up, a milter application is driven by the underlying libmilter C implementation. This documentation will speak of the ‘milter library’ in those cases.

Usage

As an example, we can create a milter that counts the envelope recipients of an email, and adds a header recording the count.

This demonstrates all important milter functionality: handling of events with callbacks (the envelope recipients) and returning response statuses (here always ‘continue’), storing data in the callback context (the recipient count), and finally performing some message modification (adding a header).

#[macro_use] extern crate milter;

use milter::*;

#[on_rcpt(rcpt_callback)]
fn handle_rcpt(ctx: Context<usize>, _: Vec<&str>) -> milter::Result<Status> {
    match ctx.data.borrow_mut()? {
        Some(mut count) => *count += 1,
        None => {
            ctx.data.replace(1)?;
        }
    }

    Ok(Status::Continue)
}

#[on_eom(eom_callback)]
fn handle_eom(ctx: ActionContext<usize>) -> milter::Result<Status> {
    if let Some(count) = ctx.data.take()? {
        ctx.add_header("X-Rcpt-Count", &count.to_string())?;
    }

    Ok(Status::Continue)
}

#[on_abort(abort_callback)]
fn handle_abort(ctx: Context<usize>) -> Status {
    let _ = ctx.data.take();

    Status::Continue
}

fn main() {
    let _ = Milter::new("inet:3000@localhost")
        .name("RcptMilter")
        .on_rcpt(rcpt_callback)
        .on_eom(eom_callback)
        .on_abort(abort_callback)
        .actions(Actions::ADD_HEADER)
        .run();
}

Intended for creation of main.rs binaries. Signals are handled by the milter library, shut down with Ctrl-C.

The remainder of this module documentation highlights some aspects to be aware of when creating a milter application.

Callback flow

One must be aware of the basic flow of callback calls. The flow is as follows. When negotiation is used, this is the very first step, preceding connect.

Several messages can be processed in a single connection. In that case, the message-oriented stages (mail to eom) will be traversed repeatedly. Message-oriented processing is always bracketed by connection-oriented stages connect and close.

At any point during processing of a message the flow can be derouted to abort, which skips the remaining of the message steps and processing continues at the start of the message loop.

In any case, close will be called at end of processing, so this is the natural place to do cleanup of resources.

Notice that resources can be connection-scoped and message-scoped.

At each stage, a Status is returned that decides whether to continue, reject, etc. Only at the eom stage, message modification operations can be applied, such as adding headers or altering the message body.

Callback resource management

The callback context allows storing connection-specific and message-specific data. This is an area that has one manual memory management requirement. Any resources stored in the callback context DataHandle must be reacquired and dropped before the connection closes.

That is, all code paths through a complete callback flow must include a final call to DataHandle::take!

Failure to acquire the data causes that memory to leak.

Safety

In the callback code panicking must be avoided. As callback code is executed through a foreign code layer, restrictions re FFI apply. Stack unwinding through an FFI boundary is undefined behaviour.

That is why Result is used pervasively.

See also the Result as return type capability.

Globals

A milter implementation is fundamentally a singleton within the process. Only one invocation of Milter::run is allowed per process. Therefore, globals are an acceptable and reasonable thing to have.

Testing

For integration-level testing of a milter the tool miltertest can be used. This is provided by the opendkim-tools package.

Structs

ActionContext

Context of the end of message, ie actions stage.

Actions

Flags representing milter actions.

Context

Milter context supplied to milter callback functions.

DataHandle

A handle on user data managed in the callback context.

Error

Errors returned from milter methods, with source attached if available.

Milter

A configurable milter runner.

ProtocolOpts

Flags representing milter protocol options.

Enums

ErrorKind

The kind of milter error.

Stage

Milter protocol stage.

Status

Callback response status.

Functions

set_debug_level

Sets the trace debug level of the milter library to the given value.

version

Returns the runtime version of the milter library.

Type Definitions

AbortCallback

The type of the on_abort callback function pointer.

BodyCallback

The type of the on_body callback function pointer.

CloseCallback

The type of the on_close callback function pointer.

ConnectCallback

The type of the on_connect callback function pointer.

DataCallback

The type of the on_data callback function pointer.

EohCallback

The type of the on_eoh callback function pointer.

EomCallback

The type of the on_eom callback function pointer.

HeaderCallback

The type of the on_header callback function pointer.

HeloCallback

The type of the on_helo callback function pointer.

MailCallback

The type of the on_mail callback function pointer.

NegotiateCallback

The type of the on_negotiate callback function pointer.

RcptCallback

The type of the on_rcpt callback function pointer.

Result

A result type specialised for milter errors.

UnknownCallback

The type of the on_unknown callback function pointer.