diff --git a/src/geoip.rs b/src/geoip.rs index 9e21786..6d284b9 100644 --- a/src/geoip.rs +++ b/src/geoip.rs @@ -7,20 +7,11 @@ use maxminddb; use maxminddb::geoip2; use std::collections::BTreeMap; - use std::net::IpAddr; - +use std::path::Path; /* Datatypes */ -// TODO -pub enum IsTheNameOf { - Continent, - Country, - Subdivision, - City, -} - #[derive(serde::Deserialize, serde::Serialize, Default)] pub struct NamedLocation { iso_code: Option, @@ -53,15 +44,16 @@ pub struct AsnResult { name: Option, } -struct MMDBCarrier { - mmdb: Option>>, +pub struct MMDBCarrier { + pub mmdb: Option>>, + pub name: String, } -trait QueryLocation { +pub trait QueryLocation { fn query_location_for_ip(&self, address: IpAddr, laguages: &Vec) -> Option; } -trait QueryAsn { +pub trait QueryAsn { fn query_asn_for_ip(&self, address: IpAddr) -> Option; } @@ -128,7 +120,7 @@ pub fn geoip2_subdivision_to_named_location(item: geoip2::model::Subdivision, la impl QueryAsn for MMDBCarrier { fn query_asn_for_ip(&self, address: IpAddr) -> Option { - match self.mmdb { + match &self.mmdb { Some(mmdb) => { match mmdb.lookup::(address) { Ok(res) => { @@ -150,7 +142,7 @@ impl QueryAsn for MMDBCarrier { impl QueryLocation for MMDBCarrier { fn query_location_for_ip(&self, address: IpAddr, languages: &Vec) -> Option { - match self.mmdb { + match &self.mmdb { Some(mmdb) => { match mmdb.lookup::(address) { Ok(res) => { @@ -168,7 +160,7 @@ impl QueryLocation for MMDBCarrier { subdivisions: match res.subdivisions { Some(sds) => { - let subdivisions = Vec::new(); + let mut subdivisions = Vec::new(); subdivisions.reserve_exact(sds.len()); for sd in sds { subdivisions.push(geoip2_subdivision_to_named_location(sd, languages)); @@ -198,3 +190,27 @@ impl QueryLocation for MMDBCarrier { } } +impl MMDBCarrier { + pub fn load_database_from_path(&mut self, path: &Path) -> Result<(),maxminddb::MaxMindDBError> { + println!("Loading {} from '{}' ...", &self.name, path.display()); + match maxminddb::Reader::open_readfile(path) { + Ok(reader) => { + let wording = if self.mmdb.is_some() { + "Replaced old" + } else { + "Loaded new" + }; + self.mmdb = Some(reader); + println!("{} {} with new one.", wording, &self.name); + Ok(()) + }, + Err(e) => { + println!("Error while reading {}: {}", &self.name, &e); + if self.mmdb.is_some() { + println!("Not replacing old database."); + } + Err(e) + }, + } + } +} diff --git a/src/main.rs b/src/main.rs index e2551af..a21d7b5 100644 --- a/src/main.rs +++ b/src/main.rs @@ -5,8 +5,7 @@ use axum::{ Router, routing::get, }; -use maxminddb; -use maxminddb::geoip2; + use tera::Tera; use trust_dns_resolver::{ TokioAsyncResolver, @@ -16,12 +15,14 @@ use trust_dns_resolver::{ use std::net::{IpAddr, Ipv4Addr}; use std::sync::Arc; - +use std::path::Path; mod simple_dns; mod templating_engine; mod geoip; +use crate::geoip::QueryAsn; +use crate::geoip::QueryLocation; use geoip::AsnResult; use geoip::LocationResult; @@ -44,15 +45,17 @@ pub struct DigQuery { pub struct IpResult { hostname: Option, asn: Option, + location: Option, } struct ServiceSharedState { templating_engine: templating_engine::Engine, dns_resolver: TokioAsyncResolver, - mmdb_asn: Option>>, - mmdb_location: Option>>, + asn_db: geoip::MMDBCarrier, + location_db: geoip::MMDBCarrier, } + #[tokio::main] async fn main() { // Initalize Tera templates @@ -69,20 +72,17 @@ async fn main() { // Initalize GeoIP Database - println!("Opening GeoIP ASN Databse ..."); - let mmdb_asn = maxminddb::Reader::open_readfile("mmdb/GeoLite2-ASN.mmdb"); - match mmdb_asn { - Ok(_) => { /* NOP */ }, - Err(ref e) => println!("Error while opening GeoIP ASN Databse: {e}"), - } - - println!("Opening GeoIP Location Databse ..."); - let mmdb_location = maxminddb::Reader::open_readfile("mmdb/GeoLite2-City.mmdb"); - match mmdb_location { - Ok(_) => { /* NOP */ }, - Err(ref e) => println!("Error while opening GeoIP Location Databse: {e}"), - } + let mut asn_db = geoip::MMDBCarrier { + mmdb: None, + name: "GeoIP ASN Database".to_string(), + }; + asn_db.load_database_from_path(Path::new("mmdb/GeoLite2-ASN.mmdb")).ok(); + let mut location_db = geoip::MMDBCarrier { + mmdb: None, + name: "GeoIP Location Database".to_string(), + }; + location_db.load_database_from_path(Path::new("mmdb/GeoLite2-City.mmdb")).ok(); // Initalize DNS resolver with os defaults println!("Initalizing dns resolver ..."); @@ -96,13 +96,14 @@ async fn main() { }; // Initialize shared state - let shared_state = Arc::new(ServiceSharedState{ + let shared_state = Arc::new( + ServiceSharedState{ templating_engine: templating_engine::Engine{ tera: tera, }, dns_resolver: dns_resolver, - mmdb_asn: mmdb_asn.ok(), - mmdb_location: mmdb_location.ok(), + asn_db: asn_db, + location_db: location_db, }); // Initalize axum server @@ -144,47 +145,17 @@ async fn handle_default_route( // do reverse lookup let hostname = simple_dns::reverse_lookup(&state.dns_resolver, &address); - - let asn_result = match &state.mmdb_asn { - Some(mmdb_asn) => { - match mmdb_asn.lookup::(address) { - Ok(res) => { - Some(AsnResult { - asn: res.autonomous_system_number, - name: res.autonomous_system_organization.map(ToString::to_string), - }) - } - Err(e) => { - println!("Error while looking up ASN for {address}: {e}"); - Default::default() - } - } - }, - None => None, - }; - let location_result = match &state.mmdb_location { - Some(mmdb_location) => { - match mmdb_location.lookup::(address) { - Ok(res) => { - Some(AsnResult { - asn: res.autonomous_system_number, - name: res.autonomous_system_organization.map(ToString::to_string), - }) - } - Err(e) => { - println!("Error while looking up ASN for {address}: {e}"); - Default::default() - } - } - }, - None => None, - }; - + // asn lookup + let asn_result = state.asn_db.query_asn_for_ip(address); + + // location lookup + let location_result = state.location_db.query_location_for_ip(address, &vec!["en".to_string()]); let result = IpResult{ hostname: hostname.await, asn: asn_result, + location: location_result, }; state.templating_engine.render_view( diff --git a/templates/helpers.html b/templates/helpers.html new file mode 100644 index 0000000..d568d7b --- /dev/null +++ b/templates/helpers.html @@ -0,0 +1,7 @@ +{% macro place_dl(place, label="", iso_code_prefix="") %} + {% if place %} + {% if label %}{{label}}{% endif %} +
{{place.name}} {% if place.iso_code%}({% if iso_code_prefix %}{{iso_code_prefix}}-{% endif %}{{place.iso_code}}){% endif %}
+ {% endif %} +{% endmacro place_dl %} + diff --git a/templates/index.html b/templates/index.html index 8eb07dd..3e793e2 100644 --- a/templates/index.html +++ b/templates/index.html @@ -1,16 +1,56 @@ +{% import "helpers.html" as helper %} Your IP: {{ data.query.ip }} + {% set r = data.result %}

