(in development)
independent async milter library in Rust
The indymilter library provides facilities for creating milters in pure asynchronous Rust. A milter is a mail filtering application that can be integrated with MTAs (mail servers) such as [Postfix].
The main advantage of indymilter over similar libraries is its wholehearted adoption of the asynchronous paradigm, which enables virtually unlimited concurrency. An arbitrary number of MTA connections can be handled concurrently by a limited number of threads. Internally, the library is based on [Tokio].
This library is an implementation of the milter side of the sendmail milter
protocol. As such, it assumes the place of the libmilter C library distributed
with sendmail. It has no dependency on that library but is a stand-alone, pure,
safe (no unsafe
) Rust product.
As libmilter is the de facto standard of the venerable milter protocol, indymilter mimics its behaviour accurately. In order to guarantee perfect compatibility with existing MTAs, indymilter aims for bug-for-bug compatibility rather than a fancy ‘modern’ reimagining of the protocol. Questions about the indymilter source code can be answered by looking at the libmilter source.
To use indymilter, add it as a dependency in Cargo.toml
. Further usage
information can be found in the following places:
minimal.rs
]full.rs
]: a commented, real-world exampleinspect.rs
]For an overview of the protocol design and detailed API functionality, see the original sendmail milter API documentation. This documentation can be found in the sendmail package of your distro (for example, on Debian and Ubuntu at /usr/share/doc/sendmail-doc/libmilter/html/index.html), or directly in the sendmail source tarball at ftp://ftp.sendmail.org/pub/sendmail.
While indymilter contains an API similar to libmilter, it does not duplicate documentation for the entire API. For details you will need to check the sendmail docs.
Here is a simple but complete milter program that logs client IP addresses:
```rust use indymilter::{Callbacks, Context, SocketInfo, Status}; use tokio::{net::UnixListener, signal};
async fn main() { let listener = UnixListener::bind("/run/ipmilter.sock") .expect("cannot open milter socket");
let callbacks = Callbacks::new()
.on_connect(|context, _, socket_info| {
Box::pin(handle_connect(context, socket_info))
});
let config = Default::default();
indymilter::run(listener, callbacks, config, signal::ctrl_c())
.await
.expect("milter execution failed");
}
async fn handleconnect( _: &mut Context<()>, socketinfo: SocketInfo, ) -> Status { if let SocketInfo::Inet(addr) = socket_info { println!("connect from {}", addr.ip()); }
Status::Continue
} ```
The above milter will try to open a UNIX domain socket at /run/ipmilter.sock
,
and will then process incoming MTA connections until it is shut down with
Control-C.
Copyright © 2021–2022 David Bürgin
This program is free software: you can redistribute it and/or modify it under the terms of the GNU General Public License as published by the Free Software Foundation, either version 3 of the License, or (at your option) any later version.