use std::borrow::Cow;
use sha1::{Digest, Sha1};
use crate::mojang_api::client::get;
use crate::mojang_api::error::ApiError;
#[derive(Debug, Clone)]
pub struct BlockedServers {
pub hashes: Vec<String>,
}
impl BlockedServers {
pub fn fetch() -> Result<Self, ApiError> {
let res = get("https://sessionserver.mojang.com/blockedservers")?;
let txt = res.as_str()?;
let lines = txt.lines().map(String::from).collect();
Ok(BlockedServers { hashes: lines })
}
pub fn find_blocked_pattern<'a>(&self, address: &'a str) -> Option<Cow<'a, str>> {
let address_parts: Vec<&str> = address.split('.').collect();
if self.is_pattern_blocked(&address) {
return Some(Cow::Borrowed(address));
}
if is_ipv4(&address_parts) {
(1..address_parts.len())
.rev()
.map(|i| format!("{}.*", address_parts[..i].join(".")))
.find(|pattern| self.is_pattern_blocked(&pattern))
.map(Cow::Owned)
} else {
(1..address_parts.len())
.map(|i| format!("*.{}", address_parts[i..].join(".")))
.find(|pattern| self.is_pattern_blocked(&pattern))
.map(Cow::Owned)
}
}
pub fn is_blocked(&self, address: &str) -> bool {
self.find_blocked_pattern(address).is_some()
}
pub fn is_pattern_blocked(&self, pattern: &str) -> bool {
let hash = format!("{:#02X}", Sha1::digest(pattern.as_bytes())).to_lowercase();
self.hashes.contains(&Cow::Owned(hash))
}
}
#[doc(hidden)]
pub fn is_ipv4(ip: &[&str]) -> bool {
ip.len() == 4 && ip.iter().all(|x| x.parse::<u8>().is_ok())
}