Your IP-Address is: {{ data.query.ip }}

-

Your requested format was: {{format}}

- {% if hostname %} -

Hostname: {{data.result.hostname}}

- {% endif %} - {% if data.result.asn %} -

This IP is part of AS{{ data.result.asn.asn }}, which belongs to: {{ data.result.asn.name }}

+
+

Network Information

+
+ {% if r.hostname %} + Hostname +
{{r.hostname}}
+ {% endif %} + {% if r.asn %} + ASN +
AS{{r.asn.asn}}
+ AS Name +
{{ r.asn.name }}
+
+ {% endif %} +
+ {% if r.location %} +
+

Geolocation

+
+ {{ helper::place_dl(place=r.location.continent, label="Continent") }} + {{ helper::place_dl(place=r.location.country, label="Country") }} + {{ helper::place_dl(place=r.location.registered_country, label="Registred in") }} + {{ helper::place_dl(place=r.location.represented_country, label="Represents") }} + {% if r.location.subdivisions %} + {% for sd in r.location.subdivisions %} + {{ helper::place_dl(place=sd, label="Subdivision", iso_code_prefix=r.location.country.iso_code|default(value="")) }} + {% endfor %} + {% endif %} + {{ helper::place_dl(place=r.location.city, label="City") }} + {% if r.location.postal_code %} + Postal Code +
{{r.location.postal_code}}
+ {% endif %} + {% if r.location.time_zone %} + Timezone +
{{r.location.time_zone}}
+ {% endif %} +
+ +

+ The GeopIP and ASN information is provided by the GeoLite2 database created by + MaxMind. +

+
{% endif %}