Expand description
§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
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
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.
§Examples
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 {
#[register]
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 {
#[register]
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;
}
}
}
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")