/rust/registry/src/index.crates.io-1949cf8c6b5b557f/hickory-server-0.25.1/src/access.rs
Line | Count | Source |
1 | | use std::net::IpAddr; |
2 | | |
3 | | use ipnet::{IpNet, Ipv4Net, Ipv6Net}; |
4 | | use prefix_trie::{Prefix, PrefixSet}; |
5 | | |
6 | | /// Type to evaluate access from a source address for accessing the server. |
7 | | /// |
8 | | /// Allowed networks will override denied networks, i.e. if a network is allowed, the deny rules will not be evaluated. |
9 | | /// Allowed networks are processed in the context of denied networks, that is, if there are no denied networks, then |
10 | | /// the allowed list will effectively deny access to anything that's not in the allowed list. On the other hand, if |
11 | | /// denied networks are specified, then allowed networks will only apply if the deny rule matched, but otherwise the |
12 | | /// address will be allowed. |
13 | | #[derive(Default)] |
14 | | pub(crate) struct AccessControl { |
15 | | ipv4: InnerAccessControl<Ipv4Net>, |
16 | | ipv6: InnerAccessControl<Ipv6Net>, |
17 | | } |
18 | | |
19 | | impl AccessControl { |
20 | | /// Insert a new network that is denied access to the server |
21 | 0 | pub(crate) fn insert_deny(&mut self, networks: &[IpNet]) { |
22 | 0 | for network in networks { |
23 | 0 | match network { |
24 | 0 | IpNet::V4(v4) => { |
25 | 0 | self.ipv4.deny.insert(*v4); |
26 | 0 | } |
27 | 0 | IpNet::V6(v6) => { |
28 | 0 | self.ipv6.deny.insert(*v6); |
29 | 0 | } |
30 | | } |
31 | | } |
32 | 0 | } |
33 | | |
34 | | /// Insert a new network that is allowed access to the server |
35 | 0 | pub(crate) fn insert_allow(&mut self, networks: &[IpNet]) { |
36 | 0 | for network in networks { |
37 | 0 | match network { |
38 | 0 | IpNet::V4(v4) => { |
39 | 0 | self.ipv4.allow.insert(*v4); |
40 | 0 | } |
41 | 0 | IpNet::V6(v6) => { |
42 | 0 | self.ipv6.allow.insert(*v6); |
43 | 0 | } |
44 | | } |
45 | | } |
46 | 0 | } |
47 | | |
48 | | /// Evaluate the IP address against the allowed networks |
49 | | /// |
50 | | /// # Arguments |
51 | | /// |
52 | | /// * `ip` - source ip address to evaluate |
53 | | /// |
54 | | /// # Return |
55 | | /// |
56 | | /// Ok if access is granted, Err otherwise |
57 | | #[must_use] |
58 | 0 | pub(crate) fn allow(&self, ip: IpAddr) -> bool { |
59 | 0 | match ip { |
60 | 0 | IpAddr::V4(v4) => { |
61 | 0 | let v4 = Ipv4Net::from(v4); |
62 | | |
63 | 0 | self.ipv4.allow(&v4) |
64 | | } |
65 | 0 | IpAddr::V6(v6) => { |
66 | 0 | let v6 = Ipv6Net::from(v6); |
67 | | |
68 | 0 | self.ipv6.allow(&v6) |
69 | | } |
70 | | } |
71 | 0 | } |
72 | | } |
73 | | |
74 | | #[derive(Default)] |
75 | | struct InnerAccessControl<I: Prefix> { |
76 | | allow: PrefixSet<I>, |
77 | | deny: PrefixSet<I>, |
78 | | } |
79 | | |
80 | | impl<I: Prefix> InnerAccessControl<I> { |
81 | | /// Evaluate the IP address against the allowed networks |
82 | | /// |
83 | | /// This allows for generic evaluation over IPv4 or IPv6 address spaces |
84 | | /// |
85 | | /// # Arguments |
86 | | /// |
87 | | /// * `ip` - source ip address to evaluate |
88 | | /// |
89 | | /// # Return |
90 | | /// |
91 | | /// Ok if access is granted, Err otherwise |
92 | | #[must_use] |
93 | 0 | fn allow(&self, ip: &I) -> bool { |
94 | | // If there are no allows or denies specified, we will always default to allow. |
95 | | // Allows without denies always translate to deny all except those in the allow list. |
96 | | // Denies without allows only deny those in the specified deny list. |
97 | | // If there are both allow and deny lists, then the deny list takes precedent with the allow list |
98 | | // overriding the deny if it is more specific. |
99 | 0 | match (self.deny.get_lpm(ip), self.allow.get_lpm(ip)) { |
100 | 0 | (Some(denied), Some(allowed)) => allowed.prefix_len() > denied.prefix_len(), |
101 | 0 | (Some(_denied), None) => false, |
102 | 0 | (None, Some(_allowed)) => true, |
103 | 0 | (None, None) => match ( |
104 | 0 | self.deny.iter().next().is_some(), |
105 | 0 | self.allow.iter().next().is_some(), |
106 | 0 | ) { |
107 | 0 | (true, _) => true, // there are deny entries, but this isn't one |
108 | 0 | (false, true) => false, // there are only allow entries, but this isn't one |
109 | 0 | (false, false) => true, // there are no entries |
110 | | }, |
111 | | } |
112 | 0 | } Unexecuted instantiation: <hickory_server::access::InnerAccessControl<ipnet::ipnet::Ipv4Net>>::allow Unexecuted instantiation: <hickory_server::access::InnerAccessControl<ipnet::ipnet::Ipv6Net>>::allow |
113 | | } |
114 | | |
115 | | #[cfg(test)] |
116 | | mod tests { |
117 | | use super::*; |
118 | | |
119 | | #[test] |
120 | | fn test_none() { |
121 | | let access = AccessControl::default(); |
122 | | assert!(access.allow("192.168.1.1".parse().unwrap())); |
123 | | assert!(access.allow("fd00::1".parse().unwrap())); |
124 | | } |
125 | | |
126 | | #[test] |
127 | | fn test_v4() { |
128 | | let mut access = AccessControl::default(); |
129 | | access.insert_allow(&["192.168.1.0/24".parse().unwrap()]); |
130 | | |
131 | | assert!(access.allow("192.168.1.1".parse().unwrap())); |
132 | | assert!(access.allow("192.168.1.255".parse().unwrap())); |
133 | | assert!(!access.allow("192.168.2.1".parse().unwrap())); |
134 | | assert!(!access.allow("192.168.0.0".parse().unwrap())); |
135 | | } |
136 | | |
137 | | #[test] |
138 | | fn test_v6() { |
139 | | let mut access = AccessControl::default(); |
140 | | access.insert_allow(&["fd00::/120".parse().unwrap()]); |
141 | | |
142 | | assert!(access.allow("fd00::1".parse().unwrap())); |
143 | | assert!(access.allow("fd00::00ff".parse().unwrap())); |
144 | | assert!(!access.allow("fd00::ffff".parse().unwrap())); |
145 | | assert!(!access.allow("fd00::1:1".parse().unwrap())); |
146 | | } |
147 | | |
148 | | #[test] |
149 | | fn test_deny_v4() { |
150 | | let mut access = AccessControl::default(); |
151 | | access.insert_deny(&["192.168.1.0/24".parse().unwrap()]); |
152 | | |
153 | | assert!(!access.allow("192.168.1.1".parse().unwrap())); |
154 | | assert!(!access.allow("192.168.1.255".parse().unwrap())); |
155 | | assert!(access.allow("192.168.2.1".parse().unwrap())); |
156 | | assert!(access.allow("192.168.0.0".parse().unwrap())); |
157 | | } |
158 | | |
159 | | #[test] |
160 | | fn test_deny_v6() { |
161 | | let mut access = AccessControl::default(); |
162 | | access.insert_deny(&["fd00::/120".parse().unwrap()]); |
163 | | |
164 | | assert!(!access.allow("fd00::1".parse().unwrap())); |
165 | | assert!(!access.allow("fd00::00ff".parse().unwrap())); |
166 | | assert!(access.allow("fd00::ffff".parse().unwrap())); |
167 | | assert!(access.allow("fd00::1:1".parse().unwrap())); |
168 | | } |
169 | | |
170 | | #[test] |
171 | | fn test_deny_allow_v4() { |
172 | | let mut access = AccessControl::default(); |
173 | | access.insert_deny(&["192.168.0.0/16".parse().unwrap()]); |
174 | | access.insert_allow(&["192.168.1.0/24".parse().unwrap()]); |
175 | | |
176 | | assert!(access.allow("192.168.1.1".parse().unwrap())); |
177 | | assert!(access.allow("192.168.1.255".parse().unwrap())); |
178 | | assert!(!access.allow("192.168.2.1".parse().unwrap())); |
179 | | assert!(!access.allow("192.168.0.0".parse().unwrap())); |
180 | | |
181 | | // but all other networks should be allowed |
182 | | assert!(access.allow("10.0.0.1".parse().unwrap())); |
183 | | } |
184 | | } |