diff options
| author | Linus Färnstrand <linus@mullvad.net> | 2017-09-20 22:23:55 +0200 |
|---|---|---|
| committer | Linus Färnstrand <linus@mullvad.net> | 2017-09-21 18:52:29 +0200 |
| commit | 98ab8d1993c2a2d8c1211996f5ca7ed0002d3e32 (patch) | |
| tree | 82bceaefd28c54a167296332d66cb5c5accce971 /talpid-core/src | |
| parent | 1c6382dc9cbcf277501d7269f762c0a0c9d344b1 (diff) | |
| download | mullvadvpn-98ab8d1993c2a2d8c1211996f5ca7ed0002d3e32.tar.xz mullvadvpn-98ab8d1993c2a2d8c1211996f5ca7ed0002d3e32.zip | |
Upgrade pfctl and add DNS proxy
Diffstat (limited to 'talpid-core/src')
| -rw-r--r-- | talpid-core/src/firewall/macos.rs | 157 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/mod.rs | 2 |
2 files changed, 132 insertions, 27 deletions
diff --git a/talpid-core/src/firewall/macos.rs b/talpid-core/src/firewall/macos.rs index 4b9e05f6b0..70bb2cfb20 100644 --- a/talpid-core/src/firewall/macos.rs +++ b/talpid-core/src/firewall/macos.rs @@ -1,17 +1,27 @@ +extern crate socket_relay; +extern crate tokio_core; + use super::{Firewall, SecurityPolicy}; use pfctl; -use std::net::Ipv4Addr; + +use std::net::{IpAddr, Ipv4Addr, SocketAddr}; +use std::sync::mpsc; +use std::thread; + +use self::socket_relay::udp::{Relay, RelayCloseHandle}; use talpid_types::net; +use tunnel::TunnelMetadata; // alias used to instantiate firewall implementation pub type ConcreteFirewall = PacketFilter; -pub use pfctl::{Error, ErrorKind, Result}; +pub use pfctl::{Error, ErrorKind, Result, ResultExt}; const ANCHOR_NAME: &'static str = "talpid_core"; pub struct PacketFilter { pf: pfctl::PfCtl, pf_was_enabled: Option<bool>, + dns_proxy_close_handle: Option<RelayCloseHandle>, } impl Firewall<Error> for PacketFilter { @@ -19,6 +29,7 @@ impl Firewall<Error> for PacketFilter { Ok(PacketFilter { pf: pfctl::PfCtl::new()?, pf_was_enabled: None, + dns_proxy_close_handle: None, }) } @@ -29,6 +40,7 @@ impl Firewall<Error> for PacketFilter { } fn reset_policy(&mut self) -> Result<()> { + self.stop_dns_proxy(); vec![ self.remove_rules(), self.remove_anchor(), @@ -41,35 +53,71 @@ impl Firewall<Error> for PacketFilter { impl PacketFilter { fn set_rules(&mut self, policy: SecurityPolicy) -> Result<()> { - let drop_all_rule = pfctl::FilterRuleBuilder::default() - .action(pfctl::FilterRuleAction::Drop) - .quick(true) - .build()?; - let allow_dns_rule = pfctl::FilterRuleBuilder::default() - .action(pfctl::FilterRuleAction::Pass) - .direction(pfctl::Direction::Out) - .quick(true) - .to(pfctl::Port::One(53, pfctl::PortUnaryModifier::Equal)) - .keep_state(pfctl::StatePolicy::Keep) - .tcp_flags(Self::get_tcp_flags()) - .build()?; - let mut new_rules = self.get_loopback_rules()?; + let mut new_filter_rules = self.get_loopback_rules()?; + let mut new_redirect_rules = vec![]; match policy { SecurityPolicy::Connecting(relay_endpoint) => { - new_rules.push(Self::get_relay_rule(relay_endpoint)?); + self.stop_dns_proxy(); + new_filter_rules.push(Self::get_relay_rule(relay_endpoint)?); } - SecurityPolicy::Connected(relay_endpoint, tunnel_metadata) => { - new_rules.push(Self::get_relay_rule(relay_endpoint)?); - new_rules.push(Self::get_tunnel_rule(tunnel_metadata.interface)?); + SecurityPolicy::Connected(relay_endpoint, tunnel) => { + let dns_proxy_listen_addr = self.start_dns_proxy(&tunnel)?; + + let allow_dns_to_relay_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::FilterRuleAction::Pass) + .direction(pfctl::Direction::Out) + .quick(true) + .interface(&tunnel.interface) + .proto(pfctl::Proto::Udp) + .to(pfctl::Endpoint::new(tunnel.gateway, 53)) + .build()?; + let reroute_dns_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::FilterRuleAction::Pass) + .direction(pfctl::Direction::Out) + .quick(true) + .route(pfctl::Route::route_to(pfctl::Interface::from("lo0"))) + .proto(pfctl::Proto::Udp) + .to(pfctl::Port::from(53)) + .build()?; + let block_all_other_dns_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::FilterRuleAction::Drop) + .direction(pfctl::Direction::Out) + .quick(true) + .proto(pfctl::Proto::Tcp) + .to(pfctl::Port::from(53)) + .build()?; + + new_filter_rules.push(allow_dns_to_relay_rule); + new_filter_rules.push(reroute_dns_rule); + new_filter_rules.push(block_all_other_dns_rule); + + let dns_redirect_rule = pfctl::RedirectRuleBuilder::default() + .action(pfctl::RedirectRuleAction::Redirect) + .interface("lo0") + .proto(pfctl::Proto::Udp) + .to(pfctl::Port::from(53)) + .redirect_to(dns_proxy_listen_addr) + .build()?; + new_redirect_rules.push(dns_redirect_rule); + + new_filter_rules.push(Self::get_relay_rule(relay_endpoint)?); + new_filter_rules.push(Self::get_tunnel_rule(tunnel.interface.as_str())?); } }; - new_rules.push(allow_dns_rule); - new_rules.append(&mut Self::get_dhcp_rules()?); - new_rules.push(drop_all_rule); + new_filter_rules.append(&mut Self::get_dhcp_rules()?); + + let drop_all_rule = pfctl::FilterRuleBuilder::default() + .action(pfctl::FilterRuleAction::Drop) + .quick(true) + .build()?; + new_filter_rules.push(drop_all_rule); - self.pf.set_rules(ANCHOR_NAME, &new_rules) + let mut anchor_change = pfctl::AnchorChange::new(); + anchor_change.set_filter_rules(new_filter_rules); + anchor_change.set_redirect_rules(new_redirect_rules); + self.pf.set_rules(ANCHOR_NAME, anchor_change) } fn get_relay_rule(relay_endpoint: net::Endpoint) -> Result<pfctl::FilterRule> { @@ -86,7 +134,7 @@ impl PacketFilter { .build() } - fn get_tunnel_rule(tunnel_interface: String) -> Result<pfctl::FilterRule> { + fn get_tunnel_rule(tunnel_interface: &str) -> Result<pfctl::FilterRule> { pfctl::FilterRuleBuilder::default() .action(pfctl::FilterRuleAction::Pass) .interface(tunnel_interface) @@ -158,12 +206,29 @@ impl PacketFilter { fn add_anchor(&mut self) -> Result<()> { self.pf - .try_add_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter) + .try_add_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter)?; + self.pf + .try_add_anchor(ANCHOR_NAME, pfctl::AnchorKind::Redirect) } fn remove_anchor(&mut self) -> Result<()> { self.pf - .try_remove_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter) + .try_remove_anchor(ANCHOR_NAME, pfctl::AnchorKind::Filter)?; + self.pf + .try_remove_anchor(ANCHOR_NAME, pfctl::AnchorKind::Redirect) + } + + fn start_dns_proxy(&mut self, tunnel: &TunnelMetadata) -> Result<SocketAddr> { + self.stop_dns_proxy(); + let (listen_addr, close_handle) = spawn_dns_proxy(tunnel.ip, tunnel.gateway)?; + self.dns_proxy_close_handle = Some(close_handle); + Ok(listen_addr) + } + + fn stop_dns_proxy(&mut self) { + if let Some(close_handle) = self.dns_proxy_close_handle.take() { + close_handle.close(); + } } } @@ -173,3 +238,43 @@ fn as_pfctl_proto(protocol: net::TransportProtocol) -> pfctl::Proto { net::TransportProtocol::Tcp => pfctl::Proto::Tcp, } } + +fn spawn_dns_proxy( + tunnel_ip: Ipv4Addr, + tunnel_gateway: Ipv4Addr, +) -> Result<(SocketAddr, RelayCloseHandle)> { + let (tx, rx) = mpsc::channel(); + thread::spawn(move || { + match spawn_dns_proxy_helper(tunnel_ip, tunnel_gateway) { + Ok((mut core, relay)) => { + tx.send(Ok((relay.listen_addr(), relay.close_handle()))) + .unwrap(); + match core.run(relay) { + Err(e) => error!("DNS proxy died with an error: {}", e), + Ok(_) => info!("DNS proxy exiting"), + } + } + Err(e) => { + tx.send(Err(e)).unwrap(); + } + } + }); + rx.recv().unwrap() +} + +fn spawn_dns_proxy_helper( + tunnel_ip: Ipv4Addr, + tunnel_gateway: Ipv4Addr, +) -> Result<(tokio_core::reactor::Core, Relay)> { + let core = tokio_core::reactor::Core::new().chain_err(|| "Unable to init Tokio event loop")?; + + let relay = Relay::new( + "127.0.0.1:0".parse().unwrap(), + IpAddr::V4(tunnel_ip), + SocketAddr::from((tunnel_gateway, 53)), + core.handle(), + ).chain_err(|| "Unable to create DNS proxy socket relay")?; + info!("DNS proxy listening on {}", relay.listen_addr()); + + Ok((core, relay)) +} diff --git a/talpid-core/src/tunnel/mod.rs b/talpid-core/src/tunnel/mod.rs index 0ed7e86960..4ee7b24cee 100644 --- a/talpid-core/src/tunnel/mod.rs +++ b/talpid-core/src/tunnel/mod.rs @@ -9,8 +9,8 @@ use std::env; use std::ffi::{OsStr, OsString}; use std::fs; use std::io::{self, Write}; -use std::path::{Path, PathBuf}; use std::net::Ipv4Addr; +use std::path::{Path, PathBuf}; use talpid_types::net; |
