[][src]Crate opensmtpd

Build Status Rust-OpenSMTPD on crates.io Rust-OpenSMTPD on docs.rs License: MIT or Apache-2.0

Writing a filter for OpenSMTPD

The first step is to define an object (most of the time you want a struct) that implements the Filter trait. All of this trait's methods have an empty default implementation, so you only have to implement the ones that matters to you. For each method you implement, you must use the opensmtpd_derive::register attribute macro in order to ask OpenSMTPD to send you the corresponding events and filter requests.

The second and last step is to call the run_filter function with a mutable reference of your filter object.


Reports are very simple: the associated functions accepts a ReportEntry parameter as well as some optional parameters and do not return anything. The ReportEntry parameter contains all the common information about reports while the other parameters are specific to each filter.


Filters are similar to reports: the associated functions accepts a FilterEntry parameter as well as some optional parameters, but this time they have to return a FilterResponse. The only exception is the on_filter_data_line function that doesn't return anything.

The data-line filter

This filter is the only one that does not return a FilterResponse. Instead, you are expected to produce new lines using the return_data_line function. This function does not have to be called each time on_filter_data_line is triggered: you can store all of the data-lines, edit them and then call return_data_line on each.

The last data-line you will receive is a single dot. The last one you return must also be a single dot.


The following filter increments a variable every time a client disconnects.

use opensmtpd::{run_filter, Filter, ReportEntry};
use opensmtpd_derive::register;

struct MyCounter {
    nb: u64,

impl Filter for MyCounter {
    fn on_report_link_disconnect(&mut self, _entry: &ReportEntry) {
        self.nb + 1;

fn main() {
    let mut my_counter = MyCounter { nb: 0, };
    run_filter(&mut my_counter);

The following filter removes the X-Originating-Ip header.

Be careful, this is not production-ready since it does not support long headers and cannot differentiate a header from the body's content.

use opensmtpd::{return_data_line, run_filter, Filter, FilterEntry};
use opensmtpd_derive::register;

pub const HEADER_NAME: &str = "x-originating-ip:";
pub const HEADER_LEN: usize = 17;

struct RmXOriginatingIp {}

impl Filter for RmXOriginatingIp {
    fn on_filter_data_line(&mut self, entry: &FilterEntry, data_line: &[u8]) {
        if data_line.len() >= HEADER_LEN {
            let head_start = data_line[..HEADER_LEN].to_vec();
            if let Ok(s) = String::from_utf8(head_start) {
                if s.to_lowercase() == HEADER_NAME {
        return_data_line(entry, data_line);

fn main() {
    let mut my_filter = RmXOriginatingIp {};
    run_filter(&mut my_filter);

More examples can be found in the examples directory.

Documentation about filters

This documentation is not meant to provide information about the filters. For that purpose, you should refer to the smtpd-filters man page (man 7 smtpd-filters).

In the case this man page has not been installed with OpenSMTPD or if you want the latest one available, you can download it from the OpenSMTPD repository:

curl -sSf "https://raw.githubusercontent.com/OpenSMTPD/OpenSMTPD/master/usr.sbin/smtpd/smtpd-filters.7" | man -l -

Alternatively, using zsh, you can use the following variants. Useful on system where man is unable to read from stdin (yes BSD, that's you).

man =(curl -sSf "https://raw.githubusercontent.com/OpenSMTPD/OpenSMTPD/master/usr.sbin/smtpd/smtpd-filters.7")