mirror of
https://codeberg.org/slatian/service.echoip-slatecave.git
synced 2024-12-27 12:18:20 +01:00
Template passtrough for dns server information
This commit is contained in:
parent
cc6a025f89
commit
727d9a77cd
@ -64,5 +64,5 @@ burst = 15
|
||||
[dns.resolver.digitalcourage]
|
||||
display_name = "Digitalcourage 3"
|
||||
servers = ["5.9.164.112:853","[2a01:4f8:251:554::2]:853"]
|
||||
protocol = "Tls"
|
||||
protocol = "tls"
|
||||
tls_dns_name = "dns3.digitalcourage.de"
|
||||
|
@ -16,6 +16,7 @@ pub struct DnsConfig {
|
||||
}
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
#[serde(rename_all="lowercase")]
|
||||
pub enum DnsProtocol {
|
||||
Udp,
|
||||
Tcp,
|
||||
|
137
src/main.rs
137
src/main.rs
@ -50,13 +50,13 @@ use crate::geoip::{
|
||||
LocationResult,
|
||||
};
|
||||
use crate::idna::IdnaName;
|
||||
|
||||
use crate::simple_dns::DnsLookupResult;
|
||||
use crate::templating_engine::{
|
||||
View,
|
||||
ResponseFormat,
|
||||
TemplateSettings,
|
||||
Selectable,
|
||||
};
|
||||
|
||||
use crate::ipinfo::{AddressCast,AddressInfo,AddressScope};
|
||||
|
||||
#[derive(Deserialize, Serialize, Clone)]
|
||||
@ -85,6 +85,7 @@ pub struct IpResult {
|
||||
asn: Option<AsnResult>,
|
||||
location: Option<LocationResult>,
|
||||
ip_info: AddressInfo,
|
||||
used_dns_resolver: Option<String>,
|
||||
}
|
||||
|
||||
// We need this one to hide the partial lookup field when irelevant
|
||||
@ -97,18 +98,23 @@ pub struct DigResult {
|
||||
idn: IdnaName,
|
||||
#[serde(skip_serializing_if = "not")]
|
||||
partial_lookup: bool,
|
||||
used_dns_resolver: String,
|
||||
}
|
||||
|
||||
|
||||
struct ServiceSharedState {
|
||||
templating_engine: templating_engine::Engine,
|
||||
//dns_resolver: TokioAsyncResolver,
|
||||
dns_resolvers: HashMap<String,TokioAsyncResolver>,
|
||||
asn_db: geoip::MMDBCarrier,
|
||||
location_db: geoip::MMDBCarrier,
|
||||
config: config::EchoIpServiceConfig,
|
||||
}
|
||||
|
||||
// Stores configuration that is derived from the original configuration
|
||||
#[derive(Clone)]
|
||||
struct DerivedConfiguration {
|
||||
dns_resolver_selectables: Vec<Selectable>,
|
||||
}
|
||||
|
||||
#[derive(Parser)]
|
||||
#[command(author, version, long_about="A web service that tells you your ip-address and more …")]
|
||||
struct CliArgs {
|
||||
@ -234,6 +240,8 @@ async fn main() {
|
||||
// Initalize DNS resolver with os defaults
|
||||
println!("Initalizing dns resolver ...");
|
||||
|
||||
let mut dns_resolver_selectables = Vec::<Selectable>::new();
|
||||
|
||||
println!("Initalizing System resolver ...");
|
||||
let res = TokioAsyncResolver::tokio_from_system_conf();
|
||||
//let res = TokioAsyncResolver::tokio(ResolverConfig::default(), ResolverOpts::default());
|
||||
@ -245,22 +253,39 @@ async fn main() {
|
||||
}
|
||||
};
|
||||
|
||||
dns_resolver_selectables.push(Selectable {
|
||||
id: "default".to_string(),
|
||||
name: "System".to_string()
|
||||
});
|
||||
|
||||
//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();
|
||||
dns_resolver_selectables.push(Selectable {
|
||||
id: "quad9".to_string(),
|
||||
name: "Quad9".to_string()
|
||||
});
|
||||
println!("Initalizing Google resolver ...");
|
||||
let google_resolver = TokioAsyncResolver::tokio(
|
||||
trust_dns_resolver::config::ResolverConfig::google(),
|
||||
Default::default()
|
||||
).unwrap();
|
||||
dns_resolver_selectables.push(Selectable {
|
||||
id: "google".to_string(),
|
||||
name: "Google".to_string()
|
||||
});
|
||||
println!("Initalizing Cloudflare resolver ...");
|
||||
let cloudflare_resolver = TokioAsyncResolver::tokio(
|
||||
trust_dns_resolver::config::ResolverConfig::cloudflare_tls(),
|
||||
Default::default()
|
||||
).unwrap();
|
||||
dns_resolver_selectables.push(Selectable {
|
||||
id: "cloudflare".to_string(),
|
||||
name: "Cloudflare".to_string()
|
||||
});
|
||||
|
||||
let mut dns_resolver_map: HashMap<String,TokioAsyncResolver> = HashMap::new();
|
||||
|
||||
@ -271,6 +296,10 @@ async fn main() {
|
||||
Default::default()
|
||||
).unwrap();
|
||||
dns_resolver_map.insert(key.clone(), resolver);
|
||||
dns_resolver_selectables.push(Selectable {
|
||||
id: key.clone(),
|
||||
name: resolver_config.display_name.clone(),
|
||||
});
|
||||
}
|
||||
|
||||
dns_resolver_map.insert("default".to_string(), dns_resolver);
|
||||
@ -292,6 +321,10 @@ async fn main() {
|
||||
config: config.clone(),
|
||||
});
|
||||
|
||||
let derived_config = DerivedConfiguration {
|
||||
dns_resolver_selectables: dns_resolver_selectables,
|
||||
};
|
||||
|
||||
let signal_usr1_handlers_state = shared_state.clone();
|
||||
|
||||
task::spawn(async move {
|
||||
@ -317,6 +350,8 @@ async fn main() {
|
||||
.route("/", get(handle_default_route))
|
||||
.route("/dig/:name", get(handle_dig_route_with_path))
|
||||
.route("/ip/:address", get(handle_ip_route_with_path))
|
||||
.route("/dns_resolver/:resolver", get(handle_dns_resolver_route_with_path))
|
||||
.route("/dns_resolver", get(handle_dns_resolver_route))
|
||||
.route("/ua", get(user_agent_handler))
|
||||
.route("/hi", get(hello_world_handler))
|
||||
.fallback_service(
|
||||
@ -331,6 +366,7 @@ async fn main() {
|
||||
config.ratelimit.per_minute, config.ratelimit.burst))
|
||||
.layer(middleware::from_fn(ratelimit::rate_limit_middleware))
|
||||
.layer(Extension(config))
|
||||
.layer(Extension(derived_config))
|
||||
.layer(middleware::from_fn(settings_query_middleware))
|
||||
)
|
||||
;
|
||||
@ -347,6 +383,7 @@ async fn main() {
|
||||
async fn settings_query_middleware<B>(
|
||||
Query(query): Query<SettingsQuery>,
|
||||
Extension(config): Extension<config::EchoIpServiceConfig>,
|
||||
Extension(derived_config): Extension<DerivedConfiguration>,
|
||||
user_agent_header: Option<TypedHeader<headers::UserAgent>>,
|
||||
mut req: Request<B>,
|
||||
next: Next<B>
|
||||
@ -369,6 +406,7 @@ async fn settings_query_middleware<B>(
|
||||
template: TemplateSettings{
|
||||
format: format.unwrap_or(ResponseFormat::TextHtml),
|
||||
lang: query.lang.unwrap_or("en".to_string()),
|
||||
available_dns_resolvers: derived_config.dns_resolver_selectables,
|
||||
},
|
||||
dns_resolver_id: query.dns.unwrap_or(config.dns.default_resolver),
|
||||
});
|
||||
@ -377,24 +415,24 @@ async fn settings_query_middleware<B>(
|
||||
|
||||
async fn not_found_handler(
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
Extension(settings): Extension<TemplateSettings>,
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
) -> Response {
|
||||
let state = Arc::clone(&arc_state);
|
||||
|
||||
state.templating_engine.render_view(
|
||||
&settings,
|
||||
&settings.template,
|
||||
&View::NotFound,
|
||||
).await
|
||||
}
|
||||
|
||||
async fn hello_world_handler(
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
Extension(settings): Extension<TemplateSettings>,
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
) -> Response {
|
||||
let state = Arc::clone(&arc_state);
|
||||
|
||||
state.templating_engine.render_view(
|
||||
&settings,
|
||||
&settings.template,
|
||||
&View::Message{
|
||||
title: "Hey There!".to_string(),
|
||||
message: "You,You are an awesome Creature!".to_string()
|
||||
@ -499,6 +537,37 @@ async fn handle_search_request(
|
||||
|
||||
}
|
||||
|
||||
async fn handle_dns_resolver_route(
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
) -> Response {
|
||||
let state = Arc::clone(&arc_state);
|
||||
state.templating_engine.render_view(
|
||||
&settings.template,
|
||||
&View::DnsResolverList,
|
||||
).await
|
||||
}
|
||||
|
||||
|
||||
async fn handle_dns_resolver_route_with_path(
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
extract::Path(query): extract::Path<String>,
|
||||
) -> Response {
|
||||
let state = Arc::clone(&arc_state);
|
||||
if let Some(resolver) = state.config.dns.resolver.get(&query) {
|
||||
state.templating_engine.render_view(
|
||||
&settings.template,
|
||||
&View::DnsResolver{ config: resolver.clone() },
|
||||
).await
|
||||
} else {
|
||||
state.templating_engine.render_view(
|
||||
&settings.template,
|
||||
&View::NotFound,
|
||||
).await
|
||||
}
|
||||
}
|
||||
|
||||
async fn handle_ip_route_with_path(
|
||||
Extension(settings): Extension<QuerySettings>,
|
||||
State(arc_state): State<Arc<ServiceSharedState>>,
|
||||
@ -547,20 +616,20 @@ async fn get_ip_result(
|
||||
asn: None,
|
||||
location: None,
|
||||
ip_info: ip_info,
|
||||
used_dns_resolver: None,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// do reverse lookup
|
||||
let hostname = if state.config.dns.allow_reverse_lookup {
|
||||
let mut hostname: Option<String> = None;
|
||||
let mut used_dns_resolver: Option<String> = None;
|
||||
if state.config.dns.allow_reverse_lookup {
|
||||
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
|
||||
simple_dns::reverse_lookup(&dns_resolver, &address).await
|
||||
} else {
|
||||
None
|
||||
hostname = simple_dns::reverse_lookup(&dns_resolver, &address).await;
|
||||
used_dns_resolver = Some(dns_resolver_name.clone());
|
||||
}
|
||||
}
|
||||
} else {
|
||||
None
|
||||
};
|
||||
|
||||
// asn lookup
|
||||
let asn_result = state.asn_db.query_asn_for_ip(address);
|
||||
@ -572,23 +641,20 @@ async fn get_ip_result(
|
||||
);
|
||||
|
||||
// filter reverse lookup
|
||||
let final_hostname = match hostname {
|
||||
Some(name) => {
|
||||
if let Some(name) = &hostname {
|
||||
if match_domain_hidden_list(&name, &state.config.dns.hidden_suffixes) {
|
||||
None
|
||||
} else {
|
||||
Some(name.to_owned())
|
||||
hostname = None;
|
||||
used_dns_resolver = None;
|
||||
}
|
||||
}
|
||||
},
|
||||
None => None,
|
||||
};
|
||||
|
||||
IpResult{
|
||||
address: *address,
|
||||
hostname: final_hostname,
|
||||
hostname: hostname,
|
||||
asn: asn_result,
|
||||
location: location_result,
|
||||
ip_info: ip_info,
|
||||
used_dns_resolver: used_dns_resolver,
|
||||
}
|
||||
}
|
||||
|
||||
@ -630,11 +696,23 @@ async fn get_dig_result(
|
||||
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 {
|
||||
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
|
||||
let idna_name = IdnaName::from_string(&name);
|
||||
if let Some(dns_resolver) = &state.dns_resolvers.get(dns_resolver_name) {
|
||||
if match_domain_hidden_list(&name, &state.config.dns.hidden_suffixes) {
|
||||
// Try to hide the fact that we didn't do dns resolution at all
|
||||
// We resolve example.org as basic avoidance of timing sidechannels.
|
||||
// WARNING: this timing sidechannel avoidance is very crude.
|
||||
simple_dns::lookup(
|
||||
&dns_resolver,
|
||||
&("example.org.".to_string()),
|
||||
do_full_lookup).await;
|
||||
DigResult {
|
||||
records: DnsLookupResult{ nxdomain: true , ..Default::default() },
|
||||
idn: idna_name,
|
||||
partial_lookup: !do_full_lookup,
|
||||
used_dns_resolver: dns_resolver_name.clone(),
|
||||
}
|
||||
} else {
|
||||
DigResult {
|
||||
records: simple_dns::lookup(
|
||||
&dns_resolver,
|
||||
@ -642,9 +720,10 @@ async fn get_dig_result(
|
||||
do_full_lookup).await,
|
||||
idn: idna_name,
|
||||
partial_lookup: !do_full_lookup,
|
||||
used_dns_resolver: dns_resolver_name.clone(),
|
||||
}
|
||||
}
|
||||
} else {
|
||||
return Default::default();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -27,20 +27,20 @@ use std::net::IpAddr;
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Default, Clone)]
|
||||
pub struct DnsLookupResult {
|
||||
a: Option<Vec<IpAddr>>,
|
||||
aaaa: Option<Vec<IpAddr>>,
|
||||
aname: Option<Vec<String>>,
|
||||
cname: Option<Vec<String>>,
|
||||
mx: Option<Vec<MxRecord>>,
|
||||
ns: Option<Vec<String>>,
|
||||
soa: Option<Vec<SoaRecord>>,
|
||||
txt: Option<Vec<String>>,
|
||||
srv: Option<Vec<SrvRecord>>,
|
||||
caa: Option<Vec<String>>,
|
||||
other_error: bool,
|
||||
dns_error: bool,
|
||||
nxdomain: bool,
|
||||
timeout: bool,
|
||||
pub a: Option<Vec<IpAddr>>,
|
||||
pub aaaa: Option<Vec<IpAddr>>,
|
||||
pub aname: Option<Vec<String>>,
|
||||
pub cname: Option<Vec<String>>,
|
||||
pub mx: Option<Vec<MxRecord>>,
|
||||
pub ns: Option<Vec<String>>,
|
||||
pub soa: Option<Vec<SoaRecord>>,
|
||||
pub txt: Option<Vec<String>>,
|
||||
pub srv: Option<Vec<SrvRecord>>,
|
||||
pub caa: Option<Vec<String>>,
|
||||
pub other_error: bool,
|
||||
pub dns_error: bool,
|
||||
pub nxdomain: bool,
|
||||
pub timeout: bool,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone, PartialEq)]
|
||||
|
@ -15,6 +15,7 @@ use toml::Table;
|
||||
|
||||
use crate::DigResult;
|
||||
use crate::IpResult;
|
||||
use crate::config::DnsResolverConfig;
|
||||
|
||||
/* Response format */
|
||||
|
||||
@ -54,6 +55,13 @@ impl ResponseFormat {
|
||||
pub struct TemplateSettings {
|
||||
pub format: ResponseFormat,
|
||||
pub lang: String,
|
||||
pub available_dns_resolvers: Vec<Selectable>,
|
||||
}
|
||||
|
||||
#[derive(serde::Deserialize, serde::Serialize, Clone)]
|
||||
pub struct Selectable {
|
||||
pub id: String,
|
||||
pub name: String,
|
||||
}
|
||||
|
||||
/* The echoip view */
|
||||
@ -63,6 +71,8 @@ pub struct TemplateSettings {
|
||||
pub enum View {
|
||||
Asn { asn: u32 },
|
||||
Dig { query: String, result: DigResult },
|
||||
DnsResolver{ config: DnsResolverConfig },
|
||||
DnsResolverList,
|
||||
Index { result: IpResult, user_agent: Option<String> },
|
||||
Ip { result: IpResult },
|
||||
Message{ title: String, message: String },
|
||||
@ -75,6 +85,8 @@ impl View {
|
||||
match self {
|
||||
View::Asn{..} => "asn",
|
||||
View::Dig{..} => "dig",
|
||||
View::DnsResolver{..} => "dns_resolver",
|
||||
View::DnsResolverList => "dns_resolver_list",
|
||||
View::Index{..} => "index",
|
||||
View::Ip{..} => "ip",
|
||||
View::Message{..} => "message",
|
||||
@ -133,6 +145,12 @@ impl Engine {
|
||||
View::Index{result, ..} | View::Ip{result, ..} => {
|
||||
Json(result).into_response()
|
||||
},
|
||||
View::DnsResolverList => {
|
||||
Json(settings.available_dns_resolvers.clone()).into_response()
|
||||
},
|
||||
View::DnsResolver{ config } => {
|
||||
Json(config).into_response()
|
||||
}
|
||||
_ => Json(view).into_response(),
|
||||
}
|
||||
}
|
||||
|
Loading…
Reference in New Issue
Block a user