summaryrefslogtreecommitdiffhomepage
path: root/talpid-core/src
diff options
context:
space:
mode:
authorLinus Färnstrand <linus@mullvad.net>2017-09-20 22:23:55 +0200
committerLinus Färnstrand <linus@mullvad.net>2017-09-21 18:52:29 +0200
commit98ab8d1993c2a2d8c1211996f5ca7ed0002d3e32 (patch)
tree82bceaefd28c54a167296332d66cb5c5accce971 /talpid-core/src
parent1c6382dc9cbcf277501d7269f762c0a0c9d344b1 (diff)
downloadmullvadvpn-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.rs157
-rw-r--r--talpid-core/src/tunnel/mod.rs2
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;