Skip to main content

email_auth/spf/
types.rs

1use std::net::{Ipv4Addr, Ipv6Addr};
2
3/// SPF evaluation result (RFC 7208 Section 2.6).
4#[derive(Debug, Clone, PartialEq, Eq)]
5pub enum SpfResult {
6    /// Sender is authorized.
7    Pass,
8    /// Sender is NOT authorized, with optional explanation from exp=.
9    Fail { explanation: Option<String> },
10    /// Weak authorization failure.
11    SoftFail,
12    /// No assertion made.
13    Neutral,
14    /// No SPF record found.
15    None,
16    /// Transient DNS error.
17    TempError,
18    /// Permanent error (syntax, too many lookups, etc.).
19    PermError,
20}
21
22/// Qualifier prefix on a directive (RFC 7208 Section 4.6.2).
23#[derive(Debug, Clone, Copy, PartialEq, Eq)]
24pub enum Qualifier {
25    Pass,     // + (default)
26    Fail,     // -
27    SoftFail, // ~
28    Neutral,  // ?
29}
30
31impl Qualifier {
32    pub fn from_char(c: char) -> Option<Self> {
33        match c {
34            '+' => Some(Qualifier::Pass),
35            '-' => Some(Qualifier::Fail),
36            '~' => Some(Qualifier::SoftFail),
37            '?' => Some(Qualifier::Neutral),
38            _ => None,
39        }
40    }
41}
42
43/// A directive is a qualifier + mechanism pair.
44#[derive(Debug, Clone, PartialEq, Eq)]
45pub struct Directive {
46    pub qualifier: Qualifier,
47    pub mechanism: Mechanism,
48}
49
50/// SPF mechanism variants (RFC 7208 Section 5).
51#[derive(Debug, Clone, PartialEq, Eq)]
52pub enum Mechanism {
53    All,
54    Include { domain: String },
55    A { domain: Option<String>, cidr4: Option<u8>, cidr6: Option<u8> },
56    Mx { domain: Option<String>, cidr4: Option<u8>, cidr6: Option<u8> },
57    Ptr { domain: Option<String> },
58    Ip4 { addr: Ipv4Addr, prefix: Option<u8> },
59    Ip6 { addr: Ipv6Addr, prefix: Option<u8> },
60    Exists { domain: String },
61}
62
63/// Parsed SPF record.
64#[derive(Debug, Clone, PartialEq, Eq)]
65pub struct SpfRecord {
66    pub directives: Vec<Directive>,
67    pub redirect: Option<String>,
68    pub explanation: Option<String>,
69}
70
71impl SpfRecord {
72    /// Parse an SPF record string (the TXT record value, starting with "v=spf1").
73    /// Returns PermError description on parse failure.
74    pub fn parse(record: &str) -> Result<Self, String> {
75        super::parser::parse_record(record)
76    }
77}