mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2025-01-14 20:47:09 +01:00
Made querying with paths possible (for wasier commandline usage)
This commit is contained in:
parent
90705ea08a
commit
2394d90087
57
src/config.rs
Normal file
57
src/config.rs
Normal file
@ -0,0 +1,57 @@
|
|||||||
|
use std::path::Path;
|
||||||
|
use axum_client_ip::SecureClientIpSource;
|
||||||
|
use std::net::SocketAddr;
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct EchoIpServiceConfig {
|
||||||
|
server: ServerConfig,
|
||||||
|
dns: DnsConfig,
|
||||||
|
geoip: GeoIpConfig,
|
||||||
|
template: TemplateConfig,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct ServerConfig {
|
||||||
|
listen_on: SocketAddr,
|
||||||
|
ip_header: SecureClientIpSource,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct DnsConfig {
|
||||||
|
allow_forward_lookup: bool,
|
||||||
|
allow_reverse_lookup: bool,
|
||||||
|
hide_private_range_ips: bool,
|
||||||
|
hidden_suffixes: Vec<String>,
|
||||||
|
//Future Idea: allow custom resolver
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct GeoIpConfig {
|
||||||
|
asn_database: Option<String>,
|
||||||
|
location_database: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize)]
|
||||||
|
pub struct TemplateConfig {
|
||||||
|
template_location: String,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for ServerConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
ServerConfig {
|
||||||
|
listen_on: "127.0.0.1:3000",
|
||||||
|
ip_header: SecureClientIpSource::RightmostXForwardedFor,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for DnsConfig {
|
||||||
|
fn default() -> Self {
|
||||||
|
DnsConfig {
|
||||||
|
allow_forward_lookup: true;
|
||||||
|
allow_reverse_lookup: false;
|
||||||
|
hide_private_range_ips: true;
|
||||||
|
hidden_suffixes: Vec::new();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
16
src/geoip.rs
16
src/geoip.rs
@ -50,7 +50,7 @@ pub struct MMDBCarrier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
pub trait QueryLocation {
|
pub trait QueryLocation {
|
||||||
fn query_location_for_ip(&self, address: IpAddr, laguages: &Vec<String>) -> Option<LocationResult>;
|
fn query_location_for_ip(&self, address: IpAddr, laguages: &Vec<&String>) -> Option<LocationResult>;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub trait QueryAsn {
|
pub trait QueryAsn {
|
||||||
@ -61,7 +61,7 @@ pub trait QueryAsn {
|
|||||||
|
|
||||||
pub fn extract_localized_name(
|
pub fn extract_localized_name(
|
||||||
names: &Option<BTreeMap<&str, &str>>,
|
names: &Option<BTreeMap<&str, &str>>,
|
||||||
languages: &Vec<String>)
|
languages: &Vec<&String>)
|
||||||
-> Option<String> {
|
-> Option<String> {
|
||||||
match names {
|
match names {
|
||||||
Some(names) => {
|
Some(names) => {
|
||||||
@ -76,7 +76,7 @@ names: &Option<BTreeMap<&str, &str>>,
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn geoip2_city_to_named_location(item: geoip2::model::City, languages: &Vec<String>) -> NamedLocation {
|
pub fn geoip2_city_to_named_location(item: geoip2::model::City, languages: &Vec<&String>) -> NamedLocation {
|
||||||
NamedLocation {
|
NamedLocation {
|
||||||
iso_code: None,
|
iso_code: None,
|
||||||
geoname_id: item.geoname_id,
|
geoname_id: item.geoname_id,
|
||||||
@ -84,7 +84,7 @@ pub fn geoip2_city_to_named_location(item: geoip2::model::City, languages: &Vec<
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn geoip2_continent_to_named_location(item: geoip2::model::Continent, languages: &Vec<String>) -> NamedLocation {
|
pub fn geoip2_continent_to_named_location(item: geoip2::model::Continent, languages: &Vec<&String>) -> NamedLocation {
|
||||||
NamedLocation {
|
NamedLocation {
|
||||||
iso_code: item.code.map(ToString::to_string),
|
iso_code: item.code.map(ToString::to_string),
|
||||||
geoname_id: item.geoname_id,
|
geoname_id: item.geoname_id,
|
||||||
@ -92,7 +92,7 @@ pub fn geoip2_continent_to_named_location(item: geoip2::model::Continent, langua
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn geoip2_country_to_named_location(item: geoip2::model::Country, languages: &Vec<String>) -> NamedLocation {
|
pub fn geoip2_country_to_named_location(item: geoip2::model::Country, languages: &Vec<&String>) -> NamedLocation {
|
||||||
NamedLocation {
|
NamedLocation {
|
||||||
iso_code: item.iso_code.map(ToString::to_string),
|
iso_code: item.iso_code.map(ToString::to_string),
|
||||||
geoname_id: item.geoname_id,
|
geoname_id: item.geoname_id,
|
||||||
@ -100,7 +100,7 @@ pub fn geoip2_country_to_named_location(item: geoip2::model::Country, languages:
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn geoip2_represented_country_to_named_location(item: geoip2::model::RepresentedCountry, languages: &Vec<String>) -> NamedLocation {
|
pub fn geoip2_represented_country_to_named_location(item: geoip2::model::RepresentedCountry, languages: &Vec<&String>) -> NamedLocation {
|
||||||
NamedLocation {
|
NamedLocation {
|
||||||
iso_code: item.iso_code.map(ToString::to_string),
|
iso_code: item.iso_code.map(ToString::to_string),
|
||||||
geoname_id: item.geoname_id,
|
geoname_id: item.geoname_id,
|
||||||
@ -108,7 +108,7 @@ pub fn geoip2_represented_country_to_named_location(item: geoip2::model::Represe
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn geoip2_subdivision_to_named_location(item: geoip2::model::Subdivision, languages: &Vec<String>) -> NamedLocation {
|
pub fn geoip2_subdivision_to_named_location(item: geoip2::model::Subdivision, languages: &Vec<&String>) -> NamedLocation {
|
||||||
NamedLocation {
|
NamedLocation {
|
||||||
iso_code: item.iso_code.map(ToString::to_string),
|
iso_code: item.iso_code.map(ToString::to_string),
|
||||||
geoname_id: item.geoname_id,
|
geoname_id: item.geoname_id,
|
||||||
@ -141,7 +141,7 @@ impl QueryAsn for MMDBCarrier {
|
|||||||
}
|
}
|
||||||
|
|
||||||
impl QueryLocation for MMDBCarrier {
|
impl QueryLocation for MMDBCarrier {
|
||||||
fn query_location_for_ip(&self, address: IpAddr, languages: &Vec<String>) -> Option<LocationResult> {
|
fn query_location_for_ip(&self, address: IpAddr, languages: &Vec<&String>) -> Option<LocationResult> {
|
||||||
match &self.mmdb {
|
match &self.mmdb {
|
||||||
Some(mmdb) => {
|
Some(mmdb) => {
|
||||||
match mmdb.lookup::<geoip2::City>(address) {
|
match mmdb.lookup::<geoip2::City>(address) {
|
||||||
|
115
src/main.rs
115
src/main.rs
@ -1,10 +1,12 @@
|
|||||||
use axum::{
|
use axum::{
|
||||||
extract::Query,
|
extract::Query,
|
||||||
extract::State,
|
extract::State,
|
||||||
|
extract,
|
||||||
response::Response,
|
response::Response,
|
||||||
Router,
|
Router,
|
||||||
routing::get,
|
routing::get,
|
||||||
};
|
};
|
||||||
|
use axum_client_ip::{SecureClientIp, SecureClientIpSource};
|
||||||
|
|
||||||
use tera::Tera;
|
use tera::Tera;
|
||||||
use trust_dns_resolver::{
|
use trust_dns_resolver::{
|
||||||
@ -13,13 +15,14 @@ use trust_dns_resolver::{
|
|||||||
config::ResolverConfig,
|
config::ResolverConfig,
|
||||||
};
|
};
|
||||||
|
|
||||||
use std::net::{IpAddr, Ipv4Addr};
|
use std::net::IpAddr;
|
||||||
use std::sync::Arc;
|
use std::sync::Arc;
|
||||||
use std::path::Path;
|
use std::path::Path;
|
||||||
|
|
||||||
mod simple_dns;
|
mod simple_dns;
|
||||||
mod templating_engine;
|
mod templating_engine;
|
||||||
mod geoip;
|
mod geoip;
|
||||||
|
mod config;
|
||||||
|
|
||||||
use crate::geoip::QueryAsn;
|
use crate::geoip::QueryAsn;
|
||||||
use crate::geoip::QueryLocation;
|
use crate::geoip::QueryLocation;
|
||||||
@ -30,15 +33,22 @@ use crate::templating_engine::View;
|
|||||||
use crate::templating_engine::ResponseFormat;
|
use crate::templating_engine::ResponseFormat;
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
pub struct IpQuery {
|
pub struct BaseQuery {
|
||||||
ip: Option<IpAddr>,
|
|
||||||
format: Option<ResponseFormat>,
|
format: Option<ResponseFormat>,
|
||||||
|
lang: Option<String>,
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
|
pub struct IpQuery {
|
||||||
|
format: Option<ResponseFormat>,
|
||||||
|
lang: Option<String>,
|
||||||
|
ip: IpAddr,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
pub struct DigQuery {
|
pub struct DigQuery {
|
||||||
name: String,
|
|
||||||
format: Option<ResponseFormat>,
|
format: Option<ResponseFormat>,
|
||||||
|
name: String,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(serde::Deserialize, serde::Serialize)]
|
#[derive(serde::Deserialize, serde::Serialize)]
|
||||||
@ -48,6 +58,7 @@ pub struct IpResult {
|
|||||||
location: Option<LocationResult>,
|
location: Option<LocationResult>,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
struct ServiceSharedState {
|
struct ServiceSharedState {
|
||||||
templating_engine: templating_engine::Engine,
|
templating_engine: templating_engine::Engine,
|
||||||
dns_resolver: TokioAsyncResolver,
|
dns_resolver: TokioAsyncResolver,
|
||||||
@ -105,13 +116,17 @@ async fn main() {
|
|||||||
asn_db: asn_db,
|
asn_db: asn_db,
|
||||||
location_db: location_db,
|
location_db: location_db,
|
||||||
});
|
});
|
||||||
|
|
||||||
// Initalize axum server
|
// Initalize axum server
|
||||||
let app = Router::new()
|
let app = Router::new()
|
||||||
.route("/", get(handle_default_route))
|
.route("/", get(handle_default_route))
|
||||||
.route("/dig", get(handle_dig_route))
|
.route("/dig", get(handle_dig_route))
|
||||||
|
.route("/dig/:name", get(handle_dig_route_with_path))
|
||||||
|
.route("/ip", get(handle_ip_route))
|
||||||
|
.route("/ip/:address", get(handle_ip_route_with_path))
|
||||||
.route("/hi", get(hello_world_handler))
|
.route("/hi", get(hello_world_handler))
|
||||||
.with_state(shared_state)
|
.with_state(shared_state)
|
||||||
|
.layer(SecureClientIpSource::RightmostXForwardedFor.into_extension())
|
||||||
;
|
;
|
||||||
|
|
||||||
println!("Starting Server ...");
|
println!("Starting Server ...");
|
||||||
@ -134,13 +149,19 @@ async fn hello_world_handler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
async fn handle_default_route(
|
async fn handle_default_route(
|
||||||
Query(ip_query): Query<IpQuery>,
|
Query(query): Query<BaseQuery>,
|
||||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||||
|
SecureClientIp(address): SecureClientIp
|
||||||
) -> Response {
|
) -> Response {
|
||||||
|
|
||||||
let address = ip_query.ip.unwrap_or(IpAddr::V4(Ipv4Addr::new(0, 0, 0, 0)));
|
let format = query.format.unwrap_or(ResponseFormat::TextHtml);
|
||||||
let format = ip_query.format.unwrap_or(ResponseFormat::TextHtml);
|
|
||||||
|
|
||||||
|
let ip_query = IpQuery {
|
||||||
|
format: query.format,
|
||||||
|
lang: query.lang,
|
||||||
|
ip: address,
|
||||||
|
};
|
||||||
|
|
||||||
let state = Arc::clone(&arc_state);
|
let state = Arc::clone(&arc_state);
|
||||||
|
|
||||||
// do reverse lookup
|
// do reverse lookup
|
||||||
@ -150,7 +171,10 @@ async fn handle_default_route(
|
|||||||
let asn_result = state.asn_db.query_asn_for_ip(address);
|
let asn_result = state.asn_db.query_asn_for_ip(address);
|
||||||
|
|
||||||
// location lookup
|
// location lookup
|
||||||
let location_result = state.location_db.query_location_for_ip(address, &vec!["en".to_string()]);
|
let location_result = state.location_db.query_location_for_ip(
|
||||||
|
address,
|
||||||
|
&vec![&ip_query.lang.as_ref().unwrap_or(&"en".to_string()), &"en".to_string()]
|
||||||
|
);
|
||||||
|
|
||||||
let result = IpResult{
|
let result = IpResult{
|
||||||
hostname: hostname.await,
|
hostname: hostname.await,
|
||||||
@ -164,11 +188,82 @@ async fn handle_default_route(
|
|||||||
).await
|
).await
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async fn handle_ip_route(
|
||||||
|
Query(ip_query): Query<IpQuery>,
|
||||||
|
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||||
|
) -> Response {
|
||||||
|
return handle_ip_request(ip_query, arc_state).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_ip_route_with_path(
|
||||||
|
Query(query): Query<BaseQuery>,
|
||||||
|
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||||
|
extract::Path(address): extract::Path<IpAddr>,
|
||||||
|
) -> Response {
|
||||||
|
return handle_ip_request(IpQuery {
|
||||||
|
format: query.format,
|
||||||
|
lang: query.lang,
|
||||||
|
ip: address,
|
||||||
|
}, arc_state).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_ip_request(
|
||||||
|
ip_query: IpQuery,
|
||||||
|
arc_state: Arc<ServiceSharedState>,
|
||||||
|
) -> Response {
|
||||||
|
|
||||||
|
let address = ip_query.ip;
|
||||||
|
let format = ip_query.format.unwrap_or(ResponseFormat::TextHtml);
|
||||||
|
|
||||||
|
let state = Arc::clone(&arc_state);
|
||||||
|
|
||||||
|
// do reverse lookup
|
||||||
|
let hostname = simple_dns::reverse_lookup(&state.dns_resolver, &address);
|
||||||
|
|
||||||
|
// 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![&ip_query.lang.as_ref().unwrap_or(&"en".to_string()), &"en".to_string()]
|
||||||
|
);
|
||||||
|
|
||||||
|
let result = IpResult{
|
||||||
|
hostname: hostname.await,
|
||||||
|
asn: asn_result,
|
||||||
|
location: location_result,
|
||||||
|
};
|
||||||
|
|
||||||
|
state.templating_engine.render_view(
|
||||||
|
format,
|
||||||
|
View::Ip{query: ip_query, result: result}
|
||||||
|
).await
|
||||||
|
}
|
||||||
|
|
||||||
async fn handle_dig_route(
|
async fn handle_dig_route(
|
||||||
Query(dig_query): Query<DigQuery>,
|
Query(dig_query): Query<DigQuery>,
|
||||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||||
) -> Response {
|
) -> Response {
|
||||||
|
return handle_dig_request(dig_query, arc_state).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_dig_route_with_path(
|
||||||
|
Query(query): Query<BaseQuery>,
|
||||||
|
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||||
|
extract::Path(name): extract::Path<String>,
|
||||||
|
) -> Response {
|
||||||
|
return handle_dig_request(DigQuery {
|
||||||
|
format: query.format,
|
||||||
|
name: name,
|
||||||
|
}, arc_state).await
|
||||||
|
}
|
||||||
|
|
||||||
|
async fn handle_dig_request(
|
||||||
|
dig_query: DigQuery,
|
||||||
|
arc_state: Arc<ServiceSharedState>,
|
||||||
|
) -> Response {
|
||||||
|
|
||||||
let state = Arc::clone(&arc_state);
|
let state = Arc::clone(&arc_state);
|
||||||
let name = &dig_query.name;
|
let name = &dig_query.name;
|
||||||
let format = dig_query.format.unwrap_or(ResponseFormat::TextHtml);
|
let format = dig_query.format.unwrap_or(ResponseFormat::TextHtml);
|
||||||
|
Loading…
Reference in New Issue
Block a user