mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2025-07-15 13:33:28 +02:00
First prototype with multiple dns providers
This commit is contained in:
119
src/main.rs
119
src/main.rs
@ -33,6 +33,7 @@ use tokio::signal::unix::{
|
||||
};
|
||||
use tokio::task;
|
||||
|
||||
use std::collections::HashMap;
|
||||
use std::fs;
|
||||
use std::net::IpAddr;
|
||||
use std::sync::Arc;
|
||||
@ -72,6 +73,16 @@ pub struct SearchQuery {
|
||||
query: Option<String>,
|
||||
}
|
||||
|
||||
pub fn default_dns_name() -> String {
|
||||
"default".to_string()
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct ResolverQuery {
|
||||
#[serde(default="default_dns_name")]
|
||||
dns: String,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct IpResult {
|
||||
address: IpAddr,
|
||||
@ -96,7 +107,8 @@ pub struct DigResult {
|
||||
|
||||
struct ServiceSharedState {
|
||||
templating_engine: templating_engine::Engine,
|
||||
dns_resolver: TokioAsyncResolver,
|
||||
//dns_resolver: TokioAsyncResolver,
|
||||
dns_resolvers: HashMap<String,TokioAsyncResolver>,
|
||||
asn_db: geoip::MMDBCarrier,
|
||||
location_db: geoip::MMDBCarrier,
|
||||
config: config::EchoIpServiceConfig,
|
||||
@ -238,6 +250,30 @@ async fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
//FIXME: Not release ready,must be configurable and have better error handling.
|
||||
println!("Initalizing Quad9 resolver ...");
|
||||
let quad9_resolver = TokioAsyncResolver::tokio(
|
||||
trust_dns_resolver::config::ResolverConfig::quad9_tls(),
|
||||
Default::default()
|
||||
).unwrap();
|
||||
println!("Initalizing Google resolver ...");
|
||||
let google_resolver = TokioAsyncResolver::tokio(
|
||||
trust_dns_resolver::config::ResolverConfig::google(),
|
||||
Default::default()
|
||||
).unwrap();
|
||||
println!("Initalizing Cloudflare resolver ...");
|
||||
let cloudflare_resolver = TokioAsyncResolver::tokio(
|
||||
trust_dns_resolver::config::ResolverConfig::cloudflare_tls(),
|
||||
Default::default()
|
||||
).unwrap();
|
||||
|
||||
let mut dns_resolver_map: HashMap<String,TokioAsyncResolver> = HashMap::new();
|
||||
|
||||
dns_resolver_map.insert("default".to_string(), dns_resolver);
|
||||
dns_resolver_map.insert("quad9".to_string(), quad9_resolver);
|
||||
dns_resolver_map.insert("google".to_string(), google_resolver);
|
||||
dns_resolver_map.insert("cloudflare".to_string(), cloudflare_resolver);
|
||||
|
||||
let listen_on = config.server.listen_on;
|
||||
let ip_header = config.server.ip_header.clone();
|
||||
|
||||
@ -245,7 +281,8 @@ async fn main() {
|
||||
let shared_state = Arc::new(
|
||||
ServiceSharedState {
|
||||
templating_engine: templating_engine,
|
||||
dns_resolver: dns_resolver,
|
||||
//dns_resolver: dns_resolver,
|
||||
dns_resolvers: dns_resolver_map,
|
||||
asn_db: asn_db,
|
||||
location_db: location_db,
|
||||
config: config.clone(),
|
||||
@ -294,7 +331,7 @@ async fn main() {
|
||||
)
|
||||
;
|
||||
|
||||
println!("Starting Server ...");
|
||||
println!("Starting Server on {} ...",listen_on);
|
||||
|
||||
axum::Server::bind(&listen_on)
|
||||
.serve(app.into_make_service_with_connect_info::<std::net::SocketAddr>())
|
||||
@ -367,6 +404,7 @@ async fn user_agent_handler(
|
||||
|
||||
async fn handle_default_route(
|
||||
Query(search_query): Query<SearchQuery>,
|
||||
Query(resolver_settings): Query<ResolverQuery>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
Extension(settings): Extension<TemplateSettings>,
|
||||
user_agent_header: Option<TypedHeader<headers::UserAgent>>,
|
||||
@ -377,11 +415,17 @@ async fn handle_default_route(
|
||||
|
||||
if let Some(search_query) = search_query.query {
|
||||
if search_query.trim() != "" {
|
||||
return handle_search_request(search_query, false, settings, state).await;
|
||||
return handle_search_request(
|
||||
search_query,
|
||||
false,
|
||||
settings,
|
||||
resolver_settings,
|
||||
state
|
||||
).await;
|
||||
}
|
||||
}
|
||||
|
||||
let result = get_ip_result(&address, &settings.lang, &state).await;
|
||||
let result = get_ip_result(&address, &settings.lang, &"default".to_string(), &state).await;
|
||||
|
||||
let user_agent: Option<String> = match user_agent_header {
|
||||
Some(TypedHeader(user_agent)) => Some(user_agent.to_string()),
|
||||
@ -402,6 +446,7 @@ async fn handle_search_request(
|
||||
search_query: String,
|
||||
this_should_have_been_an_ip: bool,
|
||||
settings: TemplateSettings,
|
||||
resolver_settings: ResolverQuery,
|
||||
arc_state: Arc<ServiceSharedState>,
|
||||
) -> Response {
|
||||
|
||||
@ -425,12 +470,15 @@ async fn handle_search_request(
|
||||
|
||||
// Try to interpret as an IP-Address
|
||||
if let Ok(address) = search_query.parse() {
|
||||
return handle_ip_request(address, settings, arc_state).await;
|
||||
return handle_ip_request(address, settings, resolver_settings, arc_state).await;
|
||||
}
|
||||
|
||||
// Fall back to treating it as a hostname
|
||||
return handle_dig_request(
|
||||
search_query.to_string(), settings, arc_state,
|
||||
search_query.to_string(),
|
||||
settings,
|
||||
resolver_settings,
|
||||
arc_state,
|
||||
!this_should_have_been_an_ip,
|
||||
).await
|
||||
|
||||
@ -439,23 +487,29 @@ async fn handle_search_request(
|
||||
async fn handle_ip_route_with_path(
|
||||
Extension(settings): Extension<TemplateSettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
Query(resolver_settings): Query<ResolverQuery>,
|
||||
extract::Path(query): extract::Path<String>,
|
||||
) -> Response {
|
||||
if let Ok(address) = query.parse() {
|
||||
return handle_ip_request(address, settings, arc_state).await
|
||||
return handle_ip_request(address, settings, resolver_settings, arc_state).await
|
||||
} else {
|
||||
return handle_search_request(query, true, settings, arc_state).await;
|
||||
return handle_search_request(query, true, settings, resolver_settings, arc_state).await;
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_ip_request(
|
||||
address: IpAddr,
|
||||
settings: TemplateSettings,
|
||||
resolver_settings: ResolverQuery,
|
||||
arc_state: Arc<ServiceSharedState>,
|
||||
) -> Response {
|
||||
|
||||
let state = Arc::clone(&arc_state);
|
||||
let result = get_ip_result(&address, &settings.lang, &state).await;
|
||||
let result = get_ip_result(
|
||||
&address,
|
||||
&settings.lang,
|
||||
&resolver_settings.dns,
|
||||
&state).await;
|
||||
|
||||
state.templating_engine.render_view(
|
||||
&settings,
|
||||
@ -466,6 +520,7 @@ async fn handle_ip_request(
|
||||
async fn get_ip_result(
|
||||
address: &IpAddr,
|
||||
lang: &String,
|
||||
dns_resolver_name: &String,
|
||||
state: &ServiceSharedState,
|
||||
) -> IpResult {
|
||||
|
||||
@ -485,7 +540,11 @@ async fn get_ip_result(
|
||||
|
||||
// do reverse lookup
|
||||
let hostname = if state.config.dns.allow_reverse_lookup {
|
||||
simple_dns::reverse_lookup(&state.dns_resolver, &address).await
|
||||
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
|
||||
simple_dns::reverse_lookup(&dns_resolver, &address).await
|
||||
} else {
|
||||
None
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
@ -521,23 +580,30 @@ async fn get_ip_result(
|
||||
}
|
||||
|
||||
async fn handle_dig_route_with_path(
|
||||
Query(resolver_settings): Query<ResolverQuery>,
|
||||
Extension(settings): Extension<TemplateSettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
extract::Path(name): extract::Path<String>,
|
||||
) -> Response {
|
||||
return handle_dig_request(name, settings, arc_state, true).await
|
||||
return handle_dig_request(name, settings, resolver_settings, arc_state, true).await
|
||||
}
|
||||
|
||||
async fn handle_dig_request(
|
||||
dig_query: String,
|
||||
settings: TemplateSettings,
|
||||
resolver_settings: ResolverQuery,
|
||||
arc_state: Arc<ServiceSharedState>,
|
||||
do_full_lookup: bool,
|
||||
) -> Response {
|
||||
|
||||
let state = Arc::clone(&arc_state);
|
||||
|
||||
let dig_result = get_dig_result(&dig_query, &state, do_full_lookup).await;
|
||||
let dig_result = get_dig_result(
|
||||
&dig_query,
|
||||
&resolver_settings.dns,
|
||||
&state,
|
||||
do_full_lookup
|
||||
).await;
|
||||
|
||||
state.templating_engine.render_view(
|
||||
&settings,
|
||||
@ -547,22 +613,27 @@ async fn handle_dig_request(
|
||||
}
|
||||
|
||||
async fn get_dig_result(
|
||||
dig_query: &String,
|
||||
state: &ServiceSharedState,
|
||||
do_full_lookup: bool,
|
||||
dig_query: &String,
|
||||
dns_resolver_name: &String,
|
||||
state: &ServiceSharedState,
|
||||
do_full_lookup: bool,
|
||||
) -> DigResult {
|
||||
let name = &dig_query.trim().trim_end_matches(".").to_string();
|
||||
if match_domain_hidden_list(&name, &state.config.dns.hidden_suffixes) {
|
||||
Default::default()
|
||||
} else {
|
||||
let idna_name = IdnaName::from_string(&name);
|
||||
DigResult {
|
||||
records: simple_dns::lookup(
|
||||
&state.dns_resolver,
|
||||
&(idna_name.idn.clone().unwrap_or(name.to_owned())+"."),
|
||||
do_full_lookup).await,
|
||||
idn: idna_name,
|
||||
partial_lookup: !do_full_lookup,
|
||||
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
|
||||
let idna_name = IdnaName::from_string(&name);
|
||||
DigResult {
|
||||
records: simple_dns::lookup(
|
||||
&dns_resolver,
|
||||
&(idna_name.idn.clone().unwrap_or(name.to_owned())+"."),
|
||||
do_full_lookup).await,
|
||||
idn: idna_name,
|
||||
partial_lookup: !do_full_lookup,
|
||||
}
|
||||
} else {
|
||||
return Default::default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -116,10 +116,10 @@ impl Engine {
|
||||
_ => text.into_response(),
|
||||
}
|
||||
Err(e) => {
|
||||
println!("There was an error while rendering index.html: {e:?}");
|
||||
println!("There was an error while rendering template {template_name}: {e:?}");
|
||||
(
|
||||
StatusCode::INTERNAL_SERVER_ERROR,
|
||||
"Template error, contact owner or see logs.\n"
|
||||
format!("Template error in {template_name}, contact owner or see logs.\n")
|
||||
).into_response()
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user