http_ip/
filter.rs

1//!Filtering of IP addresses
2
3use core::fmt;
4use core::net::{IpAddr, SocketAddr};
5
6///Interface to define function that filters out IP address
7///
8///When match is found, IP address is skipped from being selected as client's IP (e.g. it is load balancer IP)
9pub trait Filter: Sized {
10    ///Returns `true` if `ip` matches
11    fn is_match(&self, ip: IpAddr) -> bool;
12    #[inline(always)]
13    ///Combines `self` with `right` filter in `OR` operation
14    fn or<F2: Filter>(self, right: F2) -> Or<Self, F2> {
15        or(self, right)
16    }
17}
18
19impl Filter for () {
20    #[inline(always)]
21    ///NULL filter, never matching
22    fn is_match(&self, _: IpAddr) -> bool {
23        false
24    }
25}
26
27impl Filter for IpAddr {
28    #[inline(always)]
29    fn is_match(&self, ip: IpAddr) -> bool {
30        *self == ip
31    }
32}
33
34impl Filter for SocketAddr {
35    #[inline(always)]
36    fn is_match(&self, ip: IpAddr) -> bool {
37        self.ip() == ip
38    }
39}
40
41///Combination of filters with `OR` condition
42pub struct Or<F1, F2> {
43    left: F1,
44    right: F2,
45}
46
47impl<F1: Filter, F2: Filter> Filter for Or<F1, F2> {
48    #[inline(always)]
49    fn is_match(&self, ip: IpAddr) -> bool {
50        self.left.is_match(ip) || self.right.is_match(ip)
51    }
52}
53
54#[derive(Debug, PartialEq, Eq)]
55//Possible errors parsing CIDR
56enum ParseError<'a> {
57    //Error parsing CIDR expression
58    ParseError(ip_cidr::ParseError<'a>),
59    //CIDR expression is valid, but its prefix does not fit type of IP address
60    InvalidPrefix
61}
62
63#[repr(transparent)]
64#[derive(PartialEq, Eq)]
65///Error which is returned when parsing CIDR's textual representation
66pub struct CidrParseError<'a>(ParseError<'a>);
67
68impl fmt::Debug for CidrParseError<'_> {
69    #[inline(always)]
70    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
71        fmt::Debug::fmt(&self.0, fmt)
72    }
73}
74
75impl fmt::Display for CidrParseError<'_> {
76    #[inline(always)]
77    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
78        match &self.0 {
79            ParseError::InvalidPrefix => fmt.write_str("Invalid CIDR prefix"),
80            ParseError::ParseError(error) => fmt::Display::fmt(error, fmt),
81        }
82    }
83}
84
85#[repr(transparent)]
86#[derive(Copy, Clone, PartialEq, Eq, PartialOrd, Ord)]
87///CIDR filter
88pub struct Cidr(ip_cidr::Cidr);
89
90impl Cidr {
91    #[inline]
92    ///Creates new instance from textual representation
93    pub const fn from_text(text: &str) -> Result<Self, CidrParseError<'_>> {
94        match ip_cidr::parse_cidr(text) {
95            Ok(Some(inner)) => Ok(Self(inner)),
96            Ok(None) => Err(CidrParseError(ParseError::InvalidPrefix)),
97            Err(error) => Err(CidrParseError(ParseError::ParseError(error))),
98        }
99    }
100
101    #[inline]
102    ///Creates new instance from IP and prefix, returning error if `prefix` is invalid
103    pub const fn new(ip: IpAddr, prefix: u8) -> Result<Self, CidrParseError<'static>> {
104        match ip_cidr::Cidr::new(ip, prefix) {
105            Some(cidr) => Ok(Self(cidr)),
106            None => Err(CidrParseError(ParseError::InvalidPrefix)),
107        }
108    }
109}
110
111impl Filter for Cidr {
112    #[inline(always)]
113    fn is_match(&self, ip: IpAddr) -> bool {
114        self.0.contains(ip)
115    }
116}
117
118impl fmt::Debug for Cidr {
119    #[inline(always)]
120    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
121        fmt::Debug::fmt(&self.0, fmt)
122    }
123}
124
125impl fmt::Display for Cidr {
126    #[inline(always)]
127    fn fmt(&self, fmt: &mut fmt::Formatter<'_>) -> fmt::Result {
128        fmt::Display::fmt(&self.0, fmt)
129    }
130}
131
132#[inline]
133///Creates new `OR` filter out of two filters
134pub const fn or<F1, F2>(left: F1, right: F2) -> Or<F1, F2> {
135    Or {
136        left,
137        right
138    }
139}
140