99 lines
2.9 KiB
Rust
99 lines
2.9 KiB
Rust
use maxminddb::geoip2;
|
|
use maxminddb::Reader;
|
|
use serde::{Deserialize, Serialize};
|
|
use std::net::IpAddr;
|
|
use std::path::{Path, PathBuf};
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct GeoIpRecord {
|
|
pub ip: String,
|
|
pub country: Option<CountryInfo>,
|
|
pub asn: Option<AsnInfo>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct CountryInfo {
|
|
pub iso_code: Option<String>,
|
|
pub name: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct AsnInfo {
|
|
pub number: Option<u32>,
|
|
pub organization: Option<String>,
|
|
}
|
|
|
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
|
pub struct GeoIpStatus {
|
|
pub country_db: Option<String>,
|
|
pub asn_db: Option<String>,
|
|
pub country_loaded: bool,
|
|
pub asn_loaded: bool,
|
|
}
|
|
|
|
pub struct GeoIpService {
|
|
country_db: Option<(Reader<Vec<u8>>, PathBuf)>,
|
|
asn_db: Option<(Reader<Vec<u8>>, PathBuf)>,
|
|
}
|
|
|
|
impl GeoIpService {
|
|
pub fn new(country_path: Option<PathBuf>, asn_path: Option<PathBuf>) -> Self {
|
|
let country_db = country_path
|
|
.as_ref()
|
|
.and_then(|path| load_db(path).map(|db| (db, path.clone())));
|
|
let asn_db = asn_path
|
|
.as_ref()
|
|
.and_then(|path| load_db(path).map(|db| (db, path.clone())));
|
|
Self { country_db, asn_db }
|
|
}
|
|
|
|
pub fn status(&self) -> GeoIpStatus {
|
|
GeoIpStatus {
|
|
country_db: self.country_db.as_ref().map(|(_, path)| path.display().to_string()),
|
|
asn_db: self.asn_db.as_ref().map(|(_, path)| path.display().to_string()),
|
|
country_loaded: self.country_db.is_some(),
|
|
asn_loaded: self.asn_db.is_some(),
|
|
}
|
|
}
|
|
|
|
pub fn lookup(&self, ip: IpAddr) -> GeoIpRecord {
|
|
let country = self
|
|
.country_db
|
|
.as_ref()
|
|
.and_then(|(db, _)| lookup_country(db, ip));
|
|
let asn = self.asn_db.as_ref().and_then(|(db, _)| lookup_asn(db, ip));
|
|
GeoIpRecord {
|
|
ip: ip.to_string(),
|
|
country,
|
|
asn,
|
|
}
|
|
}
|
|
}
|
|
|
|
fn load_db(path: &Path) -> Option<Reader<Vec<u8>>> {
|
|
let bytes = std::fs::read(path).ok()?;
|
|
Reader::from_source(bytes).ok()
|
|
}
|
|
|
|
fn lookup_country(db: &Reader<Vec<u8>>, ip: IpAddr) -> Option<CountryInfo> {
|
|
let data: geoip2::Country = db.lookup(ip).ok()?;
|
|
let country = data.country?;
|
|
Some(CountryInfo {
|
|
iso_code: country.iso_code.map(|value| value.to_string()),
|
|
name: country
|
|
.names
|
|
.as_ref()
|
|
.and_then(|names| names.get("en").map(|value| value.to_string())),
|
|
})
|
|
}
|
|
|
|
fn lookup_asn(db: &Reader<Vec<u8>>, ip: IpAddr) -> Option<AsnInfo> {
|
|
let data: geoip2::Asn = db.lookup(ip).ok()?;
|
|
Some(AsnInfo {
|
|
number: data.autonomous_system_number.map(|value| value as u32),
|
|
organization: data
|
|
.autonomous_system_organization
|
|
.map(|value| value.to_string()),
|
|
})
|
|
}
|