Skip to content

Commit

Permalink
Enable Encrypted DNS proxy access method in the daemon
Browse files Browse the repository at this point in the history
  • Loading branch information
MarkusPettersson98 committed Oct 21, 2024
1 parent d12ad3e commit 4ef8a02
Show file tree
Hide file tree
Showing 8 changed files with 69 additions and 12 deletions.
2 changes: 2 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

4 changes: 3 additions & 1 deletion mullvad-api/src/https_client_with_sni.rs
Original file line number Diff line number Diff line change
Expand Up @@ -81,7 +81,6 @@ enum InnerConnectionMode {
Socks5(SocksConfig),
/// Connect to the destination via Mullvad Encrypted DNS proxy.
/// See [`mullvad-encrypted-dns-proxy`] for how the proxy works.
#[allow(dead_code)] // TODO: Remove this allow
EncryptedDnsProxy(mullvad_encrypted_dns_proxy::config::ProxyConfig),
}

Expand Down Expand Up @@ -275,6 +274,9 @@ impl TryFrom<ApiConnectionMode> for InnerConnectionMode {
peer: config.endpoint,
authentication: config.auth,
}),
ProxyConfig::EncryptedDnsProxy(config) => {
InnerConnectionMode::EncryptedDnsProxy(config)
}
},
})
}
Expand Down
9 changes: 9 additions & 0 deletions mullvad-api/src/proxy.rs
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ use hyper_util::client::legacy::connect::{Connected, Connection};
use serde::{Deserialize, Serialize};
use std::{
fmt, io,
net::SocketAddr,
path::Path,
pin::Pin,
task::{self, Poll},
Expand Down Expand Up @@ -74,6 +75,7 @@ pub enum ProxyConfig {
Shadowsocks(proxy::Shadowsocks),
Socks5Local(proxy::Socks5Local),
Socks5Remote(proxy::Socks5Remote),
EncryptedDnsProxy(mullvad_encrypted_dns_proxy::config::ProxyConfig),
}

impl ProxyConfig {
Expand All @@ -87,6 +89,10 @@ impl ProxyConfig {
ProxyConfig::Socks5Remote(remote) => {
Endpoint::from_socket_address(remote.endpoint, TransportProtocol::Tcp)
}
ProxyConfig::EncryptedDnsProxy(proxy) => {
let addr = SocketAddr::V4(proxy.addr);
Endpoint::from_socket_address(addr, TransportProtocol::Tcp)
}
}
}
}
Expand All @@ -100,6 +106,9 @@ impl fmt::Display for ProxyConfig {
ProxyConfig::Socks5Local(local) => {
write!(f, "Socks5 {} via localhost:{}", endpoint, local.local_port)
}
ProxyConfig::EncryptedDnsProxy(proxy) => {
write!(f, "ApiSusan {}", proxy.addr)
}
}
}
}
Expand Down
1 change: 1 addition & 0 deletions mullvad-daemon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -31,6 +31,7 @@ tokio-stream = "0.1"
mullvad-relay-selector = { path = "../mullvad-relay-selector" }
mullvad-types = { path = "../mullvad-types" }
mullvad-api = { path = "../mullvad-api" }
mullvad-encrypted-dns-proxy = { path = "../mullvad-encrypted-dns-proxy" }
mullvad-fs = { path = "../mullvad-fs" }
mullvad-paths = { path = "../mullvad-paths" }
mullvad-version = { path = "../mullvad-version" }
Expand Down
55 changes: 47 additions & 8 deletions mullvad-daemon/src/api.rs
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,7 @@ use mullvad_api::{
proxy::{ApiConnectionMode, ConnectionModeProvider, ProxyConfig},
AddressCache,
};
use mullvad_encrypted_dns_proxy::state::EncryptedDnsProxyState;
use mullvad_relay_selector::RelaySelector;
use mullvad_types::access_method::{
AccessMethod, AccessMethodSetting, BuiltInAccessMethod, Id, Settings,
Expand Down Expand Up @@ -239,6 +240,8 @@ pub struct AccessModeSelector {
cache_dir: PathBuf,
/// Used for selecting a Bridge when the `Mullvad Bridges` access method is used.
relay_selector: RelaySelector,
/// Used for selecting a config for the 'Encrypted DNS proxy' access method.
encrypted_dns_proxy_cache: EncryptedDnsProxyState,
access_method_settings: Settings,
address_cache: AddressCache,
access_method_event_sender: DaemonEventSender<(AccessMethodEvent, oneshot::Sender<()>)>,
Expand Down Expand Up @@ -267,10 +270,26 @@ impl AccessModeSelector {
}
}

// Initialize the Encrypted DNS cache
let mut encrypted_dns_proxy_cache = {
// Initialize an empty cache
let mut cache = EncryptedDnsProxyState::default();
// Hydrate the cache by fetching new proxy configs.
if let Err(_error) = cache.fetch_configs().await {
// TODO: What should we do if we initially fail to fetch configs? Handle later.
}
cache
};

// Always start looking from the position of `Direct`.
let (index, next) = Self::find_next_active(0, &access_method_settings);
let initial_connection_mode =
Self::resolve_inner(next, &relay_selector, &address_cache).await;
let initial_connection_mode = Self::resolve_inner(
next,
&relay_selector,
&mut encrypted_dns_proxy_cache,
&address_cache,
)
.await;

let (change_tx, change_rx) = mpsc::unbounded();

Expand All @@ -280,6 +299,7 @@ impl AccessModeSelector {
cmd_rx,
cache_dir,
relay_selector,
encrypted_dns_proxy_cache,
access_method_settings,
address_cache,
access_method_event_sender,
Expand Down Expand Up @@ -496,16 +516,28 @@ impl AccessModeSelector {
}

async fn resolve(&mut self, access_method: AccessMethodSetting) -> ResolvedConnectionMode {
Self::resolve_inner(access_method, &self.relay_selector, &self.address_cache).await
// TODO: Should we fetch new configs here everytime?
// self.encrypted_dns_proxy_cache.fetch_configs().await;
Self::resolve_inner(
access_method,
&self.relay_selector,
&mut self.encrypted_dns_proxy_cache,
&self.address_cache,
)
.await
}

async fn resolve_inner(
access_method: AccessMethodSetting,
relay_selector: &RelaySelector,
encrypted_dns_proxy_cache: &mut EncryptedDnsProxyState,
address_cache: &AddressCache,
) -> ResolvedConnectionMode {
let connection_mode =
resolve_connection_mode(access_method.access_method.clone(), relay_selector);
let connection_mode = resolve_connection_mode(
access_method.access_method.clone(),
relay_selector,
encrypted_dns_proxy_cache,
);
let endpoint =
resolve_allowed_endpoint(&connection_mode, address_cache.get_address().await);
ResolvedConnectionMode {
Expand All @@ -523,6 +555,7 @@ impl AccessModeSelector {
fn resolve_connection_mode(
access_method: AccessMethod,
relay_selector: &RelaySelector,
encrypted_dns_proxy_cache: &mut EncryptedDnsProxyState,
) -> ApiConnectionMode {
match access_method {
AccessMethod::BuiltIn(BuiltInAccessMethod::Direct) => ApiConnectionMode::Direct,
Expand All @@ -536,9 +569,15 @@ fn resolve_connection_mode(
);
ApiConnectionMode::Direct
}),
AccessMethod::BuiltIn(BuiltInAccessMethod::EncryptedDnsProxy) => {
todo!("Implement me!")
}
AccessMethod::BuiltIn(BuiltInAccessMethod::EncryptedDnsProxy) => encrypted_dns_proxy_cache
.next_configuration()
.map(ProxyConfig::EncryptedDnsProxy)
.map(ApiConnectionMode::Proxied)
.unwrap_or_else(|| {
log::error!("Could not select next Encrypted DNS proxy config");
log::error!("Defaulting to direct API connection");
ApiConnectionMode::Direct
}),
AccessMethod::Custom(config) => ApiConnectionMode::Proxied(ProxyConfig::from(config)),
}
}
Expand Down
1 change: 1 addition & 0 deletions mullvad-encrypted-dns-proxy/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ workspace = true
tokio = { workspace = true, features = [ "macros" ] }
log = { workspace = true }
hickory-resolver = { version = "0.24.1", features = [ "dns-over-https-rustls" ]}
serde = { workspace = true }
webpki-roots = "0.25.0"
rustls = "0.21"

Expand Down
5 changes: 3 additions & 2 deletions mullvad-encrypted-dns-proxy/src/config/mod.rs
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ use std::net::{Ipv6Addr, SocketAddrV4};
mod plain;
mod xor;

use serde::{Deserialize, Serialize};
pub use xor::XorKey;

/// All the errors that can happen during deserialization of a [`ProxyConfig`].
Expand Down Expand Up @@ -67,7 +68,7 @@ pub trait Obfuscator: Send {

/// Represents a Mullvad Encrypted DNS proxy configuration. Created by parsing
/// the config out of an IPv6 address resolved over DoH.
#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct ProxyConfig {
/// The remote address to connect to the proxy over. This is the address
/// on the internet where the proxy is listening.
Expand All @@ -77,7 +78,7 @@ pub struct ProxyConfig {
pub obfuscation: Option<ObfuscationConfig>,
}

#[derive(Debug, Clone, Eq, PartialEq, Hash)]
#[derive(Debug, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub enum ObfuscationConfig {
XorV2(xor::XorKey),
}
Expand Down
4 changes: 3 additions & 1 deletion mullvad-encrypted-dns-proxy/src/config/xor.rs
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
use core::fmt;
use std::net::{Ipv4Addr, SocketAddrV4};

use serde::{Deserialize, Serialize};

/// Parse a proxy config that XORs all traffic with the given key.
///
/// A Xor configuration is represented by the proxy type `ProxyType::XorV2`. There used to be a `XorV1`, but it
Expand Down Expand Up @@ -37,7 +39,7 @@ pub fn parse_xor(data: [u8; 12]) -> Result<super::ProxyConfig, super::Error> {

/// A bunch of bytes, representing a "key" Simply meaning a slice of bytes that the data
/// will be XORed with.
#[derive(Copy, Clone, Eq, PartialEq, Hash)]
#[derive(Copy, Clone, Eq, PartialEq, Hash, Serialize, Deserialize)]
pub struct XorKey {
data: [u8; 6],
len: usize,
Expand Down

0 comments on commit 4ef8a02

Please sign in to comment.