halldyll_core/security/
ipblock.rs1use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
4use url::Url;
5
6pub struct IpBlocker {
8 block_private: bool,
10 block_loopback: bool,
12 block_link_local: bool,
14 block_multicast: bool,
16}
17
18impl Default for IpBlocker {
19 fn default() -> Self {
20 Self {
21 block_private: true,
22 block_loopback: true,
23 block_link_local: true,
24 block_multicast: true,
25 }
26 }
27}
28
29impl IpBlocker {
30 pub fn new() -> Self {
32 Self::default()
33 }
34
35 pub fn configure(
37 block_private: bool,
38 block_loopback: bool,
39 block_link_local: bool,
40 block_multicast: bool,
41 ) -> Self {
42 Self {
43 block_private,
44 block_loopback,
45 block_link_local,
46 block_multicast,
47 }
48 }
49
50 pub fn is_blocked(&self, ip: &IpAddr) -> bool {
52 match ip {
53 IpAddr::V4(ipv4) => self.is_blocked_ipv4(ipv4),
54 IpAddr::V6(ipv6) => self.is_blocked_ipv6(ipv6),
55 }
56 }
57
58 fn is_blocked_ipv4(&self, ip: &Ipv4Addr) -> bool {
60 if self.block_loopback && ip.is_loopback() {
62 return true;
63 }
64
65 if self.block_private && ip.is_private() {
67 return true;
68 }
69
70 if self.block_link_local && ip.is_link_local() {
72 return true;
73 }
74
75 if self.block_multicast && ip.is_multicast() {
77 return true;
78 }
79
80 if ip.is_broadcast() {
82 return true;
83 }
84
85 if ip.is_unspecified() {
87 return true;
88 }
89
90 if ip.is_documentation() {
92 return true;
93 }
94
95 false
96 }
97
98 fn is_blocked_ipv6(&self, ip: &Ipv6Addr) -> bool {
100 if self.block_loopback && ip.is_loopback() {
102 return true;
103 }
104
105 if self.block_link_local {
108 let segments = ip.segments();
109 if (segments[0] & 0xffc0) == 0xfe80 {
110 return true;
111 }
112 }
113
114 if self.block_multicast && ip.is_multicast() {
116 return true;
117 }
118
119 if ip.is_unspecified() {
121 return true;
122 }
123
124 if self.block_private {
126 let segments = ip.segments();
127 if (segments[0] & 0xfe00) == 0xfc00 {
128 return true;
129 }
130 }
131
132 false
133 }
134
135 pub fn is_url_hostname_blocked(&self, url: &Url) -> bool {
137 if let Some(host) = url.host_str() {
139 if let Ok(ip) = host.parse::<IpAddr>() {
141 return self.is_blocked(&ip);
142 }
143
144 let host_lower = host.to_lowercase();
146 if host_lower == "localhost"
147 || host_lower == "localhost.localdomain"
148 || host_lower.ends_with(".local")
149 || host_lower.ends_with(".localhost")
150 {
151 return self.block_loopback;
152 }
153 }
154
155 false
156 }
157
158 pub fn block_reason(&self, ip: &IpAddr) -> Option<String> {
160 match ip {
161 IpAddr::V4(ipv4) => {
162 if self.block_loopback && ipv4.is_loopback() {
163 return Some("loopback address".to_string());
164 }
165 if self.block_private && ipv4.is_private() {
166 return Some("private address".to_string());
167 }
168 if self.block_link_local && ipv4.is_link_local() {
169 return Some("link-local address".to_string());
170 }
171 if self.block_multicast && ipv4.is_multicast() {
172 return Some("multicast address".to_string());
173 }
174 if ipv4.is_broadcast() {
175 return Some("broadcast address".to_string());
176 }
177 if ipv4.is_unspecified() {
178 return Some("unspecified address".to_string());
179 }
180 }
181 IpAddr::V6(ipv6) => {
182 if self.block_loopback && ipv6.is_loopback() {
183 return Some("loopback address".to_string());
184 }
185 if self.block_multicast && ipv6.is_multicast() {
186 return Some("multicast address".to_string());
187 }
188 if ipv6.is_unspecified() {
189 return Some("unspecified address".to_string());
190 }
191 }
192 }
193 None
194 }
195}