From 9656d070b6bebb3a673bd7080ba25451927cd2c0 Mon Sep 17 00:00:00 2001 From: videogame hacker Date: Thu, 30 Dec 2021 18:01:06 +0000 Subject: [PATCH] Parse ip2asn data --- .editorconfig | 12 ++++++++++++ Cargo.lock | 16 ++++++++++++++++ Cargo.toml | 1 + README.md | 25 +++++++++++++++++++++++++ data/.gitignore | 2 ++ src/main.rs | 43 +++++++++++++++++++++++++++++++++++++++++++ 6 files changed, 99 insertions(+) create mode 100644 .editorconfig create mode 100644 README.md create mode 100644 data/.gitignore diff --git a/.editorconfig b/.editorconfig new file mode 100644 index 0000000..bcb5235 --- /dev/null +++ b/.editorconfig @@ -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 diff --git a/Cargo.lock b/Cargo.lock index 8a8b27d..f3d597e 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -75,6 +75,12 @@ dependencies = [ "generic-array", ] +[[package]] +name = "either" +version = "1.6.1" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "e78d4f1cc4ae33bbfc157ed5d5a5ef3bc29227303d595861deb238fcec4e9457" + [[package]] name = "fnv" version = "1.0.7" @@ -147,6 +153,7 @@ dependencies = [ name = "geoip-api" version = "0.1.0" dependencies = [ + "itertools", "tokio", "warp", ] @@ -309,6 +316,15 @@ dependencies = [ "cfg-if", ] +[[package]] +name = "itertools" +version = "0.10.3" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a9a9d19fa1e79b6215ff29b9d6880b706147f16e9b1dbb1e4e5947b5b02bc5e3" +dependencies = [ + "either", +] + [[package]] name = "itoa" version = "0.4.8" diff --git a/Cargo.toml b/Cargo.toml index 9edbe8f..ccc1d22 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -5,5 +5,6 @@ edition = "2021" authors = ["charlotte ✨ "] [dependencies] +itertools = "0.10.3" tokio = { version = "1.15.0", features = ["full"] } warp = "0.3.2" diff --git a/README.md b/README.md new file mode 100644 index 0000000..a858bc6 --- /dev/null +++ b/README.md @@ -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" +} +``` diff --git a/data/.gitignore b/data/.gitignore new file mode 100644 index 0000000..d6b7ef3 --- /dev/null +++ b/data/.gitignore @@ -0,0 +1,2 @@ +* +!.gitignore diff --git a/src/main.rs b/src/main.rs index 781f65a..df0b923 100644 --- a/src/main.rs +++ b/src/main.rs @@ -1,7 +1,50 @@ +use itertools::Itertools; +use std::{net::Ipv4Addr, sync::Arc}; + 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 { + 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] 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()); + println!("\nListening on http://127.0.0.1:8000 ..."); warp::serve(hello).bind(([127, 0, 0, 1], 8000)).await; }