From bf8aa57d03012b456f28da06381b7a528b91ba5e Mon Sep 17 00:00:00 2001 From: Jason Volk Date: Wed, 10 Sep 2025 01:01:00 +0000 Subject: [PATCH] Add config option for dns passthru for appservices. (#158) Signed-off-by: Jason Volk --- src/core/config/mod.rs | 7 +++++++ src/service/client/mod.rs | 12 ++++++++++-- src/service/resolver/dns.rs | 20 +++++++++++++++++--- tuwunel-example.toml | 7 +++++++ 4 files changed, 41 insertions(+), 5 deletions(-) diff --git a/src/core/config/mod.rs b/src/core/config/mod.rs index ca44e2b9..fd767eb3 100644 --- a/src/core/config/mod.rs +++ b/src/core/config/mod.rs @@ -378,6 +378,13 @@ pub struct Config { #[serde(default, with = "serde_regex")] pub dns_passthru_domains: RegexSet, + /// Whether to resolve appservices via the alternative path; setting this is + /// superior to providing domains in `dns_passthru_domains` if all + /// appservices intend to be matched anyway. The overhead of matching regex + /// and maintaining the list of domains can be avoided. + #[serde(default)] + pub dns_passthru_appservices: bool, + /// Max request size for file uploads in bytes. Defaults to 20MB. /// /// default: 20971520 diff --git a/src/service/client/mod.rs b/src/service/client/mod.rs index c0029eba..04910c4f 100644 --- a/src/service/client/mod.rs +++ b/src/service/client/mod.rs @@ -5,7 +5,7 @@ use std::{ use either::Either; use ipaddress::IPAddress; -use reqwest::redirect; +use reqwest::{dns::Resolve, redirect}; use tuwunel_core::{Config, Result, err, implement, trace}; use crate::{service, services::OnceServices}; @@ -98,7 +98,7 @@ impl crate::Service for Service { .redirect(redirect::Policy::limited(2))), appservice: create_client!(config, services; base(config)? - .dns_resolver2(Arc::clone(&services.resolver.resolver)) + .dns_resolver2(appservice_resolver(&services)) .connect_timeout(Duration::from_secs(5)) .read_timeout(Duration::from_secs(config.appservice_timeout)) .timeout(Duration::from_secs(config.appservice_timeout)) @@ -220,6 +220,14 @@ fn builder_interface( } } +fn appservice_resolver(services: &Arc) -> Arc { + if services.server.config.dns_passthru_appservices { + services.resolver.resolver.passthru.clone() + } else { + services.resolver.resolver.clone() + } +} + #[inline] #[must_use] #[implement(Service)] diff --git a/src/service/resolver/dns.rs b/src/service/resolver/dns.rs index 29f034d6..1a44ab84 100644 --- a/src/service/resolver/dns.rs +++ b/src/service/resolver/dns.rs @@ -13,7 +13,7 @@ use super::cache::{Cache, CachedOverride}; pub struct Resolver { pub(crate) resolver: Arc, - pub(crate) passthru: Arc, + pub(crate) passthru: Arc, pub(crate) hooked: Arc, server: Arc, } @@ -24,6 +24,11 @@ pub(crate) struct Hooked { server: Arc, } +pub(crate) struct Passthru { + resolver: Arc, + server: Arc, +} + type ResolvingResult = Result>; impl Resolver { @@ -47,9 +52,12 @@ impl Resolver { resolver: resolver.clone(), cache, }), + passthru: Arc::new(Passthru { + server: server.clone(), + resolver: passthru, + }), server: server.clone(), resolver, - passthru, })) } @@ -137,7 +145,7 @@ impl Resolve for Resolver { .dns_passthru_domains .is_match(name.as_str()) { - &self.passthru + &self.passthru.resolver } else { &self.resolver }; @@ -146,6 +154,12 @@ impl Resolve for Resolver { } } +impl Resolve for Passthru { + fn resolve(&self, name: Name) -> Resolving { + resolve_to_reqwest(self.server.clone(), self.resolver.clone(), name).boxed() + } +} + impl Resolve for Hooked { fn resolve(&self, name: Name) -> Resolving { hooked_resolve(self.cache.clone(), self.server.clone(), self.resolver.clone(), name) diff --git a/tuwunel-example.toml b/tuwunel-example.toml index 029123af..da3c48e1 100644 --- a/tuwunel-example.toml +++ b/tuwunel-example.toml @@ -295,6 +295,13 @@ # #dns_passthru_domains = [] +# Whether to resolve appservices via the alternative path; setting this is +# superior to providing domains in `dns_passthru_domains` if all +# appservices intend to be matched anyway. The overhead of matching regex +# and maintaining the list of domains can be avoided. +# +#dns_passthru_appservices = false + # Max request size for file uploads in bytes. Defaults to 20MB. # #max_request_size = 20971520