1#[derive(Debug, Clone, PartialEq)]
7#[doc(hidden)]
8pub struct IpRange {
9 pub network: u128,
10 pub mask: u128,
11}
12
13#[derive(Debug, Clone, PartialEq)]
18pub enum TrustedProxies {
19 All,
20 Ranges(Vec<IpRangeEntry>),
21}
22
23#[derive(Debug, Clone, PartialEq)]
25pub struct IpRangeEntry(IpRange);
26
27pub const DEFAULT_TRUSTED_PROXIES: &str = "127.0.0.0/8, ::1, 10.0.0.0/8, 172.16.0.0/12, 192.168.0.0/16, 100.64.0.0/10, fc00::/7";
29
30impl TrustedProxies {
31 pub fn parse(list: &str) -> Result<TrustedProxies, String> {
34 let list = if list.trim().is_empty() { DEFAULT_TRUSTED_PROXIES } else { list.trim() };
35 if list == "*" {
36 return Ok(TrustedProxies::All);
37 }
38 if list.eq_ignore_ascii_case("none") {
39 return Ok(TrustedProxies::Ranges(Vec::new()));
40 }
41 list.split(',')
42 .map(str::trim)
43 .filter(|e| !e.is_empty())
44 .map(|e| IpRange::parse(e).map(IpRangeEntry).ok_or_else(|| format!("trusted_proxies: {e:?} is not an address or CIDR range")))
45 .collect::<Result<Vec<_>, _>>()
46 .map(TrustedProxies::Ranges)
47 }
48
49 pub fn trusts(&self, ip: &str) -> bool {
51 match self {
52 TrustedProxies::All => true,
53 TrustedProxies::Ranges(ranges) => parse_ip(ip).is_some_and(|a| ranges.iter().any(|r| (a & r.0.mask) == r.0.network)),
54 }
55 }
56}
57
58pub fn is_valid_entry(entry: &str) -> bool {
60 IpRange::parse(entry).is_some()
61}
62
63impl IpRange {
64 #[doc(hidden)]
65 pub fn parse(entry: &str) -> Option<Self> {
66 let entry = entry.trim();
67 let (addr, prefix_len) = match entry.split_once('/') {
68 Some((addr, len)) => {
69 let len: u32 = len.parse().ok()?;
70 let is_v4 = addr.parse::<std::net::Ipv4Addr>().is_ok();
71 let max = if is_v4 { 32 } else { 128 };
72 if len > max {
73 return None;
74 }
75 (addr, if is_v4 { len + 96 } else { len })
76 }
77 None => (entry, 128),
78 };
79 let network = parse_ip(addr)?;
80 let mask = if prefix_len == 0 { 0 } else { !0u128 << (128 - prefix_len) };
81 Some(IpRange { network: network & mask, mask })
82 }
83}
84
85#[doc(hidden)]
88pub fn parse_ip(s: &str) -> Option<u128> {
89 let s = s.trim().trim_start_matches('[').trim_end_matches(']');
90 let s = s.split('%').next()?;
91 match s.parse::<std::net::IpAddr>().ok()? {
92 std::net::IpAddr::V4(v4) => Some(u128::from(v4.to_ipv6_mapped())),
93 std::net::IpAddr::V6(v6) => Some(u128::from(v6)),
94 }
95}