mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2025-01-14 12:37:08 +01:00
Working geoip lookup
This commit is contained in:
parent
2fb2385004
commit
90705ea08a
50
src/geoip.rs
50
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<String>,
|
||||
@ -53,15 +44,16 @@ pub struct AsnResult {
|
||||
name: Option<String>,
|
||||
}
|
||||
|
||||
struct MMDBCarrier {
|
||||
mmdb: Option<maxminddb::Reader<Vec<u8>>>,
|
||||
pub struct MMDBCarrier {
|
||||
pub mmdb: Option<maxminddb::Reader<Vec<u8>>>,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
trait QueryLocation {
|
||||
pub trait QueryLocation {
|
||||
fn query_location_for_ip(&self, address: IpAddr, laguages: &Vec<String>) -> Option<LocationResult>;
|
||||
}
|
||||
|
||||
trait QueryAsn {
|
||||
pub trait QueryAsn {
|
||||
fn query_asn_for_ip(&self, address: IpAddr) -> Option<AsnResult>;
|
||||
}
|
||||
|
||||
@ -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<AsnResult> {
|
||||
match self.mmdb {
|
||||
match &self.mmdb {
|
||||
Some(mmdb) => {
|
||||
match mmdb.lookup::<geoip2::Asn>(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<String>) -> Option<LocationResult> {
|
||||
match self.mmdb {
|
||||
match &self.mmdb {
|
||||
Some(mmdb) => {
|
||||
match mmdb.lookup::<geoip2::City>(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)
|
||||
},
|
||||
}
|
||||
}
|
||||
}
|
||||
|
85
src/main.rs
85
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<String>,
|
||||
asn: Option<AsnResult>,
|
||||
location: Option<LocationResult>,
|
||||
}
|
||||
|
||||
struct ServiceSharedState {
|
||||
templating_engine: templating_engine::Engine,
|
||||
dns_resolver: TokioAsyncResolver,
|
||||
mmdb_asn: Option<maxminddb::Reader<Vec<u8>>>,
|
||||
mmdb_location: Option<maxminddb::Reader<Vec<u8>>>,
|
||||
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::<geoip2::Asn>(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::<geoip2::City>(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(
|
||||
|
7
templates/helpers.html
Normal file
7
templates/helpers.html
Normal file
@ -0,0 +1,7 @@
|
||||
{% macro place_dl(place, label="", iso_code_prefix="") %}
|
||||
{% if place %}
|
||||
{% if label %}<dh>{{label}}</dh>{% endif %}
|
||||
<dd>{{place.name}} {% if place.iso_code%}({% if iso_code_prefix %}{{iso_code_prefix}}-{% endif %}{{place.iso_code}}){% endif %}</dd>
|
||||
{% endif %}
|
||||
{% endmacro place_dl %}
|
||||
|
@ -1,16 +1,56 @@
|
||||
{% import "helpers.html" as helper %}
|
||||
<!DOCTYPE html>
|
||||
<html>
|
||||
<head>
|
||||
<title>Your IP: {{ data.query.ip }}</title>
|
||||
</head>
|
||||
<body>
|
||||
{% set r = data.result %}
|
||||
<h1>Your IP-Address is: {{ data.query.ip }}</h1>
|
||||
<p>Your requested format was: <b>{{format}}</b></p>
|
||||
{% if hostname %}
|
||||
<p>Hostname: <b>{{data.result.hostname}}</b></p>
|
||||
{% endif %}
|
||||
{% if data.result.asn %}
|
||||
<p>This IP is part of <b>AS{{ data.result.asn.asn }}</b>, which belongs to: <i>{{ data.result.asn.name }}</i></p>
|
||||
<section>
|
||||
<h2>Network Information</h2>
|
||||
<dl>
|
||||
{% if r.hostname %}
|
||||
<dh>Hostname</dh>
|
||||
<dd><a href="/dig?name={{r.hostname}}">{{r.hostname}}</a></dd>
|
||||
{% endif %}
|
||||
{% if r.asn %}
|
||||
<dh><abbr="Autonomous System Number">ASN</abbr></dh>
|
||||
<dd>AS{{r.asn.asn}}</dd>
|
||||
<dh>AS Name</dh>
|
||||
<dd>{{ r.asn.name }}</dd>
|
||||
</dl>
|
||||
{% endif %}
|
||||
</section>
|
||||
{% if r.location %}
|
||||
<section>
|
||||
<h2>Geolocation</h2>
|
||||
<dl>
|
||||
{{ 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 %}
|
||||
<dh>Postal Code</dh>
|
||||
<dd>{{r.location.postal_code}}</dd>
|
||||
{% endif %}
|
||||
{% if r.location.time_zone %}
|
||||
<dh>Timezone</dh>
|
||||
<dd>{{r.location.time_zone}}</dd>
|
||||
{% endif %}
|
||||
</dl>
|
||||
<!--We have to put that there to comply with maxminds licensing-->
|
||||
<p><small>
|
||||
The GeopIP and ASN information is provided by the GeoLite2 database created by
|
||||
<a target="_blank" href="https://www.maxmind.com">MaxMind</a>.
|
||||
</small></p>
|
||||
</section>
|
||||
{% endif %}
|
||||
</body>
|
||||
</html>
|
||||
|
Loading…
Reference in New Issue
Block a user