spftrace 0.2.0

Utility for tracing SPF queries
# spftrace

The **spftrace** utility is a tool for executing, analysing, and displaying SPF
queries. SPF is specified in [RFC 7208].

This utility executes an SPF query with IP address and domain and shows the
result graphically as an evaluation tree.

This utility uses the [viaspf] library to execute SPF queries. The viaspf
library can trace a query as it executes and makes the trace available as
structured data in its API. spftrace then analyses this data and displays it.

Thanks to the underlying execution engine being a real, RFC-conformant SPF
implementation, spftrace handles all SPF policies correctly. Less well-known
features of SPF such as macros, dual CIDR prefix lengths, *exp* modifiers, or
the void lookup limit are all evaluated according to spec.

[RFC 7208]: https://www.rfc-editor.org/rfc/rfc7208
[viaspf]: https://crates.io/crates/viaspf

## Installation

The spftrace utility is a [Rust] program. Install it with Cargo as usual:

```
cargo install --locked spftrace
```

The minimum supported Rust version is 1.61.0.

[Rust]: https://www.rust-lang.org

## Usage

Pass a sender identity (an email address or a domain name) and an IP address to
spftrace.

Use the unspecified IP address `0.0.0.0` (IPv4) or `::` (IPv6) to display the
full evaluation tree of some SPF-enabled domain.

```
spftrace example.com 0.0.0.0
```

```
example.com
│   "v=spf1 mx include:spf.example.com ~all"
├── mx → example.com (lookups: 1/10, nested: 2/10)
│   ├── mx1.example.com
│   │   ├── 216.58.192.0
│   │   ├── 65.55.52.224
│   │   └── 207.46.116.128
│   └── mx2.example.com
│       └── 65.55.238.129
│   not-match
├── include:spf.example.com → spf.example.com (lookups: 2/10)
│   spf.example.com
│   │   "v=spf1 ip4:207.46.4.128/25 ip4:65.55.174.0/24 ip6:2c0f:fb50:4000::/36
│   │    ip6:2001:4860:4000::/36 ~all"
│   ├── ip4:207.46.4.128/25 not-match
│   ├── ip4:65.55.174.0/24 not-match
│   ├── ip6:2c0f:fb50:4000::/36 not-match
│   ├── ip6:2001:4860:4000::/36 not-match
│   └── all match result=softfail
│   not-match
└── all match result=softfail
softfail
```

The command shown above can be abbreviated to `spftrace example.com`: When no IP
address is specified, `0.0.0.0` is assumed.

For details, refer to the included manual page [*spftrace*(1)]. (You can view
the manual page without installing by passing the file path to `man`: `man
./spftrace.1`)

[*spftrace*(1)]: https://gitlab.com/glts/spftrace/-/blob/0.2.0/spftrace.1

## Development

Unit tests are in separate module `tests`. Specify a test name and pass the
`--test-threads 1` test binary option to run and visually check some test (Cargo
is unable to capture stdout, so limiting the number of threads at least avoids
interleaved output).

For example:

```
cargo test ptr -- --test-threads 1
```

## Licence

Copyright © 2022–2023 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/.