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}