Parse ip2asn data

main
Charlotte Som 2021-12-30 18:01:06 +00:00
parent 33674eec78
commit 9656d070b6
6 changed files with 99 additions and 0 deletions

12
.editorconfig Normal file
View File

@ -0,0 +1,12 @@
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = false
insert_final_newline = true
[*.rs]
indent_size = 4

16
Cargo.lock generated
View File

@ -75,6 +75,12 @@ dependencies = [
"generic-array", "generic-array",
] ]
[[package]]
name = "either"
version = "1.6.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457"
[[package]] [[package]]
name = "fnv" name = "fnv"
version = "1.0.7" version = "1.0.7"
@ -147,6 +153,7 @@ dependencies = [
name = "geoip-api" name = "geoip-api"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"itertools",
"tokio", "tokio",
"warp", "warp",
] ]
@ -309,6 +316,15 @@ dependencies = [
"cfg-if", "cfg-if",
] ]
[[package]]
name = "itertools"
version = "0.10.3"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3"
dependencies = [
"either",
]
[[package]] [[package]]
name = "itoa" name = "itoa"
version = "0.4.8" version = "0.4.8"

View File

@ -5,5 +5,6 @@ edition = "2021"
authors = ["charlotte ✨ <charlotte@lavender.software>"] authors = ["charlotte ✨ <charlotte@lavender.software>"]
[dependencies] [dependencies]
itertools = "0.10.3"
tokio = { version = "1.15.0", features = ["full"] } tokio = { version = "1.15.0", features = ["full"] }
warp = "0.3.2" warp = "0.3.2"

25
README.md Normal file
View File

@ -0,0 +1,25 @@
# geoip-api
HTTP API for converting an IPv4 address to a country code.
Uses the [`iptoasn.com`](https://iptoasn.com) dataset.
## Setup
Grab the latest `ip2asn-v4.tsv.gz` and extract it to `data/ip2asn-v4.tsv`.
Then just use `cargo` as usual.
## Usage
```shell
$ http GET 'http://127.0.0.1:8000/192.168.0.1'
```
```json
{
"asn": 0,
"country_code": "None",
"asn_desc": "Not routed"
}
```

2
data/.gitignore vendored Normal file
View File

@ -0,0 +1,2 @@
*
!.gitignore

View File

@ -1,7 +1,50 @@
use itertools::Itertools;
use std::{net::Ipv4Addr, sync::Arc};
use warp::Filter; use warp::Filter;
const IP2ASN_DATA: &str = include_str!("../data/ip2asn-v4.tsv");
#[derive(Debug)]
struct RangeRecord {
ip_start: Ipv4Addr,
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
}
#[tokio::main] #[tokio::main]
async fn main() { async fn main() {
eprintln!("[i] Parsing IP ASN data...");
let _ranges = Arc::new(parse_ranges());
eprintln!("[i] Done!");
let hello = warp::any().map(|| "Hello!".to_string()); let hello = warp::any().map(|| "Hello!".to_string());
println!("\nListening on http://127.0.0.1:8000 ...");
warp::serve(hello).bind(([127, 0, 0, 1], 8000)).await; warp::serve(hello).bind(([127, 0, 0, 1], 8000)).await;
} }