Skip to main content

rusmes_core/matchers/
remote_address.rs

1//! Matcher for messages from specific remote addresses or CIDR ranges
2
3use crate::matcher::Matcher;
4use async_trait::async_trait;
5use rusmes_proto::{Mail, MailAddress};
6use std::net::IpAddr;
7
8/// Matches messages from specific IP addresses or CIDR ranges
9pub struct RemoteAddressMatcher {
10    allowed_ips: Vec<IpAddr>,
11    allowed_cidrs: Vec<(IpAddr, u8)>,
12}
13
14impl RemoteAddressMatcher {
15    /// Create a new RemoteAddress matcher with IP addresses and CIDR ranges
16    pub fn new(allowed_ips: Vec<IpAddr>, allowed_cidrs: Vec<(IpAddr, u8)>) -> Self {
17        Self {
18            allowed_ips,
19            allowed_cidrs,
20        }
21    }
22
23    /// Check if an IP address matches any of the allowed CIDR ranges
24    fn matches_cidr(&self, addr: &IpAddr) -> bool {
25        for (cidr_addr, prefix_len) in &self.allowed_cidrs {
26            if Self::ip_in_cidr(addr, cidr_addr, *prefix_len) {
27                return true;
28            }
29        }
30        false
31    }
32
33    /// Check if an IP address is within a CIDR range
34    fn ip_in_cidr(addr: &IpAddr, cidr_addr: &IpAddr, prefix_len: u8) -> bool {
35        match (addr, cidr_addr) {
36            (IpAddr::V4(a), IpAddr::V4(c)) => {
37                let addr_bits = u32::from_be_bytes(a.octets());
38                let cidr_bits = u32::from_be_bytes(c.octets());
39                let mask = !0u32 << (32 - prefix_len);
40                (addr_bits & mask) == (cidr_bits & mask)
41            }
42            (IpAddr::V6(a), IpAddr::V6(c)) => {
43                let addr_bits = u128::from_be_bytes(a.octets());
44                let cidr_bits = u128::from_be_bytes(c.octets());
45                let mask = !0u128 << (128 - prefix_len);
46                (addr_bits & mask) == (cidr_bits & mask)
47            }
48            _ => false,
49        }
50    }
51}
52
53#[async_trait]
54impl Matcher for RemoteAddressMatcher {
55    async fn match_mail(&self, mail: &Mail) -> anyhow::Result<Vec<MailAddress>> {
56        if let Some(remote_addr) = mail.remote_addr() {
57            if self.allowed_ips.contains(remote_addr) || self.matches_cidr(remote_addr) {
58                return Ok(mail.recipients().to_vec());
59            }
60        }
61        Ok(Vec::new())
62    }
63
64    fn name(&self) -> &str {
65        "RemoteAddress"
66    }
67}