use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use url::Url;
pub struct IpBlocker {
block_private: bool,
block_loopback: bool,
block_link_local: bool,
block_multicast: bool,
}
impl Default for IpBlocker {
fn default() -> Self {
Self {
block_private: true,
block_loopback: true,
block_link_local: true,
block_multicast: true,
}
}
}
impl IpBlocker {
pub fn new() -> Self {
Self::default()
}
pub fn configure(
block_private: bool,
block_loopback: bool,
block_link_local: bool,
block_multicast: bool,
) -> Self {
Self {
block_private,
block_loopback,
block_link_local,
block_multicast,
}
}
pub fn is_blocked(&self, ip: &IpAddr) -> bool {
match ip {
IpAddr::V4(ipv4) => self.is_blocked_ipv4(ipv4),
IpAddr::V6(ipv6) => self.is_blocked_ipv6(ipv6),
}
}
fn is_blocked_ipv4(&self, ip: &Ipv4Addr) -> bool {
if self.block_loopback && ip.is_loopback() {
return true;
}
if self.block_private && ip.is_private() {
return true;
}
if self.block_link_local && ip.is_link_local() {
return true;
}
if self.block_multicast && ip.is_multicast() {
return true;
}
if ip.is_broadcast() {
return true;
}
if ip.is_unspecified() {
return true;
}
if ip.is_documentation() {
return true;
}
false
}
fn is_blocked_ipv6(&self, ip: &Ipv6Addr) -> bool {
if self.block_loopback && ip.is_loopback() {
return true;
}
if self.block_link_local {
let segments = ip.segments();
if (segments[0] & 0xffc0) == 0xfe80 {
return true;
}
}
if self.block_multicast && ip.is_multicast() {
return true;
}
if ip.is_unspecified() {
return true;
}
if self.block_private {
let segments = ip.segments();
if (segments[0] & 0xfe00) == 0xfc00 {
return true;
}
}
false
}
pub fn is_url_hostname_blocked(&self, url: &Url) -> bool {
if let Some(host) = url.host_str() {
if let Ok(ip) = host.parse::<IpAddr>() {
return self.is_blocked(&ip);
}
let host_lower = host.to_lowercase();
if host_lower == "localhost"
|| host_lower == "localhost.localdomain"
|| host_lower.ends_with(".local")
|| host_lower.ends_with(".localhost")
{
return self.block_loopback;
}
}
false
}
pub fn block_reason(&self, ip: &IpAddr) -> Option<String> {
match ip {
IpAddr::V4(ipv4) => {
if self.block_loopback && ipv4.is_loopback() {
return Some("loopback address".to_string());
}
if self.block_private && ipv4.is_private() {
return Some("private address".to_string());
}
if self.block_link_local && ipv4.is_link_local() {
return Some("link-local address".to_string());
}
if self.block_multicast && ipv4.is_multicast() {
return Some("multicast address".to_string());
}
if ipv4.is_broadcast() {
return Some("broadcast address".to_string());
}
if ipv4.is_unspecified() {
return Some("unspecified address".to_string());
}
}
IpAddr::V6(ipv6) => {
if self.block_loopback && ipv6.is_loopback() {
return Some("loopback address".to_string());
}
if self.block_multicast && ipv6.is_multicast() {
return Some("multicast address".to_string());
}
if ipv6.is_unspecified() {
return Some("unspecified address".to_string());
}
}
}
None
}
}