86 lines
2.3 KiB
Rust
86 lines
2.3 KiB
Rust
use itertools::Itertools;
|
|
use serde::Serialize;
|
|
use std::{net::Ipv4Addr, sync::Arc};
|
|
|
|
use warp::{http::StatusCode, reply, Filter};
|
|
|
|
use serde_json::json;
|
|
|
|
const IP2ASN_DATA: &str = include_str!("../data/ip2asn-v4.tsv");
|
|
|
|
#[derive(Serialize)]
|
|
struct RangeRecord {
|
|
#[serde(skip)]
|
|
ip_start: Ipv4Addr,
|
|
#[serde(skip)]
|
|
ip_end: Ipv4Addr,
|
|
asn: u64,
|
|
country_code: &'static str,
|
|
asn_description: &'static str,
|
|
}
|
|
|
|
fn parse_ranges() -> Vec<RangeRecord> {
|
|
let mut ranges = Vec::with_capacity(IP2ASN_DATA.lines().count());
|
|
|
|
IP2ASN_DATA
|
|
.lines()
|
|
.filter(|s| !s.is_empty())
|
|
.map(|l| {
|
|
l.splitn(5, '\t')
|
|
.next_tuple::<(&str, &str, &str, &str, &str)>()
|
|
.unwrap()
|
|
})
|
|
.for_each(|(ip_start, ip_end, asn, country_code, asn_description)| {
|
|
ranges.push(RangeRecord {
|
|
ip_start: ip_start.parse().unwrap(),
|
|
ip_end: ip_end.parse().unwrap(),
|
|
asn: asn.parse().unwrap(),
|
|
country_code,
|
|
asn_description,
|
|
})
|
|
});
|
|
|
|
ranges
|
|
}
|
|
|
|
type RangeData = Arc<Vec<RangeRecord>>;
|
|
fn with_ranges(
|
|
ranges: RangeData,
|
|
) -> impl Filter<Extract = (RangeData,), Error = std::convert::Infallible> + Clone {
|
|
warp::any().map(move || ranges.clone())
|
|
}
|
|
|
|
fn get_range_data(ip: String, ranges: RangeData) -> impl warp::Reply {
|
|
if let Ok(ipv4) = ip.parse::<Ipv4Addr>() {
|
|
for range in ranges.iter() {
|
|
if (range.ip_start..=range.ip_end).contains(&ipv4) {
|
|
return reply::with_status(reply::json(&range), StatusCode::OK);
|
|
}
|
|
}
|
|
|
|
reply::with_status(
|
|
reply::json(&json!({ "error": "range not found" })),
|
|
StatusCode::NOT_FOUND,
|
|
)
|
|
} else {
|
|
reply::with_status(
|
|
reply::json(&json!({ "error": "invalid ipv4 address" })),
|
|
StatusCode::BAD_REQUEST,
|
|
)
|
|
}
|
|
}
|
|
|
|
#[tokio::main]
|
|
async fn main() {
|
|
eprintln!("[i] Parsing IP ASN data...");
|
|
let ranges: RangeData = Arc::new(parse_ranges());
|
|
eprintln!("[i] Done!");
|
|
|
|
let get_ip_data = warp::path!(String)
|
|
.and(with_ranges(ranges.clone()))
|
|
.map(get_range_data);
|
|
|
|
println!("\nListening on http://127.0.0.1:8000 ...");
|
|
warp::serve(get_ip_data).bind(([127, 0, 0, 1], 8000)).await;
|
|
}
|