# indymilter

*independent async milter library for Rust*

* pure, safe Rust library for writing milters
* asynchronous implementation based on Tokio
* no dependency on libmilter C library
* fully compatible with existing milter-aware MTAs

---

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 small 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 standard implementation 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.

[Postfix]: https://www.postfix.org
[Tokio]: https://tokio.rs

## Usage

To use [indymilter], add it as a dependency in `Cargo.toml`. Further usage
information can be found in the following places:

* [API documentation]
* Examples:
  - [`minimal.rs`]
  - [`inspect.rs`]
  - [`tlsinfo.rs`]: a commented, real-world example
* sendmail milter API documentation

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.

The above is important: While indymilter provides an API similar to libmilter,
it does not duplicate documentation for the entire API. For details you will
need to check the sendmail docs.

[indymilter]: https://crates.io/crates/indymilter
[API documentation]: https://docs.rs/indymilter
[`minimal.rs`]: https://codeberg.org/glts/indymilter/src/tag/0.3.0/examples/minimal.rs
[`inspect.rs`]: https://codeberg.org/glts/indymilter/src/tag/0.3.0/examples/inspect.rs
[`tlsinfo.rs`]: https://codeberg.org/glts/indymilter/src/tag/0.3.0/examples/tlsinfo.rs

## Example

Here is a simple but complete milter program that logs client IP addresses:

```rust
use indymilter::{Callbacks, Context, SocketInfo, Status};
use tokio::{net::TcpListener, signal};

#[tokio::main]
async fn main() {
    let listener = TcpListener::bind("127.0.0.1:3000")
        .await
        .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 handle_connect(
    _: &mut Context<()>,
    socket_info: SocketInfo,
) -> Status {
    if let SocketInfo::Inet(addr) = socket_info {
        println!("connect from {}", addr.ip());
    }

    Status::Continue
}
```

The above milter will try to open a TCP socket on port 3000, and will then
process incoming MTA connections until it is shut down with Control-C.

## Licence

Copyright © 2021–2024 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.

This program is distributed in the hope that it will be useful, but WITHOUT ANY
WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
PARTICULAR PURPOSE. See the GNU General Public License for more details.

You should have received a copy of the GNU General Public License along with
this program. If not, see <https://www.gnu.org/licenses/>.
