summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorDavid Lönnhager <david.l@mullvad.net>2024-09-18 11:03:02 +0200
committerDavid Lönnhager <david.l@mullvad.net>2024-09-18 11:03:02 +0200
commit5ff831f597c1361effbca3749df056329cbb2f84 (patch)
tree5d3d8e9ecb7101759957c1f702c1a59d5f426a7a
parentcdef6650f38dba21052f1c6e0d5e9e2ebadf0d97 (diff)
parentbeb063780d6f3a261e40ac7940c61c937e2b951d (diff)
downloadmullvadvpn-5ff831f597c1361effbca3749df056329cbb2f84.tar.xz
mullvadvpn-5ff831f597c1361effbca3749df056329cbb2f84.zip
Merge branch 'refactor-tunnel-dns-config'
-rw-r--r--mullvad-daemon/src/dns.rs77
-rw-r--r--mullvad-daemon/src/lib.rs2
-rw-r--r--talpid-core/src/dns/android.rs4
-rw-r--r--talpid-core/src/dns/linux/mod.rs5
-rw-r--r--talpid-core/src/dns/macos.rs8
-rw-r--r--talpid-core/src/dns/mod.rs127
-rw-r--r--talpid-core/src/dns/windows/auto.rs16
-rw-r--r--talpid-core/src/dns/windows/iphlpapi.rs5
-rw-r--r--talpid-core/src/dns/windows/mod.rs14
-rw-r--r--talpid-core/src/dns/windows/netsh.rs5
-rw-r--r--talpid-core/src/dns/windows/tcpip.rs6
-rw-r--r--talpid-core/src/firewall/linux.rs65
-rw-r--r--talpid-core/src/firewall/macos.rs158
-rw-r--r--talpid-core/src/firewall/mod.rs4
-rw-r--r--talpid-core/src/firewall/windows.rs54
-rw-r--r--talpid-core/src/tunnel_state_machine/connected_state.rs34
-rw-r--r--talpid-core/src/tunnel_state_machine/connecting_state.rs2
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnected_state.rs9
-rw-r--r--talpid-core/src/tunnel_state_machine/disconnecting_state.rs6
-rw-r--r--talpid-core/src/tunnel_state_machine/error_state.rs12
-rw-r--r--talpid-core/src/tunnel_state_machine/mod.rs28
-rw-r--r--windows/winfw/src/winfw/winfw.cpp88
-rw-r--r--windows/winfw/src/winfw/winfw.h14
23 files changed, 437 insertions, 306 deletions
diff --git a/mullvad-daemon/src/dns.rs b/mullvad-daemon/src/dns.rs
index 7846d7c3ff..9b0ae9163c 100644
--- a/mullvad-daemon/src/dns.rs
+++ b/mullvad-daemon/src/dns.rs
@@ -1,5 +1,6 @@
use mullvad_types::settings::{DnsOptions, DnsState};
use std::net::{IpAddr, Ipv4Addr};
+use talpid_core::{dns::DnsConfig, firewall::is_local_address};
/// When we want to block certain contents with the help of DNS server side,
/// we compute the resolver IP to use based on these constants. The last
@@ -12,9 +13,8 @@ const DNS_ADULT_BLOCKING_IP_BIT: u8 = 1 << 3; // 0b00001000
const DNS_GAMBLING_BLOCKING_IP_BIT: u8 = 1 << 4; // 0b00010000
const DNS_SOCIAL_MEDIA_BLOCKING_IP_BIT: u8 = 1 << 5; // 0b00100000
-/// Return the resolvers as a vector of `IpAddr`s. Returns `None` when no special resolvers
-/// are requested and the tunnel default gateway should be used.
-pub fn addresses_from_options(options: &DnsOptions) -> Option<Vec<IpAddr>> {
+/// Return the DNS resolvers to use
+pub fn addresses_from_options(options: &DnsOptions) -> DnsConfig {
match options.state {
DnsState::Default => {
// Check if we should use a custom blocking DNS resolver.
@@ -43,17 +43,74 @@ pub fn addresses_from_options(options: &DnsOptions) -> Option<Vec<IpAddr>> {
if last_byte != 0 {
let mut dns_ip = DNS_BLOCKING_IP_BASE.octets();
dns_ip[dns_ip.len() - 1] |= last_byte;
- Some(vec![IpAddr::V4(Ipv4Addr::from(dns_ip))])
+ DnsConfig::from_addresses(&[IpAddr::V4(Ipv4Addr::from(dns_ip))], &[])
} else {
- None
+ DnsConfig::default()
}
}
+ DnsState::Custom if options.custom_options.addresses.is_empty() => DnsConfig::default(),
DnsState::Custom => {
- if options.custom_options.addresses.is_empty() {
- None
- } else {
- Some(options.custom_options.addresses.clone())
- }
+ let (non_tunnel_config, tunnel_config): (Vec<_>, Vec<_>) = options
+ .custom_options
+ .addresses
+ .iter()
+ // Private IP ranges should not be tunneled
+ .partition(|&addr| is_local_address(addr));
+ DnsConfig::from_addresses(&tunnel_config, &non_tunnel_config)
}
}
}
+
+#[cfg(test)]
+mod test {
+ use crate::dns::addresses_from_options;
+ use mullvad_types::settings::{CustomDnsOptions, DefaultDnsOptions, DnsOptions, DnsState};
+ use talpid_core::dns::DnsConfig;
+
+ #[test]
+ fn test_default_dns() {
+ let public_cfg = DnsOptions {
+ state: DnsState::Default,
+ custom_options: CustomDnsOptions::default(),
+ default_options: DefaultDnsOptions::default(),
+ };
+
+ assert_eq!(addresses_from_options(&public_cfg), DnsConfig::default());
+ }
+
+ #[test]
+ fn test_content_blockers() {
+ let public_cfg = DnsOptions {
+ state: DnsState::Default,
+ custom_options: CustomDnsOptions::default(),
+ default_options: DefaultDnsOptions {
+ block_ads: true,
+ ..DefaultDnsOptions::default()
+ },
+ };
+
+ assert_eq!(
+ addresses_from_options(&public_cfg),
+ DnsConfig::from_addresses(&["100.64.0.1".parse().unwrap()], &[],)
+ );
+ }
+
+ // Public IPs should be tunneled, but most private IPs should not be
+ #[test]
+ fn test_custom_dns() {
+ let public_ip = "1.2.3.4".parse().unwrap();
+ let private_ip = "172.16.10.1".parse().unwrap();
+ let public_cfg = DnsOptions {
+ state: DnsState::Custom,
+ custom_options: CustomDnsOptions {
+ addresses: vec![public_ip, private_ip],
+ },
+ default_options: DefaultDnsOptions::default(),
+ };
+
+ assert_eq!(
+ addresses_from_options(&public_cfg),
+ DnsConfig::from_addresses(&[public_ip], &[private_ip],)
+ );
+ }
+}
diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs
index 097dcc6bf6..b0e9ea7d58 100644
--- a/mullvad-daemon/src/lib.rs
+++ b/mullvad-daemon/src/lib.rs
@@ -758,7 +758,7 @@ impl Daemon {
tunnel_state_machine::InitialTunnelState {
allow_lan: settings.allow_lan,
block_when_disconnected: settings.block_when_disconnected,
- dns_servers: dns::addresses_from_options(&settings.tunnel_options.dns_options),
+ dns_config: dns::addresses_from_options(&settings.tunnel_options.dns_options),
allowed_endpoint: access_mode_handler
.get_current()
.await
diff --git a/talpid-core/src/dns/android.rs b/talpid-core/src/dns/android.rs
index 6f4e110d3f..01acae9eb5 100644
--- a/talpid-core/src/dns/android.rs
+++ b/talpid-core/src/dns/android.rs
@@ -1,4 +1,4 @@
-use std::net::IpAddr;
+use crate::dns::ResolvedDnsConfig;
/// Stub error type for DNS errors on Android.
#[derive(Debug, thiserror::Error)]
@@ -14,7 +14,7 @@ impl super::DnsMonitorT for DnsMonitor {
Ok(DnsMonitor)
}
- fn set(&mut self, _interface: &str, _servers: &[IpAddr]) -> Result<(), Self::Error> {
+ fn set(&mut self, _interface: &str, _servers: ResolvedDnsConfig) -> Result<(), Self::Error> {
Ok(())
}
diff --git a/talpid-core/src/dns/linux/mod.rs b/talpid-core/src/dns/linux/mod.rs
index 3e7f6ac0b8..ad27237cd4 100644
--- a/talpid-core/src/dns/linux/mod.rs
+++ b/talpid-core/src/dns/linux/mod.rs
@@ -10,6 +10,8 @@ use self::{
use std::{env, fmt, net::IpAddr};
use talpid_routing::RouteManagerHandle;
+use super::ResolvedDnsConfig;
+
pub type Result<T> = std::result::Result<T, Error>;
/// Errors that can happen in the Linux DNS monitor
@@ -53,7 +55,8 @@ impl super::DnsMonitorT for DnsMonitor {
})
}
- fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<()> {
+ fn set(&mut self, interface: &str, config: ResolvedDnsConfig) -> Result<()> {
+ let servers = config.tunnel_config();
self.reset()?;
// Creating a new DNS monitor for each set, in case the system changed how it manages DNS.
let mut inner = DnsMonitorHolder::new()?;
diff --git a/talpid-core/src/dns/macos.rs b/talpid-core/src/dns/macos.rs
index e0535fedf7..78f4550688 100644
--- a/talpid-core/src/dns/macos.rs
+++ b/talpid-core/src/dns/macos.rs
@@ -21,6 +21,8 @@ use system_configuration::{
};
use talpid_routing::debounce::BurstGuard;
+use super::ResolvedDnsConfig;
+
pub type Result<T> = std::result::Result<T, Error>;
/// Errors that can happen when setting/monitoring DNS on macOS.
@@ -357,9 +359,11 @@ impl super::DnsMonitorT for DnsMonitor {
})
}
- fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<()> {
+ fn set(&mut self, interface: &str, config: ResolvedDnsConfig) -> Result<()> {
+ let servers: Vec<_> = config.addresses().collect();
+
let mut state = self.state.lock();
- state.apply_new_config(&self.store, interface, servers)
+ state.apply_new_config(&self.store, interface, &servers)
}
fn reset(&mut self) -> Result<()> {
diff --git a/talpid-core/src/dns/mod.rs b/talpid-core/src/dns/mod.rs
index 6f81829dab..d6fd333449 100644
--- a/talpid-core/src/dns/mod.rs
+++ b/talpid-core/src/dns/mod.rs
@@ -1,4 +1,6 @@
+use std::fmt;
use std::net::IpAddr;
+
#[cfg(target_os = "linux")]
use talpid_routing::RouteManagerHandle;
@@ -23,6 +25,116 @@ mod imp;
pub use self::imp::Error;
+/// DNS configuration
+#[derive(Debug, Clone, PartialEq)]
+pub struct DnsConfig {
+ config: InnerDnsConfig,
+}
+
+impl Default for DnsConfig {
+ fn default() -> Self {
+ Self {
+ config: InnerDnsConfig::Default,
+ }
+ }
+}
+
+impl DnsConfig {
+ /// Use the specified addresses for DNS resolution
+ pub fn from_addresses(tunnel_config: &[IpAddr], non_tunnel_config: &[IpAddr]) -> Self {
+ DnsConfig {
+ config: InnerDnsConfig::Override {
+ tunnel_config: tunnel_config.to_owned(),
+ non_tunnel_config: non_tunnel_config.to_owned(),
+ },
+ }
+ }
+}
+
+#[derive(Debug, Clone, PartialEq)]
+enum InnerDnsConfig {
+ /// Use gateway addresses from the tunnel config
+ Default,
+ /// Use the specified addresses for DNS resolution
+ Override {
+ /// Addresses to configure on the tunnel interface
+ tunnel_config: Vec<IpAddr>,
+ /// Addresses to allow on non-tunnel interface.
+ /// For the most part, the tunnel state machine will not handle any of this configuration
+ /// on non-tunnel interface, only allow them in the firewall.
+ non_tunnel_config: Vec<IpAddr>,
+ },
+}
+
+impl DnsConfig {
+ pub(crate) fn resolve(&self, default_tun_config: &[IpAddr]) -> ResolvedDnsConfig {
+ match &self.config {
+ InnerDnsConfig::Default => ResolvedDnsConfig {
+ tunnel_config: default_tun_config.to_owned(),
+ non_tunnel_config: vec![],
+ },
+ InnerDnsConfig::Override {
+ tunnel_config,
+ non_tunnel_config,
+ } => ResolvedDnsConfig {
+ tunnel_config: tunnel_config.to_owned(),
+ non_tunnel_config: non_tunnel_config.to_owned(),
+ },
+ }
+ }
+}
+
+/// DNS configuration with `DnsConfig::Default` resolved
+#[derive(Debug, Clone, PartialEq, Eq)]
+pub struct ResolvedDnsConfig {
+ /// Addresses to configure on the tunnel interface
+ tunnel_config: Vec<IpAddr>,
+ /// Addresses to allow on non-tunnel interface.
+ /// For the most part, the tunnel state machine will not handle any of this configuration
+ /// on non-tunnel interface, only allow them in the firewall.
+ non_tunnel_config: Vec<IpAddr>,
+}
+
+impl fmt::Display for ResolvedDnsConfig {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
+ f.write_str("Tunnel DNS: ")?;
+ Self::fmt_addr_set(f, &self.tunnel_config)?;
+
+ f.write_str(" Non-tunnel DNS: ")?;
+ Self::fmt_addr_set(f, &self.non_tunnel_config)
+ }
+}
+
+impl ResolvedDnsConfig {
+ fn fmt_addr_set(f: &mut fmt::Formatter<'_>, addrs: &[IpAddr]) -> fmt::Result {
+ f.write_str("{")?;
+ for (i, addr) in addrs.iter().enumerate() {
+ if i > 0 {
+ f.write_str(", ")?;
+ }
+ write!(f, "{}", addr)?;
+ }
+ f.write_str("}")
+ }
+
+ /// Addresses to configure on the tunnel interface
+ pub fn tunnel_config(&self) -> &[IpAddr] {
+ &self.tunnel_config
+ }
+
+ /// Addresses to allow on non-tunnel interface.
+ /// For the most part, the tunnel state machine will not handle any of this configuration
+ /// on non-tunnel interface, only allow them in the firewall.
+ pub fn non_tunnel_config(&self) -> &[IpAddr] {
+ &self.non_tunnel_config
+ }
+
+ /// Consume `self` and return a vector of all addresses
+ pub fn addresses(self) -> impl Iterator<Item = IpAddr> {
+ self.non_tunnel_config.into_iter().chain(self.tunnel_config)
+ }
+}
+
/// Sets and monitors system DNS settings. Makes sure the desired DNS servers are being used.
pub struct DnsMonitor {
inner: imp::DnsMonitor,
@@ -45,16 +157,9 @@ impl DnsMonitor {
}
/// Set DNS to the given servers. And start monitoring the system for changes.
- pub fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
- log::info!(
- "Setting DNS servers to {}",
- servers
- .iter()
- .map(|ip| ip.to_string())
- .collect::<Vec<String>>()
- .join(", ")
- );
- self.inner.set(interface, servers)
+ pub fn set(&mut self, interface: &str, config: ResolvedDnsConfig) -> Result<(), Error> {
+ log::info!("Setting DNS servers: {config}",);
+ self.inner.set(interface, config)
}
/// Reset system DNS settings to what it was before being set by this instance.
@@ -81,7 +186,7 @@ trait DnsMonitorT: Sized {
#[cfg(target_os = "linux")] route_manager: RouteManagerHandle,
) -> Result<Self, Self::Error>;
- fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Self::Error>;
+ fn set(&mut self, interface: &str, servers: ResolvedDnsConfig) -> Result<(), Self::Error>;
fn reset(&mut self) -> Result<(), Self::Error>;
diff --git a/talpid-core/src/dns/windows/auto.rs b/talpid-core/src/dns/windows/auto.rs
index c0b04742d4..3e17111956 100644
--- a/talpid-core/src/dns/windows/auto.rs
+++ b/talpid-core/src/dns/windows/auto.rs
@@ -1,5 +1,5 @@
use super::{iphlpapi, netsh, tcpip};
-use crate::dns::DnsMonitorT;
+use crate::dns::{DnsMonitorT, ResolvedDnsConfig};
use windows_sys::Win32::System::Rpc::RPC_S_SERVER_UNAVAILABLE;
pub struct DnsMonitor {
@@ -12,11 +12,11 @@ enum InnerMonitor {
}
impl InnerMonitor {
- fn set(&mut self, interface: &str, servers: &[std::net::IpAddr]) -> Result<(), super::Error> {
+ fn set(&mut self, interface: &str, config: ResolvedDnsConfig) -> Result<(), super::Error> {
match self {
- InnerMonitor::Iphlpapi(monitor) => monitor.set(interface, servers)?,
- InnerMonitor::Netsh(monitor) => monitor.set(interface, servers)?,
- InnerMonitor::Tcpip(monitor) => monitor.set(interface, servers)?,
+ InnerMonitor::Iphlpapi(monitor) => monitor.set(interface, config)?,
+ InnerMonitor::Netsh(monitor) => monitor.set(interface, config)?,
+ InnerMonitor::Tcpip(monitor) => monitor.set(interface, config)?,
}
Ok(())
}
@@ -53,10 +53,10 @@ impl DnsMonitorT for DnsMonitor {
Ok(Self { current_monitor })
}
- fn set(&mut self, interface: &str, servers: &[std::net::IpAddr]) -> Result<(), Self::Error> {
- let result = self.current_monitor.set(interface, servers);
+ fn set(&mut self, interface: &str, config: ResolvedDnsConfig) -> Result<(), Self::Error> {
+ let result = self.current_monitor.set(interface, config.clone());
if self.fallback_due_to_dnscache(&result) {
- return self.set(interface, servers);
+ return self.set(interface, config);
}
result
}
diff --git a/talpid-core/src/dns/windows/iphlpapi.rs b/talpid-core/src/dns/windows/iphlpapi.rs
index 994b888d90..eeb4b3560c 100644
--- a/talpid-core/src/dns/windows/iphlpapi.rs
+++ b/talpid-core/src/dns/windows/iphlpapi.rs
@@ -3,7 +3,7 @@
//! it requires at least Windows 10, build 19041. For that reason, use run-time linking and fall
//! back on other methods if it is not available.
-use crate::dns::DnsMonitorT;
+use crate::dns::{DnsMonitorT, ResolvedDnsConfig};
use once_cell::sync::OnceCell;
use std::{
ffi::OsString,
@@ -122,7 +122,8 @@ impl DnsMonitorT for DnsMonitor {
Ok(DnsMonitor { current_guid: None })
}
- fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
+ fn set(&mut self, interface: &str, config: ResolvedDnsConfig) -> Result<(), Error> {
+ let servers = config.tunnel_config();
let guid = guid_from_luid(&luid_from_alias(interface).map_err(Error::ObtainInterfaceLuid)?)
.map_err(Error::ObtainInterfaceGuid)?;
diff --git a/talpid-core/src/dns/windows/mod.rs b/talpid-core/src/dns/windows/mod.rs
index 889f8b4126..138f75c35f 100644
--- a/talpid-core/src/dns/windows/mod.rs
+++ b/talpid-core/src/dns/windows/mod.rs
@@ -1,6 +1,6 @@
-use std::{env, fmt, net::IpAddr};
+use std::{env, fmt};
-use super::DnsMonitorT;
+use super::{DnsMonitorT, ResolvedDnsConfig};
mod auto;
mod dnsapi;
@@ -46,12 +46,12 @@ impl DnsMonitorT for DnsMonitor {
Ok(DnsMonitor { inner })
}
- fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
+ fn set(&mut self, interface: &str, config: ResolvedDnsConfig) -> Result<(), Error> {
match self.inner {
- DnsMonitorHolder::Auto(ref mut inner) => inner.set(interface, servers)?,
- DnsMonitorHolder::Iphlpapi(ref mut inner) => inner.set(interface, servers)?,
- DnsMonitorHolder::Netsh(ref mut inner) => inner.set(interface, servers)?,
- DnsMonitorHolder::Tcpip(ref mut inner) => inner.set(interface, servers)?,
+ DnsMonitorHolder::Auto(ref mut inner) => inner.set(interface, config)?,
+ DnsMonitorHolder::Iphlpapi(ref mut inner) => inner.set(interface, config)?,
+ DnsMonitorHolder::Netsh(ref mut inner) => inner.set(interface, config)?,
+ DnsMonitorHolder::Tcpip(ref mut inner) => inner.set(interface, config)?,
}
Ok(())
}
diff --git a/talpid-core/src/dns/windows/netsh.rs b/talpid-core/src/dns/windows/netsh.rs
index 25a11b826a..b731310056 100644
--- a/talpid-core/src/dns/windows/netsh.rs
+++ b/talpid-core/src/dns/windows/netsh.rs
@@ -1,4 +1,4 @@
-use crate::dns::DnsMonitorT;
+use crate::dns::{DnsMonitorT, ResolvedDnsConfig};
use std::{
ffi::OsString,
io::{self, Write},
@@ -69,7 +69,8 @@ impl DnsMonitorT for DnsMonitor {
})
}
- fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
+ fn set(&mut self, interface: &str, config: ResolvedDnsConfig) -> Result<(), Error> {
+ let servers = config.tunnel_config();
let interface_luid = luid_from_alias(interface).map_err(Error::ObtainInterfaceLuid)?;
let interface_index =
index_from_luid(&interface_luid).map_err(Error::ObtainInterfaceIndex)?;
diff --git a/talpid-core/src/dns/windows/tcpip.rs b/talpid-core/src/dns/windows/tcpip.rs
index a4c3ca8d76..70bb4660d6 100644
--- a/talpid-core/src/dns/windows/tcpip.rs
+++ b/talpid-core/src/dns/windows/tcpip.rs
@@ -1,4 +1,4 @@
-use crate::dns::DnsMonitorT;
+use crate::dns::{DnsMonitorT, ResolvedDnsConfig};
use std::{io, net::IpAddr};
use talpid_types::ErrorExt;
use talpid_windows::net::{guid_from_luid, luid_from_alias};
@@ -44,7 +44,9 @@ impl DnsMonitorT for DnsMonitor {
})
}
- fn set(&mut self, interface: &str, servers: &[IpAddr]) -> Result<(), Error> {
+ fn set(&mut self, interface: &str, config: ResolvedDnsConfig) -> Result<(), Error> {
+ let servers = config.tunnel_config();
+
let guid = guid_from_luid(&luid_from_alias(interface).map_err(Error::ObtainInterfaceLuid)?)
.map_err(Error::ObtainInterfaceGuid)?;
set_dns(&guid, servers)?;
diff --git a/talpid-core/src/firewall/linux.rs b/talpid-core/src/firewall/linux.rs
index 5f3489649a..71a13ad914 100644
--- a/talpid-core/src/firewall/linux.rs
+++ b/talpid-core/src/firewall/linux.rs
@@ -293,15 +293,10 @@ impl<'a> PolicyBatch<'a> {
fn add_split_tunneling_rules(&mut self, policy: &FirewallPolicy, fwmark: u32) -> Result<()> {
// Send select DNS requests in the tunnel
if let FirewallPolicy::Connected {
- tunnel,
- dns_servers,
- ..
+ tunnel, dns_config, ..
} = policy
{
- for server in dns_servers
- .iter()
- .filter(|server| !is_local_dns_address(tunnel, server))
- {
+ for server in dns_config.tunnel_config() {
let allow_rule = allow_tunnel_dns_rule(
&self.mangle_chain,
&tunnel.interface,
@@ -559,11 +554,34 @@ impl<'a> PolicyBatch<'a> {
peer_endpoint,
tunnel,
allow_lan,
- dns_servers,
+ dns_config,
} => {
self.add_allow_tunnel_endpoint_rules(peer_endpoint, fwmark);
- self.add_allow_dns_rules(tunnel, dns_servers, TransportProtocol::Udp)?;
- self.add_allow_dns_rules(tunnel, dns_servers, TransportProtocol::Tcp)?;
+
+ for server in dns_config.tunnel_config() {
+ self.add_allow_tunnel_dns_rule(
+ &tunnel.interface,
+ TransportProtocol::Udp,
+ *server,
+ )?;
+ self.add_allow_tunnel_dns_rule(
+ &tunnel.interface,
+ TransportProtocol::Tcp,
+ *server,
+ )?;
+ }
+ for server in dns_config.non_tunnel_config() {
+ self.add_allow_local_dns_rule(
+ &tunnel.interface,
+ TransportProtocol::Udp,
+ *server,
+ )?;
+ self.add_allow_local_dns_rule(
+ &tunnel.interface,
+ TransportProtocol::Tcp,
+ *server,
+ )?;
+ }
// Important to block DNS *before* we allow the tunnel and allow LAN. So DNS
// can't leak to the wrong IPs in the tunnel or on the LAN.
@@ -685,27 +703,6 @@ impl<'a> PolicyBatch<'a> {
self.batch.add(&out_rule, nftnl::MsgType::Add);
}
- fn add_allow_dns_rules(
- &mut self,
- tunnel: &tunnel::TunnelMetadata,
- dns_servers: &[IpAddr],
- protocol: TransportProtocol,
- ) -> Result<()> {
- let (local_resolvers, remote_resolvers): (Vec<IpAddr>, Vec<IpAddr>) = dns_servers
- .iter()
- .partition(|server| is_local_dns_address(tunnel, server));
-
- for resolver in &local_resolvers {
- self.add_allow_local_dns_rule(&tunnel.interface, protocol, *resolver)?;
- }
-
- for resolver in &remote_resolvers {
- self.add_allow_tunnel_dns_rule(&tunnel.interface, protocol, *resolver)?;
- }
-
- Ok(())
- }
-
fn add_allow_tunnel_dns_rule(
&mut self,
interface: &str,
@@ -895,12 +892,6 @@ impl<'a> PolicyBatch<'a> {
}
}
-fn is_local_dns_address(tunnel: &tunnel::TunnelMetadata, server: &IpAddr) -> bool {
- super::is_local_address(server)
- && server != &tunnel.ipv4_gateway
- && Some(server) != tunnel.ipv6_gateway.map(IpAddr::from).as_ref()
-}
-
fn allow_tunnel_dns_rule<'a>(
chain: &'a Chain<'_>,
iface: &str,
diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs
index 60cecf11de..78f72ab647 100644
--- a/talpid-core/src/firewall/macos.rs
+++ b/talpid-core/src/firewall/macos.rs
@@ -244,13 +244,20 @@ impl Firewall {
peer_endpoint,
tunnel,
allow_lan,
- dns_servers,
+ dns_config,
redirect_interface,
} => {
let mut rules = vec![];
- for server in dns_servers.iter() {
- rules.append(&mut self.get_allow_dns_rules_when_connected(tunnel, *server)?);
+ for server in dns_config.tunnel_config() {
+ rules.append(
+ &mut self.get_allow_tunnel_dns_rules_when_connected(tunnel, *server)?,
+ );
+ }
+ for server in dns_config.non_tunnel_config() {
+ rules.append(
+ &mut self.get_allow_local_dns_rules_when_connected(tunnel, *server)?,
+ );
}
rules.push(self.get_allow_relay_rule(peer_endpoint)?);
@@ -299,86 +306,87 @@ impl Firewall {
}
}
- fn get_allow_dns_rules_when_connected(
+ fn get_allow_local_dns_rules_when_connected(
&self,
tunnel: &crate::tunnel::TunnelMetadata,
server: IpAddr,
) -> Result<Vec<pfctl::FilterRule>> {
let mut rules = Vec::with_capacity(4);
- let is_local = super::is_local_address(&server)
- && server != tunnel.ipv4_gateway
- && !tunnel
- .ipv6_gateway
- .map(|ref gateway| &server == gateway)
- .unwrap_or(false);
+ // Block requests on the tunnel interface
+ let block_tunnel_tcp = self
+ .create_rule_builder(FilterRuleAction::Drop(DropAction::Return))
+ .direction(pfctl::Direction::Out)
+ .quick(true)
+ .interface(&tunnel.interface)
+ .proto(pfctl::Proto::Tcp)
+ .keep_state(pfctl::StatePolicy::None)
+ .to(pfctl::Endpoint::new(server, 53))
+ .build()?;
+ rules.push(block_tunnel_tcp);
+ let block_tunnel_udp = self
+ .create_rule_builder(FilterRuleAction::Drop(DropAction::Return))
+ .direction(pfctl::Direction::Out)
+ .quick(true)
+ .interface(&tunnel.interface)
+ .proto(pfctl::Proto::Udp)
+ .keep_state(pfctl::StatePolicy::None)
+ .to(pfctl::Endpoint::new(server, 53))
+ .build()?;
+ rules.push(block_tunnel_udp);
- if is_local {
- // Block requests on the tunnel interface
- let block_tunnel_tcp = self
- .create_rule_builder(FilterRuleAction::Drop(DropAction::Return))
- .direction(pfctl::Direction::Out)
- .quick(true)
- .interface(&tunnel.interface)
- .proto(pfctl::Proto::Tcp)
- .keep_state(pfctl::StatePolicy::None)
- .to(pfctl::Endpoint::new(server, 53))
- .build()?;
- rules.push(block_tunnel_tcp);
- let block_tunnel_udp = self
- .create_rule_builder(FilterRuleAction::Drop(DropAction::Return))
- .direction(pfctl::Direction::Out)
- .quick(true)
- .interface(&tunnel.interface)
- .proto(pfctl::Proto::Udp)
- .keep_state(pfctl::StatePolicy::None)
- .to(pfctl::Endpoint::new(server, 53))
- .build()?;
- rules.push(block_tunnel_udp);
+ // Allow requests on other interfaces
+ let allow_nontunnel_tcp = self
+ .create_rule_builder(FilterRuleAction::Pass)
+ .direction(pfctl::Direction::Out)
+ .quick(true)
+ .proto(pfctl::Proto::Tcp)
+ .keep_state(pfctl::StatePolicy::Keep)
+ .tcp_flags(Self::get_tcp_flags())
+ .to(pfctl::Endpoint::new(server, 53))
+ .build()?;
+ rules.push(allow_nontunnel_tcp);
+ let allow_nontunnel_udp = self
+ .create_rule_builder(FilterRuleAction::Pass)
+ .direction(pfctl::Direction::Out)
+ .quick(true)
+ .proto(pfctl::Proto::Udp)
+ .keep_state(pfctl::StatePolicy::Keep)
+ .to(pfctl::Endpoint::new(server, 53))
+ .build()?;
+ rules.push(allow_nontunnel_udp);
- // Allow requests on other interfaces
- let allow_nontunnel_tcp = self
- .create_rule_builder(FilterRuleAction::Pass)
- .direction(pfctl::Direction::Out)
- .quick(true)
- .proto(pfctl::Proto::Tcp)
- .keep_state(pfctl::StatePolicy::Keep)
- .tcp_flags(Self::get_tcp_flags())
- .to(pfctl::Endpoint::new(server, 53))
- .build()?;
- rules.push(allow_nontunnel_tcp);
- let allow_nontunnel_udp = self
- .create_rule_builder(FilterRuleAction::Pass)
- .direction(pfctl::Direction::Out)
- .quick(true)
- .proto(pfctl::Proto::Udp)
- .keep_state(pfctl::StatePolicy::Keep)
- .to(pfctl::Endpoint::new(server, 53))
- .build()?;
- rules.push(allow_nontunnel_udp);
- } else {
- // Allow outgoing requests on the tunnel interface only
- let allow_tunnel_tcp = self
- .create_rule_builder(FilterRuleAction::Pass)
- .direction(pfctl::Direction::Out)
- .quick(true)
- .interface(&tunnel.interface)
- .proto(pfctl::Proto::Tcp)
- .keep_state(pfctl::StatePolicy::Keep)
- .tcp_flags(Self::get_tcp_flags())
- .to(pfctl::Endpoint::new(server, 53))
- .build()?;
- rules.push(allow_tunnel_tcp);
- let allow_tunnel_udp = self
- .create_rule_builder(FilterRuleAction::Pass)
- .direction(pfctl::Direction::Out)
- .quick(true)
- .interface(&tunnel.interface)
- .proto(pfctl::Proto::Udp)
- .to(pfctl::Endpoint::new(server, 53))
- .build()?;
- rules.push(allow_tunnel_udp);
- };
+ Ok(rules)
+ }
+
+ fn get_allow_tunnel_dns_rules_when_connected(
+ &self,
+ tunnel: &crate::tunnel::TunnelMetadata,
+ server: IpAddr,
+ ) -> Result<Vec<pfctl::FilterRule>> {
+ let mut rules = Vec::with_capacity(4);
+
+ // Allow outgoing requests on the tunnel interface only
+ let allow_tunnel_tcp = self
+ .create_rule_builder(FilterRuleAction::Pass)
+ .direction(pfctl::Direction::Out)
+ .quick(true)
+ .interface(&tunnel.interface)
+ .proto(pfctl::Proto::Tcp)
+ .keep_state(pfctl::StatePolicy::Keep)
+ .tcp_flags(Self::get_tcp_flags())
+ .to(pfctl::Endpoint::new(server, 53))
+ .build()?;
+ rules.push(allow_tunnel_tcp);
+ let allow_tunnel_udp = self
+ .create_rule_builder(FilterRuleAction::Pass)
+ .direction(pfctl::Direction::Out)
+ .quick(true)
+ .interface(&tunnel.interface)
+ .proto(pfctl::Proto::Udp)
+ .to(pfctl::Endpoint::new(server, 53))
+ .build()?;
+ rules.push(allow_tunnel_udp);
Ok(rules)
}
diff --git a/talpid-core/src/firewall/mod.rs b/talpid-core/src/firewall/mod.rs
index c9c351bb59..8f36e905ff 100644
--- a/talpid-core/src/firewall/mod.rs
+++ b/talpid-core/src/firewall/mod.rs
@@ -1,3 +1,5 @@
+#[cfg(not(target_os = "android"))]
+use crate::dns::ResolvedDnsConfig;
use ipnetwork::{IpNetwork, Ipv4Network, Ipv6Network};
use std::{
fmt,
@@ -104,7 +106,7 @@ pub enum FirewallPolicy {
allow_lan: bool,
/// Servers that are allowed to respond to DNS requests.
#[cfg(not(target_os = "android"))]
- dns_servers: Vec<IpAddr>,
+ dns_config: ResolvedDnsConfig,
/// Interface to redirect (VPN tunnel) traffic to
#[cfg(target_os = "macos")]
redirect_interface: Option<String>,
diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs
index eda06ce5fd..0ee5a81ca2 100644
--- a/talpid-core/src/firewall/windows.rs
+++ b/talpid-core/src/firewall/windows.rs
@@ -1,4 +1,4 @@
-use crate::tunnel::TunnelMetadata;
+use crate::{dns::ResolvedDnsConfig, tunnel::TunnelMetadata};
use std::{ffi::CStr, io, net::IpAddr, ptr};
@@ -113,10 +113,10 @@ impl Firewall {
peer_endpoint,
tunnel,
allow_lan,
- dns_servers,
+ dns_config,
} => {
let cfg = &WinFwSettings::new(allow_lan);
- self.set_connected_state(&peer_endpoint, cfg, &tunnel, &dns_servers)
+ self.set_connected_state(&peer_endpoint, cfg, &tunnel, &dns_config)
}
FirewallPolicy::Blocked {
allow_lan,
@@ -250,14 +250,10 @@ impl Firewall {
endpoint: &AllowedEndpoint,
winfw_settings: &WinFwSettings,
tunnel_metadata: &TunnelMetadata,
- dns_servers: &[IpAddr],
+ dns_config: &ResolvedDnsConfig,
) -> Result<(), Error> {
log::trace!("Applying 'connected' firewall policy");
let ip_str = widestring_ip(endpoint.endpoint.address.ip());
- let v4_gateway = widestring_ip(tunnel_metadata.ipv4_gateway.into());
- let v6_gateway = tunnel_metadata
- .ipv6_gateway
- .map(|v6_ip| widestring_ip(v6_ip.into()));
let tunnel_alias = WideCString::from_str_truncate(&tunnel_metadata.interface);
@@ -268,11 +264,6 @@ impl Firewall {
protocol: WinFwProt::from(endpoint.endpoint.protocol),
};
- let v6_gateway_ptr = match &v6_gateway {
- Some(v6_ip) => v6_ip.as_ptr(),
- None => ptr::null(),
- };
-
// SAFETY: `relay_client_wstrs` must not be dropped until `WinFw_ApplyPolicyConnected` has
// returned.
let relay_client_wstrs: Vec<_> = endpoint
@@ -286,9 +277,24 @@ impl Firewall {
.collect();
let relay_client_wstr_ptrs_len = relay_client_wstr_ptrs.len();
- let dns_servers: Vec<WideCString> =
- dns_servers.iter().cloned().map(widestring_ip).collect();
- let dns_servers: Vec<*const u16> = dns_servers.iter().map(|ip| ip.as_ptr()).collect();
+ let tunnel_dns_servers: Vec<WideCString> = dns_config
+ .tunnel_config()
+ .iter()
+ .cloned()
+ .map(widestring_ip)
+ .collect();
+ let tunnel_dns_servers: Vec<*const u16> =
+ tunnel_dns_servers.iter().map(|ip| ip.as_ptr()).collect();
+ let non_tunnel_dns_servers: Vec<WideCString> = dns_config
+ .non_tunnel_config()
+ .iter()
+ .cloned()
+ .map(widestring_ip)
+ .collect();
+ let non_tunnel_dns_servers: Vec<*const u16> = non_tunnel_dns_servers
+ .iter()
+ .map(|ip| ip.as_ptr())
+ .collect();
let result = unsafe {
WinFw_ApplyPolicyConnected(
@@ -297,10 +303,10 @@ impl Firewall {
relay_client_wstr_ptrs.as_ptr(),
relay_client_wstr_ptrs_len,
tunnel_alias.as_ptr(),
- v4_gateway.as_ptr(),
- v6_gateway_ptr,
- dns_servers.as_ptr(),
- dns_servers.len(),
+ tunnel_dns_servers.as_ptr(),
+ tunnel_dns_servers.len(),
+ non_tunnel_dns_servers.as_ptr(),
+ non_tunnel_dns_servers.len(),
)
.into_result()
.map_err(Error::ApplyingConnectedPolicy)
@@ -635,10 +641,10 @@ mod winfw {
relayClient: *const *const libc::wchar_t,
relayClientLen: usize,
tunnelIfaceAlias: *const libc::wchar_t,
- v4Gateway: *const libc::wchar_t,
- v6Gateway: *const libc::wchar_t,
- dnsServers: *const *const libc::wchar_t,
- numDnsServers: usize,
+ tunnelDnsServers: *const *const libc::wchar_t,
+ numTunnelDnsServers: usize,
+ nonTunnelDnsServers: *const *const libc::wchar_t,
+ numNonTunnelDnsServers: usize,
) -> WinFwPolicyStatus;
#[link_name = "WinFw_ApplyPolicyBlocked"]
diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs
index c7f480ed6d..a5ca3395a4 100644
--- a/talpid-core/src/tunnel_state_machine/connected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connected_state.rs
@@ -4,6 +4,7 @@ use super::{
TunnelStateTransition,
};
use crate::{
+ dns::ResolvedDnsConfig,
firewall::FirewallPolicy,
tunnel::{TunnelEvent, TunnelMetadata},
};
@@ -12,7 +13,6 @@ use futures::{
stream::Fuse,
StreamExt,
};
-use std::net::IpAddr;
use talpid_types::{
net::{AllowedClients, AllowedEndpoint, TunnelParameters},
tunnel::{ErrorStateCause, FirewallPolicyError},
@@ -106,13 +106,6 @@ impl ConnectedState {
})
}
- fn get_dns_servers(&self, shared_values: &SharedTunnelStateValues) -> Vec<IpAddr> {
- shared_values
- .dns_servers
- .clone()
- .unwrap_or_else(|| self.metadata.gateways())
- }
-
fn get_firewall_policy(&self, shared_values: &SharedTunnelStateValues) -> FirewallPolicy {
let endpoint = self.tunnel_parameters.get_next_hop_endpoint();
@@ -146,28 +139,25 @@ impl ConnectedState {
tunnel: self.metadata.clone(),
allow_lan: shared_values.allow_lan,
#[cfg(not(target_os = "android"))]
- dns_servers: self.get_dns_servers(shared_values),
+ dns_config: Self::resolve_dns(&self.metadata, shared_values),
#[cfg(target_os = "macos")]
redirect_interface,
}
}
- fn set_dns(&self, shared_values: &mut SharedTunnelStateValues) -> Result<(), BoxedError> {
- let dns_ips = self.get_dns_servers(shared_values);
+ fn resolve_dns(
+ metadata: &TunnelMetadata,
+ shared_values: &SharedTunnelStateValues,
+ ) -> ResolvedDnsConfig {
+ shared_values.dns_config.resolve(&metadata.gateways())
+ }
- #[cfg(any(target_os = "linux", target_os = "windows"))]
- let dns_ips = dns_ips
- .into_iter()
- .filter(|ip| {
- !crate::firewall::is_local_address(ip)
- || IpAddr::V4(self.metadata.ipv4_gateway) == *ip
- || self.metadata.ipv6_gateway.map(IpAddr::V6) == Some(*ip)
- })
- .collect::<Vec<_>>();
+ fn set_dns(&self, shared_values: &mut SharedTunnelStateValues) -> Result<(), BoxedError> {
+ let dns_config = Self::resolve_dns(&self.metadata, shared_values);
shared_values
.dns_monitor
- .set(&self.metadata.interface, &dns_ips)
+ .set(&self.metadata.interface, dns_config)
.map_err(BoxedError::new)?;
Ok(())
@@ -259,7 +249,7 @@ impl ConnectedState {
SameState(self)
}
Some(TunnelCommand::Dns(servers, complete_tx)) => {
- let consequence = if shared_values.set_dns_servers(servers) {
+ let consequence = if shared_values.set_dns_config(servers) {
#[cfg(target_os = "android")]
{
if let Err(_err) = shared_values.restart_tunnel(false) {
diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs
index d9a58a0ea7..09ba4f18e1 100644
--- a/talpid-core/src/tunnel_state_machine/connecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs
@@ -439,7 +439,7 @@ impl ConnectingState {
SameState(self)
}
Some(TunnelCommand::Dns(servers, complete_tx)) => {
- let consequence = if shared_values.set_dns_servers(servers) {
+ let consequence = if shared_values.set_dns_config(servers) {
#[cfg(target_os = "android")]
{
if let Err(_err) = shared_values.restart_tunnel(false) {
diff --git a/talpid-core/src/tunnel_state_machine/disconnected_state.rs b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
index 74cd3b65c4..9da6520f61 100644
--- a/talpid-core/src/tunnel_state_machine/disconnected_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnected_state.rs
@@ -131,9 +131,10 @@ impl DisconnectedState {
fn setup_local_dns_config(
shared_values: &mut SharedTunnelStateValues,
) -> Result<(), dns::Error> {
- shared_values
- .dns_monitor
- .set("lo", &[Ipv4Addr::LOCALHOST.into()])
+ shared_values.dns_monitor.set(
+ "lo",
+ dns::DnsConfig::default().resolve(&[Ipv4Addr::LOCALHOST.into()]),
+ )
}
}
@@ -164,7 +165,7 @@ impl TunnelState for DisconnectedState {
}
Some(TunnelCommand::Dns(servers, complete_tx)) => {
// Same situation as allow LAN above.
- shared_values.set_dns_servers(servers);
+ shared_values.set_dns_config(servers);
let _ = complete_tx.send(());
SameState(self)
}
diff --git a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
index ddcb3cebd2..4a108788e1 100644
--- a/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
+++ b/talpid-core/src/tunnel_state_machine/disconnecting_state.rs
@@ -51,7 +51,7 @@ impl DisconnectingState {
AfterDisconnect::Nothing
}
Some(TunnelCommand::Dns(servers, complete_tx)) => {
- let _ = shared_values.set_dns_servers(servers);
+ let _ = shared_values.set_dns_config(servers);
let _ = complete_tx.send(());
AfterDisconnect::Nothing
}
@@ -105,7 +105,7 @@ impl DisconnectingState {
AfterDisconnect::Block(reason)
}
Some(TunnelCommand::Dns(servers, complete_tx)) => {
- let _ = shared_values.set_dns_servers(servers);
+ let _ = shared_values.set_dns_config(servers);
let _ = complete_tx.send(());
AfterDisconnect::Block(reason)
}
@@ -163,7 +163,7 @@ impl DisconnectingState {
AfterDisconnect::Reconnect(retry_attempt)
}
Some(TunnelCommand::Dns(servers, complete_tx)) => {
- let _ = shared_values.set_dns_servers(servers);
+ let _ = shared_values.set_dns_config(servers);
let _ = complete_tx.send(());
AfterDisconnect::Reconnect(retry_attempt)
}
diff --git a/talpid-core/src/tunnel_state_machine/error_state.rs b/talpid-core/src/tunnel_state_machine/error_state.rs
index 99f8dc17c4..eeaf48956b 100644
--- a/talpid-core/src/tunnel_state_machine/error_state.rs
+++ b/talpid-core/src/tunnel_state_machine/error_state.rs
@@ -2,6 +2,8 @@ use super::{
ConnectingState, DisconnectedState, EventConsequence, SharedTunnelStateValues, TunnelCommand,
TunnelCommandReceiver, TunnelState, TunnelStateTransition,
};
+#[cfg(target_os = "macos")]
+use crate::dns::DnsConfig;
use crate::firewall::FirewallPolicy;
use futures::StreamExt;
#[cfg(target_os = "macos")]
@@ -33,10 +35,10 @@ impl ErrorState {
#[cfg(target_os = "macos")]
if !block_reason.prevents_filtering_resolver() {
- if let Err(err) = shared_values
- .dns_monitor
- .set("lo", &[Ipv4Addr::LOCALHOST.into()])
- {
+ if let Err(err) = shared_values.dns_monitor.set(
+ "lo",
+ DnsConfig::default().resolve(&[Ipv4Addr::LOCALHOST.into()]),
+ ) {
log::error!(
"{}",
err.display_chain_with_msg(
@@ -158,7 +160,7 @@ impl TunnelState for ErrorState {
SameState(self)
}
Some(TunnelCommand::Dns(servers, complete_tx)) => {
- let consequence = if shared_values.set_dns_servers(servers) {
+ let consequence = if shared_values.set_dns_config(servers) {
#[cfg(target_os = "android")]
{
// DNS is blocked in the error state, so only update tun config
diff --git a/talpid-core/src/tunnel_state_machine/mod.rs b/talpid-core/src/tunnel_state_machine/mod.rs
index 1deb226e04..e0ef07850d 100644
--- a/talpid-core/src/tunnel_state_machine/mod.rs
+++ b/talpid-core/src/tunnel_state_machine/mod.rs
@@ -14,7 +14,7 @@ use self::{
#[cfg(any(windows, target_os = "android", target_os = "macos"))]
use crate::split_tunnel;
use crate::{
- dns::DnsMonitor,
+ dns::{DnsConfig, DnsMonitor},
firewall::{Firewall, FirewallArguments, InitialFirewallState},
mpsc::Sender,
offline,
@@ -37,7 +37,6 @@ use std::os::unix::io::RawFd;
use std::{
future::Future,
io,
- net::IpAddr,
path::PathBuf,
pin::Pin,
sync::{Arc, Mutex},
@@ -92,8 +91,8 @@ pub struct InitialTunnelState {
pub allow_lan: bool,
/// Block traffic unless connected to the VPN.
pub block_when_disconnected: bool,
- /// DNS servers to use. If `None`, the tunnel gateway is used.
- pub dns_servers: Option<Vec<IpAddr>>,
+ /// DNS configuration to use
+ pub dns_config: DnsConfig,
/// A single endpoint that is allowed to communicate outside the tunnel, i.e.
/// in any of the blocking states.
pub allowed_endpoint: AllowedEndpoint,
@@ -188,8 +187,8 @@ pub enum TunnelCommand {
/// channel after attempting to set the firewall policy, regardless
/// of whether it succeeded.
AllowEndpoint(AllowedEndpoint, oneshot::Sender<()>),
- /// Set DNS servers to use.
- Dns(Option<Vec<IpAddr>>, oneshot::Sender<()>),
+ /// Set DNS configuration to use.
+ Dns(crate::dns::DnsConfig, oneshot::Sender<()>),
/// Enable or disable the block_when_disconnected feature.
BlockWhenDisconnected(bool, oneshot::Sender<()>),
/// Notify the state machine of the connectivity of the device.
@@ -374,7 +373,7 @@ impl TunnelStateMachine {
allow_lan: args.settings.allow_lan,
block_when_disconnected: args.settings.block_when_disconnected,
connectivity,
- dns_servers: args.settings.dns_servers,
+ dns_config: args.settings.dns_config,
allowed_endpoint: args.settings.allowed_endpoint,
tunnel_parameters_generator: Box::new(args.tunnel_parameters_generator),
tun_provider: Arc::new(Mutex::new(args.tun_provider)),
@@ -467,8 +466,8 @@ struct SharedTunnelStateValues {
block_when_disconnected: bool,
/// True when the computer is known to be offline.
connectivity: Connectivity,
- /// DNS servers to use (overriding default).
- dns_servers: Option<Vec<IpAddr>>,
+ /// DNS configuration to use.
+ dns_config: crate::dns::DnsConfig,
/// Endpoint that should not be blocked by the firewall.
allowed_endpoint: AllowedEndpoint,
/// The generator of new `TunnelParameter`s
@@ -514,6 +513,8 @@ impl SharedTunnelStateValues {
&mut self,
metadata: &TunnelMetadata,
) -> Result<(), ErrorStateCause> {
+ use std::net::IpAddr;
+
let v4_address = metadata
.ips
.iter()
@@ -555,9 +556,9 @@ impl SharedTunnelStateValues {
}
}
- pub fn set_dns_servers(&mut self, dns_servers: Option<Vec<IpAddr>>) -> bool {
- if self.dns_servers != dns_servers {
- self.dns_servers = dns_servers;
+ pub fn set_dns_config(&mut self, dns_config: DnsConfig) -> bool {
+ if self.dns_config != dns_config {
+ self.dns_config = dns_config;
true
} else {
false
@@ -627,7 +628,8 @@ impl SharedTunnelStateValues {
if blocking {
config.dns_servers = Some(vec![]);
} else {
- config.dns_servers = self.dns_servers.clone();
+ let addrs: Vec<_> = self.dns_config.resolve(&[]).addresses().collect();
+ config.dns_servers = if addrs.is_empty() { None } else { Some(addrs) };
}
config.allow_lan = self.allow_lan;
config.excluded_packages = self.excluded_packages.clone();
diff --git a/windows/winfw/src/winfw/winfw.cpp b/windows/winfw/src/winfw/winfw.cpp
index 3d56eb6620..c862de4b5a 100644
--- a/windows/winfw/src/winfw/winfw.cpp
+++ b/windows/winfw/src/winfw/winfw.cpp
@@ -47,23 +47,6 @@ std::optional<T> MakeOptional(T* object)
return std::make_optional(*object);
}
-//
-// Networks for which DNS requests can be made on all network adapters.
-//
-// This should be synchronized with `ALLOWED_LAN_NETS` in talpid-types,
-// but it also includes loopback addresses.
-//
-wfp::IpNetwork g_privateIpRanges[] = {
- wfp::IpNetwork(wfp::IpAddress::Literal{127, 0, 0, 0}, 8),
- wfp::IpNetwork(wfp::IpAddress::Literal{10, 0, 0, 0}, 8),
- wfp::IpNetwork(wfp::IpAddress::Literal{172, 16, 0, 0}, 12),
- wfp::IpNetwork(wfp::IpAddress::Literal{192, 168, 0, 0}, 16),
- wfp::IpNetwork(wfp::IpAddress::Literal{169, 254, 0, 0}, 16),
- wfp::IpNetwork(wfp::IpAddress::Literal6{0, 0, 0, 0, 0, 0, 0, 1}, 128),
- wfp::IpNetwork(wfp::IpAddress::Literal6{0xfe80, 0, 0, 0, 0, 0, 0, 0}, 10),
- wfp::IpNetwork(wfp::IpAddress::Literal6{0xfc80, 0, 0, 0, 0, 0, 0, 0}, 7)
-};
-
} // anonymous namespace
WINFW_LINKAGE
@@ -303,10 +286,10 @@ WinFw_ApplyPolicyConnected(
const wchar_t **relayClients,
size_t relayClientsLen,
const wchar_t *tunnelInterfaceAlias,
- const wchar_t *v4Gateway,
- const wchar_t *v6Gateway,
- const wchar_t * const *dnsServers,
- size_t numDnsServers
+ const wchar_t * const *tunnelDnsServers,
+ size_t numTunnelDnsServers,
+ const wchar_t * const *nonTunnelDnsServers,
+ size_t numNonTunnelDnsServers
)
{
if (nullptr == g_fwContext)
@@ -331,76 +314,51 @@ WinFw_ApplyPolicyConnected(
THROW_ERROR("Invalid argument: tunnelInterfaceAlias");
}
- if (nullptr == v4Gateway)
+ if (nullptr == tunnelDnsServers)
{
- THROW_ERROR("Invalid argument: v4Gateway");
+ THROW_ERROR("Invalid argument: tunnelDnsServers");
}
- if (nullptr == dnsServers)
+ if (nullptr == nonTunnelDnsServers)
{
- THROW_ERROR("Invalid argument: dnsServers");
+ THROW_ERROR("Invalid argument: nonTunnelDnsServers");
}
- std::vector<wfp::IpAddress> tunnelDnsServers;
- std::vector<wfp::IpAddress> nonTunnelDnsServers;
-
- const auto v4GatewayIp = wfp::IpAddress(v4Gateway);
- const auto v6GatewayIp = (nullptr != v6Gateway)
- ? std::make_optional(wfp::IpAddress(v6Gateway))
- : std::nullopt;
+ std::vector<wfp::IpAddress> convertedTunnelDnsServers;
+ std::vector<wfp::IpAddress> convertedNonTunnelDnsServers;
- const auto addToDnsCollection = [&](const std::optional<wfp::IpAddress> &gatewayIp, wfp::IpAddress &&ip)
+ for (size_t i = 0; i < numTunnelDnsServers; i++)
{
- if (gatewayIp.has_value() && *gatewayIp == ip)
- {
- // Requests to the gateway IP of the tunnel are only allowed on the tunnel interface.
- tunnelDnsServers.emplace_back(ip);
- return;
- }
-
- for (const auto &network : g_privateIpRanges)
- {
- if (network.includes(ip))
- {
- //
- // Resolvers on the LAN must be accessible outside the tunnel.
- //
-
- nonTunnelDnsServers.emplace_back(ip);
- return;
- }
- }
-
- tunnelDnsServers.emplace_back(ip);
- };
-
- for (size_t i = 0; i < numDnsServers; i++)
+ auto ip = wfp::IpAddress(tunnelDnsServers[i]);
+ convertedTunnelDnsServers.push_back(ip);
+ }
+ for (size_t i = 0; i < numNonTunnelDnsServers; i++)
{
- auto ip = wfp::IpAddress(dnsServers[i]);
- addToDnsCollection(ip.type() == wfp::IpAddress::Type::Ipv4 ? v4GatewayIp : v6GatewayIp, std::move(ip));
+ auto ip = wfp::IpAddress(nonTunnelDnsServers[i]);
+ convertedNonTunnelDnsServers.push_back(ip);
}
if (nullptr != g_logSink)
{
std::stringstream ss;
ss << "Non-tunnel DNS servers: ";
- for (size_t i = 0; i < nonTunnelDnsServers.size(); i++) {
+ for (size_t i = 0; i < convertedNonTunnelDnsServers.size(); i++) {
if (i > 0)
{
ss << ", ";
}
- ss << common::string::ToAnsi(nonTunnelDnsServers[i].toString());
+ ss << common::string::ToAnsi(convertedNonTunnelDnsServers[i].toString());
}
g_logSink(MULLVAD_LOG_LEVEL_DEBUG, ss.str().c_str(), g_logSinkContext);
ss.str(std::string());
ss << "Tunnel DNS servers: ";
- for (size_t i = 0; i < tunnelDnsServers.size(); i++) {
+ for (size_t i = 0; i < convertedTunnelDnsServers.size(); i++) {
if (i > 0)
{
ss << ", ";
}
- ss << common::string::ToAnsi(tunnelDnsServers[i].toString());
+ ss << common::string::ToAnsi(convertedTunnelDnsServers[i].toString());
}
g_logSink(MULLVAD_LOG_LEVEL_DEBUG, ss.str().c_str(), g_logSinkContext);
}
@@ -416,8 +374,8 @@ WinFw_ApplyPolicyConnected(
*relay,
relayClientWstrings,
tunnelInterfaceAlias,
- tunnelDnsServers,
- nonTunnelDnsServers
+ convertedTunnelDnsServers,
+ convertedNonTunnelDnsServers
) ? WINFW_POLICY_STATUS_SUCCESS : WINFW_POLICY_STATUS_GENERAL_FAILURE;
}
catch (common::error::WindowsException &err)
diff --git a/windows/winfw/src/winfw/winfw.h b/windows/winfw/src/winfw/winfw.h
index b786d943d3..ab2a136ceb 100644
--- a/windows/winfw/src/winfw/winfw.h
+++ b/windows/winfw/src/winfw/winfw.h
@@ -178,15 +178,13 @@ WinFw_ApplyPolicyConnecting(
// - What is specified by settings
// - Communication with the relay server
// - Non-DNS traffic inside the VPN tunnel
-// - DNS requests inside the VPN tunnel to any specified remote DNS server
-// - DNS requests outside the VPN tunnel to any specified local DNS servers
+// - DNS requests inside the VPN tunnel to any server in 'tunnelDnsServers'
+// - DNS requests outside the VPN tunnel to any server in 'nonTunnelDnsServers'
//
// Parameters:
//
// tunnelInterfaceAlias:
// Friendly name of VPN tunnel interface
-// dnsServers:
-// Array of string-encoded IP addresses of DNS servers to use
//
extern "C"
WINFW_LINKAGE
@@ -198,10 +196,10 @@ WinFw_ApplyPolicyConnected(
const wchar_t **relayClient,
size_t relayClientLen,
const wchar_t *tunnelInterfaceAlias,
- const wchar_t *v4Gateway,
- const wchar_t *v6Gateway,
- const wchar_t * const *dnsServers,
- size_t numDnsServers
+ const wchar_t * const *tunnelDnsServers,
+ size_t numTunnelDnsServers,
+ const wchar_t * const *nonTunnelDnsServers,
+ size_t numNonTunnelDnsServers
);
//