/rust/registry/src/index.crates.io-6f17d22bba15001f/cap-primitives-3.4.4/src/net/pool.rs
Line | Count | Source (jump to first uncovered line) |
1 | | #[cfg(test)] |
2 | | use crate::ambient_authority; |
3 | | use crate::net::pool::net::ToSocketAddrs; |
4 | | use crate::AmbientAuthority; |
5 | | use ipnet::IpNet; |
6 | | #[cfg(test)] |
7 | | use std::str::FromStr; |
8 | | use std::{io, net}; |
9 | | |
10 | | // TODO: Perhaps we should have our own version of `ToSocketAddrs` which |
11 | | // returns hostnames rather than parsing them, so we can add unresolved |
12 | | // hostnames to the pool. |
13 | | #[derive(Clone)] |
14 | | enum AddrSet { |
15 | | Net(IpNet), |
16 | | } |
17 | | |
18 | | impl AddrSet { |
19 | 0 | fn contains(&self, addr: net::IpAddr) -> bool { |
20 | 0 | match self { |
21 | 0 | Self::Net(ip_net) => ip_net.contains(&addr), |
22 | 0 | } |
23 | 0 | } |
24 | | } |
25 | | |
26 | | #[derive(Clone)] |
27 | | struct IpGrant { |
28 | | set: AddrSet, |
29 | | ports_start: u16, |
30 | | ports_end: Option<u16>, |
31 | | } |
32 | | |
33 | | impl IpGrant { |
34 | 0 | fn contains(&self, addr: &net::SocketAddr) -> bool { |
35 | 0 | if !self.set.contains(addr.ip()) { |
36 | 0 | return false; |
37 | 0 | } |
38 | 0 |
|
39 | 0 | let port = addr.port(); |
40 | 0 | if port < self.ports_start { |
41 | 0 | return false; |
42 | 0 | } |
43 | 0 | if let Some(ports_end) = self.ports_end { |
44 | 0 | if port >= ports_end { |
45 | 0 | return false; |
46 | 0 | } |
47 | 0 | } |
48 | | |
49 | 0 | true |
50 | 0 | } |
51 | | } |
52 | | |
53 | | /// A representation of a set of network resources that may be accessed. |
54 | | /// |
55 | | /// This is presently a very simple concept, though it could grow in |
56 | | /// sophistication in the future. |
57 | | /// |
58 | | /// `Pool` implements `Clone`, which creates new independent entities that |
59 | | /// carry the full authority of the originals. This means that in a borrow |
60 | | /// of a `Pool`, the scope of the authority is not necessarily limited to |
61 | | /// the scope of the borrow. |
62 | | /// |
63 | | /// Similarly, the [`cap_net_ext::PoolExt`] class allows creating "binder" |
64 | | /// and "connecter" objects which represent capabilities to bind and |
65 | | /// connect to addresses. |
66 | | /// |
67 | | /// [`cap_net_ext::PoolExt`]: https://docs.rs/cap-net-ext/latest/cap_net_ext/trait.PoolExt.html |
68 | | #[derive(Clone, Default)] |
69 | | pub struct Pool { |
70 | | // TODO: when compiling for WASI, use WASI-specific handle instead |
71 | | grants: Vec<IpGrant>, |
72 | | } |
73 | | |
74 | | impl Pool { |
75 | | /// Construct a new empty pool. |
76 | 0 | pub fn new() -> Self { |
77 | 0 | Self { grants: Vec::new() } |
78 | 0 | } |
79 | | |
80 | | /// Add addresses to the pool. |
81 | | /// |
82 | | /// # Ambient Authority |
83 | | /// |
84 | | /// This function allows ambient access to any IP address. |
85 | 0 | pub fn insert<A: ToSocketAddrs>( |
86 | 0 | &mut self, |
87 | 0 | addrs: A, |
88 | 0 | ambient_authority: AmbientAuthority, |
89 | 0 | ) -> io::Result<()> { |
90 | 0 | for addr in addrs.to_socket_addrs()? { |
91 | 0 | self.insert_socket_addr(addr, ambient_authority); |
92 | 0 | } |
93 | 0 | Ok(()) |
94 | 0 | } |
95 | | |
96 | | /// Add a specific [`net::SocketAddr`] to the pool. |
97 | | /// |
98 | | /// # Ambient Authority |
99 | | /// |
100 | | /// This function allows ambient access to any IP address. |
101 | 0 | pub fn insert_socket_addr( |
102 | 0 | &mut self, |
103 | 0 | addr: net::SocketAddr, |
104 | 0 | ambient_authority: AmbientAuthority, |
105 | 0 | ) { |
106 | 0 | self.insert_ip_net(addr.ip().into(), addr.port(), ambient_authority) |
107 | 0 | } |
108 | | |
109 | | /// Add a range of network addresses, accepting any port, to the pool. |
110 | | /// |
111 | | /// # Ambient Authority |
112 | | /// |
113 | | /// This function allows ambient access to any IP address. |
114 | 0 | pub fn insert_ip_net_port_any( |
115 | 0 | &mut self, |
116 | 0 | ip_net: ipnet::IpNet, |
117 | 0 | ambient_authority: AmbientAuthority, |
118 | 0 | ) { |
119 | 0 | self.insert_ip_net_port_range(ip_net, 0, None, ambient_authority) |
120 | 0 | } |
121 | | |
122 | | /// Add a range of network addresses, accepting a range of ports, to the |
123 | | /// pool. |
124 | | /// |
125 | | /// This grants access to the port range starting at `ports_start` and, |
126 | | /// if `ports_end` is provided, ending before `ports_end`. |
127 | | /// |
128 | | /// # Ambient Authority |
129 | | /// |
130 | | /// This function allows ambient access to any IP address. |
131 | 0 | pub fn insert_ip_net_port_range( |
132 | 0 | &mut self, |
133 | 0 | ip_net: ipnet::IpNet, |
134 | 0 | ports_start: u16, |
135 | 0 | ports_end: Option<u16>, |
136 | 0 | ambient_authority: AmbientAuthority, |
137 | 0 | ) { |
138 | 0 | let _ = ambient_authority; |
139 | 0 |
|
140 | 0 | self.grants.push(IpGrant { |
141 | 0 | set: AddrSet::Net(ip_net), |
142 | 0 | ports_start, |
143 | 0 | ports_end, |
144 | 0 | }) |
145 | 0 | } |
146 | | |
147 | | /// Add a range of network addresses with a specific port to the pool. |
148 | | /// |
149 | | /// # Ambient Authority |
150 | | /// |
151 | | /// This function allows ambient access to any IP address. |
152 | 0 | pub fn insert_ip_net( |
153 | 0 | &mut self, |
154 | 0 | ip_net: ipnet::IpNet, |
155 | 0 | port: u16, |
156 | 0 | ambient_authority: AmbientAuthority, |
157 | 0 | ) { |
158 | 0 | self.insert_ip_net_port_range(ip_net, port, port.checked_add(1), ambient_authority) |
159 | 0 | } |
160 | | |
161 | | /// Check whether the given address is within the pool. |
162 | 0 | pub fn check_addr(&self, addr: &net::SocketAddr) -> io::Result<()> { |
163 | 0 | if self.grants.iter().any(|grant| grant.contains(addr)) { |
164 | 0 | Ok(()) |
165 | | } else { |
166 | 0 | Err(io::Error::new( |
167 | 0 | io::ErrorKind::PermissionDenied, |
168 | 0 | "An address was outside the pool", |
169 | 0 | )) |
170 | | } |
171 | 0 | } |
172 | | } |
173 | | |
174 | | /// An empty array of `SocketAddr`s. |
175 | | pub const NO_SOCKET_ADDRS: &[net::SocketAddr] = &[]; |
176 | | |
177 | | /// Return an error for reporting that no socket addresses were available. |
178 | | #[cold] |
179 | 0 | pub fn no_socket_addrs() -> io::Error { |
180 | 0 | std::net::TcpListener::bind(NO_SOCKET_ADDRS).unwrap_err() |
181 | 0 | } |
182 | | |
183 | | #[test] |
184 | | fn test_empty() { |
185 | | let p = Pool::new(); |
186 | | |
187 | | p.check_addr(&net::SocketAddr::from_str("[::1]:0").unwrap()) |
188 | | .unwrap_err(); |
189 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1023").unwrap()) |
190 | | .unwrap_err(); |
191 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1024").unwrap()) |
192 | | .unwrap_err(); |
193 | | p.check_addr(&net::SocketAddr::from_str("[::1]:8080").unwrap()) |
194 | | .unwrap_err(); |
195 | | p.check_addr(&net::SocketAddr::from_str("[::1]:65535").unwrap()) |
196 | | .unwrap_err(); |
197 | | } |
198 | | |
199 | | #[test] |
200 | | fn test_port_any() { |
201 | | let mut p = Pool::new(); |
202 | | p.insert_ip_net_port_any( |
203 | | IpNet::new(net::IpAddr::V6(net::Ipv6Addr::LOCALHOST), 48).unwrap(), |
204 | | ambient_authority(), |
205 | | ); |
206 | | |
207 | | p.check_addr(&net::SocketAddr::from_str("[::1]:0").unwrap()) |
208 | | .unwrap(); |
209 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1023").unwrap()) |
210 | | .unwrap(); |
211 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1024").unwrap()) |
212 | | .unwrap(); |
213 | | p.check_addr(&net::SocketAddr::from_str("[::1]:8080").unwrap()) |
214 | | .unwrap(); |
215 | | p.check_addr(&net::SocketAddr::from_str("[::1]:65535").unwrap()) |
216 | | .unwrap(); |
217 | | } |
218 | | |
219 | | #[test] |
220 | | fn test_port_range() { |
221 | | let mut p = Pool::new(); |
222 | | p.insert_ip_net_port_range( |
223 | | IpNet::new(net::IpAddr::V6(net::Ipv6Addr::LOCALHOST), 48).unwrap(), |
224 | | 1024, |
225 | | Some(9000), |
226 | | ambient_authority(), |
227 | | ); |
228 | | |
229 | | p.check_addr(&net::SocketAddr::from_str("[::1]:0").unwrap()) |
230 | | .unwrap_err(); |
231 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1023").unwrap()) |
232 | | .unwrap_err(); |
233 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1024").unwrap()) |
234 | | .unwrap(); |
235 | | p.check_addr(&net::SocketAddr::from_str("[::1]:8080").unwrap()) |
236 | | .unwrap(); |
237 | | p.check_addr(&net::SocketAddr::from_str("[::1]:65535").unwrap()) |
238 | | .unwrap_err(); |
239 | | } |
240 | | |
241 | | #[test] |
242 | | fn test_port_one() { |
243 | | let mut p = Pool::new(); |
244 | | p.insert_ip_net( |
245 | | IpNet::new(net::IpAddr::V6(net::Ipv6Addr::LOCALHOST), 48).unwrap(), |
246 | | 8080, |
247 | | ambient_authority(), |
248 | | ); |
249 | | |
250 | | p.check_addr(&net::SocketAddr::from_str("[::1]:0").unwrap()) |
251 | | .unwrap_err(); |
252 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1023").unwrap()) |
253 | | .unwrap_err(); |
254 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1024").unwrap()) |
255 | | .unwrap_err(); |
256 | | p.check_addr(&net::SocketAddr::from_str("[::1]:8080").unwrap()) |
257 | | .unwrap(); |
258 | | p.check_addr(&net::SocketAddr::from_str("[::1]:65535").unwrap()) |
259 | | .unwrap_err(); |
260 | | } |
261 | | |
262 | | #[test] |
263 | | fn test_addrs() { |
264 | | let mut p = Pool::new(); |
265 | | match p.insert("example.com:80", ambient_authority()) { |
266 | | Ok(()) => (), |
267 | | Err(_) => return, // not all test environments have DNS |
268 | | } |
269 | | |
270 | | p.check_addr(&net::SocketAddr::from_str("[::1]:0").unwrap()) |
271 | | .unwrap_err(); |
272 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1023").unwrap()) |
273 | | .unwrap_err(); |
274 | | p.check_addr(&net::SocketAddr::from_str("[::1]:1024").unwrap()) |
275 | | .unwrap_err(); |
276 | | p.check_addr(&net::SocketAddr::from_str("[::1]:8080").unwrap()) |
277 | | .unwrap_err(); |
278 | | p.check_addr(&net::SocketAddr::from_str("[::1]:65535").unwrap()) |
279 | | .unwrap_err(); |
280 | | |
281 | | for addr in "example.com:80".to_socket_addrs().unwrap() { |
282 | | p.check_addr(&addr).unwrap(); |
283 | | } |
284 | | } |