From beb063780d6f3a261e40ac7940c61c937e2b951d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?David=20L=C3=B6nnhager?= Date: Thu, 5 Sep 2024 19:42:18 +0200 Subject: [PATCH] Keep separate tunnel state machine configs for tunnel and non-tunnel DNS --- mullvad-daemon/src/dns.rs | 77 +++++++-- mullvad-daemon/src/lib.rs | 2 +- talpid-core/src/dns/android.rs | 4 +- talpid-core/src/dns/linux/mod.rs | 5 +- talpid-core/src/dns/macos.rs | 8 +- talpid-core/src/dns/mod.rs | 127 ++++++++++++-- talpid-core/src/dns/windows/auto.rs | 16 +- talpid-core/src/dns/windows/iphlpapi.rs | 5 +- talpid-core/src/dns/windows/mod.rs | 14 +- talpid-core/src/dns/windows/netsh.rs | 5 +- talpid-core/src/dns/windows/tcpip.rs | 6 +- talpid-core/src/firewall/linux.rs | 65 +++---- talpid-core/src/firewall/macos.rs | 160 +++++++++--------- talpid-core/src/firewall/mod.rs | 4 +- talpid-core/src/firewall/windows.rs | 54 +++--- .../tunnel_state_machine/connected_state.rs | 34 ++-- .../tunnel_state_machine/connecting_state.rs | 2 +- .../disconnected_state.rs | 9 +- .../disconnecting_state.rs | 6 +- .../src/tunnel_state_machine/error_state.rs | 12 +- talpid-core/src/tunnel_state_machine/mod.rs | 28 +-- windows/winfw/src/winfw/winfw.cpp | 88 +++------- windows/winfw/src/winfw/winfw.h | 14 +- 23 files changed, 438 insertions(+), 307 deletions(-) diff --git a/mullvad-daemon/src/dns.rs b/mullvad-daemon/src/dns.rs index 7846d7c3ff96..9b0ae9163cfb 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> { +/// 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> { 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 097dcc6bf65d..b0e9ea7d5846 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 6f4e110d3fb0..01acae9eb524 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 3e7f6ac0b847..ad27237cd456 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 = std::result::Result; /// 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 e0535fedf7a0..78f455068859 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 = std::result::Result; /// 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 6f81829dabec..d6fd3334498c 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, + /// 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, + }, +} + +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, + /// 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, +} + +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 { + 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::>() - .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; - 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 c0b04742d443..3e171119560d 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 994b888d901a..eeb4b3560cfd 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 889f8b412653..138f75c35f5f 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 25a11b826aaf..b7313100565c 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 a4c3ca8d76e6..70bb4660d6d2 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 5f3489649a25..71a13ad91429 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, Vec) = 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 60cecf11de06..78f72ab64702 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> { 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); - - 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); + // 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); - } 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); - }; + // 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); + + Ok(rules) + } + + fn get_allow_tunnel_dns_rules_when_connected( + &self, + tunnel: &crate::tunnel::TunnelMetadata, + server: IpAddr, + ) -> Result> { + 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 c9c351bb590d..8f36e905ffec 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, + dns_config: ResolvedDnsConfig, /// Interface to redirect (VPN tunnel) traffic to #[cfg(target_os = "macos")] redirect_interface: Option, diff --git a/talpid-core/src/firewall/windows.rs b/talpid-core/src/firewall/windows.rs index eda06ce5fdf2..0ee5a81ca2ed 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 = - 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 = 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 = 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 c7f480ed6d83..a5ca3395a479 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 { - 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 resolve_dns( + metadata: &TunnelMetadata, + shared_values: &SharedTunnelStateValues, + ) -> ResolvedDnsConfig { + shared_values.dns_config.resolve(&metadata.gateways()) + } + fn set_dns(&self, shared_values: &mut SharedTunnelStateValues) -> Result<(), BoxedError> { - let dns_ips = self.get_dns_servers(shared_values); - - #[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::>(); + 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 d9a58a0ea723..09ba4f18e1d0 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 74cd3b65c47e..9da6520f6115 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 ddcb3cebd25f..4a108788e151 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 99f8dc17c403..eeaf48956b8c 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 1deb226e04ef..e0ef07850d6d 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>, + /// 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>, 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>, + /// 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>) -> 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 3d56eb66205b..c862de4b5aa2 100644 --- a/windows/winfw/src/winfw/winfw.cpp +++ b/windows/winfw/src/winfw/winfw.cpp @@ -47,23 +47,6 @@ std::optional 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 tunnelDnsServers; - std::vector nonTunnelDnsServers; - - const auto v4GatewayIp = wfp::IpAddress(v4Gateway); - const auto v6GatewayIp = (nullptr != v6Gateway) - ? std::make_optional(wfp::IpAddress(v6Gateway)) - : std::nullopt; + std::vector convertedTunnelDnsServers; + std::vector convertedNonTunnelDnsServers; - const auto addToDnsCollection = [&](const std::optional &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 b786d943d399..ab2a136ceb9a 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 ); //