rama_net/stream/matcher/
private_ip.rs

1use crate::stream::dep::ipnet::IpNet;
2use rama_core::{Context, context::Extensions};
3
4#[cfg(feature = "http")]
5use crate::stream::SocketInfo;
6#[cfg(feature = "http")]
7use rama_http_types::Request;
8
9#[derive(Debug, Clone)]
10/// Matcher based on the ip part of the [`SocketAddr`] of the peer,
11/// matching only if the IP is considered a private address.
12///
13/// Whether or not an address is considered private is determined by the following
14/// RFCs:
15///
16/// - [RFC 1918](https://datatracker.ietf.org/doc/html/rfc1918): Address Allocation for Private Internets (IPv4)
17/// - [RFC 4193](https://datatracker.ietf.org/doc/html/rfc4193): Unique Local IPv6 Unicast Addresses
18/// - [RFC 3927](https://datatracker.ietf.org/doc/html/rfc3927): Dynamic Configuration of IPv4 Link-Local Addresses
19/// - [RFC 4291](https://datatracker.ietf.org/doc/html/rfc4291): IP Version 6 Addressing Architecture
20/// - [RFC 1122](https://datatracker.ietf.org/doc/html/rfc1122): Requirements for Internet Hosts -- Communication Layers
21/// - [RFC 6890](https://datatracker.ietf.org/doc/html/rfc6890): Special-Purpose IP Address Registries
22/// - [RFC rfc6598](https://datatracker.ietf.org/doc/html/rfc6598): IANA-Reserved IPv4 Prefix for Shared Address Space
23///
24/// [`SocketAddr`]: std::net::SocketAddr
25pub struct PrivateIpNetMatcher {
26    matchers: [IpNet; 11],
27    optional: bool,
28}
29
30impl PrivateIpNetMatcher {
31    /// create a new loopback matcher to match on the ip part a [`SocketAddr`],
32    /// matching only if the IP is considered a private address.
33    ///
34    /// This matcher will not match in case socket address could not be found,
35    /// if you want to match in case socket address could not be found,
36    /// use the [`PrivateIpNetMatcher::optional`] constructor..
37    ///
38    /// [`SocketAddr`]: std::net::SocketAddr
39    pub fn new() -> Self {
40        Self::inner_new(false)
41    }
42
43    /// create a new loopback matcher to match on the ip part a [`SocketAddr`],
44    /// matching only if the IP is considered a private address or no socket address could be found.
45    ///
46    /// This matcher will match in case socket address could not be found.
47    /// Use the [`PrivateIpNetMatcher::new`] constructor if you want do not want
48    /// to match in case socket address could not be found.
49    ///
50    /// [`SocketAddr`]: std::net::SocketAddr
51    pub fn optional() -> Self {
52        Self::inner_new(true)
53    }
54
55    fn inner_new(optional: bool) -> Self {
56        Self {
57            matchers: [
58                // This host on this network
59                // https://datatracker.ietf.org/doc/html/rfc1122#section-3.2.1.3
60                "0.0.0.0/8"
61                    .parse::<IpNet>()
62                    .expect("parse 0.0.0.0/8 as IpNet"),
63                // Private-Use
64                // https://datatracker.ietf.org/doc/html/rfc1918
65                "10.0.0.0/8"
66                    .parse::<IpNet>()
67                    .expect("parse 10.0.0.0/8 as IpNet"),
68                // Shared Address Space
69                // https://datatracker.ietf.org/doc/html/rfc6598
70                "100.64.0.0/10"
71                    .parse::<IpNet>()
72                    .expect("parse 100.64.0.0/10 as IpNet"),
73                // Loopback
74                // https://datatracker.ietf.org/doc/html/rfc1122#section-3.2.1.3
75                "127.0.0.0/8"
76                    .parse::<IpNet>()
77                    .expect("parse 127.0.0.0/8 as IpNet"),
78                // Link Local
79                // https://datatracker.ietf.org/doc/html/rfc3927
80                "169.254.0.0/16"
81                    .parse::<IpNet>()
82                    .expect("parse 169.254.0.0/16 as IpNet"),
83                // Private-Use
84                // https://datatracker.ietf.org/doc/html/rfc1918
85                "172.16.0.0/12"
86                    .parse::<IpNet>()
87                    .expect("parse 172.16.0.0/12 as IpNet"),
88                // Private-Use
89                // https://datatracker.ietf.org/doc/html/rfc1918
90                "192.168.0.0/16"
91                    .parse::<IpNet>()
92                    .expect("parse 192.168.0.0/16 as IpNet"),
93                // Unique-Local
94                // https://datatracker.ietf.org/doc/html/rfc4193
95                "fc00::/7"
96                    .parse::<IpNet>()
97                    .expect("parse fc00::/7 as IpNet"),
98                // Linked-Scoped Unicast
99                // https://datatracker.ietf.org/doc/html/rfc4291
100                "fe80::/10"
101                    .parse::<IpNet>()
102                    .expect("parse fe80::/10 as IpNet"),
103                // Loopback Address
104                // https://datatracker.ietf.org/doc/html/rfc4291
105                "::1/128".parse::<IpNet>().expect("parse ::1/128 as IpNet"),
106                // Unspecified Address
107                // https://datatracker.ietf.org/doc/html/rfc4291
108                "::/128".parse::<IpNet>().expect("parse ::/128 as IpNet"),
109            ],
110            optional,
111        }
112    }
113}
114
115impl Default for PrivateIpNetMatcher {
116    fn default() -> Self {
117        Self::new()
118    }
119}
120
121#[cfg(feature = "http")]
122impl<State, Body> rama_core::matcher::Matcher<State, Request<Body>> for PrivateIpNetMatcher {
123    fn matches(
124        &self,
125        _ext: Option<&mut Extensions>,
126        ctx: &Context<State>,
127        _req: &Request<Body>,
128    ) -> bool {
129        ctx.get::<SocketInfo>()
130            .map(|info| {
131                let peer_ip = IpNet::from(info.peer_addr().ip());
132                self.matchers.iter().any(|ip_net| ip_net.contains(&peer_ip))
133            })
134            .unwrap_or(self.optional)
135    }
136}
137
138impl<State, Socket> rama_core::matcher::Matcher<State, Socket> for PrivateIpNetMatcher
139where
140    Socket: crate::stream::Socket,
141{
142    fn matches(
143        &self,
144        _ext: Option<&mut Extensions>,
145        _ctx: &Context<State>,
146        stream: &Socket,
147    ) -> bool {
148        stream
149            .peer_addr()
150            .map(|addr| {
151                let peer_ip = IpNet::from(addr.ip());
152                self.matchers.iter().any(|ip_net| ip_net.contains(&peer_ip))
153            })
154            .unwrap_or(self.optional)
155    }
156}
157
158#[cfg(test)]
159mod test {
160    use super::*;
161    use rama_core::matcher::Matcher;
162    use std::net::SocketAddr;
163
164    #[cfg(feature = "http")]
165    #[test]
166    fn test_local_ip_net_matcher_http() {
167        let matcher = PrivateIpNetMatcher::new();
168
169        let mut ctx = Context::default();
170        let req = Request::builder()
171            .method("GET")
172            .uri("/hello")
173            .body(())
174            .unwrap();
175
176        // test #1: no match: test with no socket info registered
177        assert!(!matcher.matches(None, &ctx, &req));
178
179        // test #2: no match: test with remote network address (ipv4)
180        ctx.insert(SocketInfo::new(None, ([1, 1, 1, 1], 8080).into()));
181        assert!(!matcher.matches(None, &ctx, &req));
182
183        // test #3: no match: test with remote network address (ipv6)
184        ctx.insert(SocketInfo::new(
185            None,
186            ([1, 1, 1, 1, 1, 1, 1, 1], 8080).into(),
187        ));
188        assert!(!matcher.matches(None, &ctx, &req));
189
190        // test #4: match: test with private address (ipv4)
191        ctx.insert(SocketInfo::new(None, ([127, 0, 0, 1], 8080).into()));
192        assert!(matcher.matches(None, &ctx, &req));
193
194        // test #5: match: test with another private address (ipv4)
195        ctx.insert(SocketInfo::new(None, ([192, 168, 0, 24], 8080).into()));
196        assert!(matcher.matches(None, &ctx, &req));
197
198        // test #6: match: test with private address (ipv6)
199        ctx.insert(SocketInfo::new(
200            None,
201            ([0, 0, 0, 0, 0, 0, 0, 1], 8080).into(),
202        ));
203        assert!(matcher.matches(None, &ctx, &req));
204
205        // test #7: match: test with missing socket info, but it's seen as optional
206        let matcher = PrivateIpNetMatcher::optional();
207        let ctx = Context::default();
208        assert!(matcher.matches(None, &ctx, &req));
209    }
210
211    #[test]
212    fn test_local_ip_net_matcher_socket_trait() {
213        let matcher = PrivateIpNetMatcher::new();
214
215        let ctx = Context::default();
216
217        struct FakeSocket {
218            local_addr: Option<SocketAddr>,
219            peer_addr: Option<SocketAddr>,
220        }
221
222        impl crate::stream::Socket for FakeSocket {
223            fn local_addr(&self) -> std::io::Result<SocketAddr> {
224                match &self.local_addr {
225                    Some(addr) => Ok(*addr),
226                    None => Err(std::io::Error::from(std::io::ErrorKind::AddrNotAvailable)),
227                }
228            }
229
230            fn peer_addr(&self) -> std::io::Result<SocketAddr> {
231                match &self.peer_addr {
232                    Some(addr) => Ok(*addr),
233                    None => Err(std::io::Error::from(std::io::ErrorKind::AddrNotAvailable)),
234                }
235            }
236        }
237
238        let mut socket = FakeSocket {
239            local_addr: None,
240            peer_addr: None,
241        };
242
243        // test #1: no match: test with no socket info registered
244        assert!(!matcher.matches(None, &ctx, &socket));
245
246        // test #2: no match: test with network address (ipv4)
247        socket.peer_addr = Some(([1, 1, 1, 1], 8080).into());
248        assert!(!matcher.matches(None, &ctx, &socket));
249
250        // test #3: no match: test with another network address (ipv6)
251        socket.peer_addr = Some(([1, 1, 1, 1, 1, 1, 1, 1], 8080).into());
252        assert!(!matcher.matches(None, &ctx, &socket));
253
254        // test #4: match: test with private address (ipv4)
255        socket.peer_addr = Some(([192, 168, 0, 0], 8080).into());
256        assert!(matcher.matches(None, &ctx, &socket));
257
258        // test #5: match: test with another private address (ipv4)
259        socket.peer_addr = Some(([127, 3, 2, 1], 8080).into());
260        assert!(matcher.matches(None, &ctx, &socket));
261
262        // test #6: match: test with yet another private address (ipv6)
263        socket.peer_addr = Some(([0, 0, 0, 0, 0, 0, 0, 1], 8080).into());
264        assert!(matcher.matches(None, &ctx, &socket));
265
266        // test #7: match: test with missing socket info, but it's seen as optional
267        let matcher = PrivateIpNetMatcher::optional();
268        socket.peer_addr = None;
269        assert!(matcher.matches(None, &ctx, &socket));
270    }
271}