summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--Cargo.lock2
-rw-r--r--mullvad-masque-proxy/Cargo.toml1
-rw-r--r--mullvad-masque-proxy/examples/masque-client.rs14
-rw-r--r--mullvad-masque-proxy/src/client/mod.rs56
-rw-r--r--mullvad-masque-proxy/tests/proxy.rs3
-rw-r--r--tunnel-obfuscation/Cargo.toml1
-rw-r--r--tunnel-obfuscation/src/quic.rs47
7 files changed, 52 insertions, 72 deletions
diff --git a/Cargo.lock b/Cargo.lock
index eeda37cecc..0578bec7af 100644
--- a/Cargo.lock
+++ b/Cargo.lock
@@ -3289,7 +3289,6 @@ dependencies = [
"rand 0.8.5",
"rustls 0.23.18",
"rustls-pemfile 2.1.3",
- "socket2 0.5.8",
"talpid-tunnel",
"thiserror 2.0.9",
"tokio",
@@ -6458,6 +6457,7 @@ dependencies = [
"mullvad-masque-proxy",
"nix 0.30.1",
"shadowsocks",
+ "socket2 0.5.8",
"thiserror 2.0.9",
"tokio",
"tokio-util 0.7.10",
diff --git a/mullvad-masque-proxy/Cargo.toml b/mullvad-masque-proxy/Cargo.toml
index 98fc75f0e4..31bc379dd3 100644
--- a/mullvad-masque-proxy/Cargo.toml
+++ b/mullvad-masque-proxy/Cargo.toml
@@ -21,7 +21,6 @@ rustls-pemfile = "2.1.3"
bytes = "1"
anyhow = { workspace = true }
log = { workspace = true }
-socket2 = { workspace = true }
typed-builder = "0.21.0"
[dev-dependencies]
diff --git a/mullvad-masque-proxy/examples/masque-client.rs b/mullvad-masque-proxy/examples/masque-client.rs
index 304a0c0ed5..3ff952b491 100644
--- a/mullvad-masque-proxy/examples/masque-client.rs
+++ b/mullvad-masque-proxy/examples/masque-client.rs
@@ -36,11 +36,6 @@ pub struct ClientArgs {
#[arg(long, short = 'S', default_value = "1280")]
mtu: u16,
- /// fwmark to use for the `server_addr` connection
- #[cfg(target_os = "linux")]
- #[arg(long)]
- fwmark: Option<u32>,
-
/// Maximum duration of inactivity (in seconds) until the tunnel times out.
/// Inactivity happens when no data is sent over the proxy.
#[arg(long, short = 'i', value_parser = duration_from_seconds)]
@@ -71,8 +66,6 @@ async fn main() {
server_hostname,
bind_addr,
mtu,
- #[cfg(target_os = "linux")]
- fwmark,
idle_timeout,
auth,
} = ClientArgs::parse();
@@ -93,9 +86,11 @@ async fn main() {
log::info!("Listening on {local_addr}");
+ let endpoint_socket = UdpSocket::bind((Ipv4Addr::UNSPECIFIED, 0)).await.unwrap();
+
let config = ClientConfig::builder()
.client_socket(local_socket)
- .local_addr((Ipv4Addr::UNSPECIFIED, 0).into())
+ .quinn_socket(endpoint_socket)
.server_addr(server_addr)
.server_host(server_hostname)
.target_addr(target_addr)
@@ -104,9 +99,6 @@ async fn main() {
.idle_timeout(idle_timeout)
.auth_header(auth);
- #[cfg(target_os = "linux")]
- let config = config.fwmark(fwmark);
-
let client = mullvad_masque_proxy::client::Client::connect(config.build()).await;
if let Err(err) = &client {
log::error!("ERROR: {:?}", err);
diff --git a/mullvad-masque-proxy/src/client/mod.rs b/mullvad-masque-proxy/src/client/mod.rs
index 4aaf7a371b..801249cfb2 100644
--- a/mullvad-masque-proxy/src/client/mod.rs
+++ b/mullvad-masque-proxy/src/client/mod.rs
@@ -24,7 +24,6 @@ use quinn::{
Endpoint, EndpointConfig, IdleTimeout, TokioRuntime, TransportConfig,
crypto::rustls::QuicClientConfig,
};
-use socket2::Socket;
use crate::{
MASQUE_WELL_KNOWN_PATH, MAX_INFLIGHT_PACKETS, MIN_IPV4_MTU, MIN_IPV6_MTU, QUIC_HEADER_SIZE,
@@ -70,9 +69,6 @@ pub enum Error {
Bind(#[source] io::Error),
#[error("Failed to setup a QUIC endpoint")]
Endpoint(#[source] io::Error),
- #[cfg(target_os = "linux")]
- #[error("Failed to set fwmark on remote socket")]
- Fwmark(#[source] io::Error),
#[error("Failed to begin connecting to QUIC endpoint")]
Connect(#[from] quinn::ConnectError),
#[error("Failed to connect to QUIC endpoint")]
@@ -120,9 +116,8 @@ pub struct ClientConfig {
/// Socket that accepts proxy clients
pub client_socket: UdpSocket,
- /// Socket address to bind the QUIC endpoint socket to
- // TODO: For Android, we need to be able to pass a socket directly
- pub local_addr: SocketAddr,
+ /// Socket to bind the QUIC endpoint socket to
+ pub quinn_socket: UdpSocket,
/// Destination to which traffic is forwarded
pub target_addr: SocketAddr,
@@ -141,11 +136,6 @@ pub struct ClientConfig {
#[builder(default = default_tls_config())]
pub tls_config: Arc<rustls::ClientConfig>,
- /// Optional fwmark to set on the QUIC endpoint socket
- #[cfg(target_os = "linux")]
- #[builder(default)]
- pub fwmark: Option<u32>,
-
/// Optional timeout when no data is sent in the proxy.
#[builder(default)]
pub idle_timeout: Option<Duration>,
@@ -180,12 +170,10 @@ impl Client {
let max_udp_payload_size = compute_udp_payload_size(config.mtu, config.server_addr);
- let quic_socket = Self::create_quic_socket(
- config.local_addr,
- #[cfg(target_os = "linux")]
- config.fwmark,
+ let endpoint = Self::setup_quic_endpoint(
+ config.quinn_socket.into_std().map_err(Error::Endpoint)?,
+ max_udp_payload_size,
)?;
- let endpoint = Self::setup_quic_endpoint(quic_socket, max_udp_payload_size)?;
let connecting =
endpoint.connect_with(client_config, config.server_addr, &config.server_host)?;
@@ -225,28 +213,11 @@ impl Client {
}
}
- pub fn create_quic_socket(
- local_addr: SocketAddr,
- #[cfg(target_os = "linux")] fwmark: Option<u32>,
- ) -> Result<Socket> {
- // family
- let domain = match &local_addr {
- SocketAddr::V4(_) => socket2::Domain::IPV4,
- SocketAddr::V6(_) => socket2::Domain::IPV6,
- };
- let ty = socket2::Type::DGRAM;
- let protocol = Some(socket2::Protocol::UDP);
- let socket = socket2::Socket::new(domain, ty, protocol).map_err(Error::Bind)?;
- #[cfg(target_os = "linux")]
- if let Some(fwmark) = fwmark {
- socket.set_mark(fwmark).map_err(Error::Fwmark)?;
- }
- socket.bind(&local_addr.into()).map_err(Error::Bind)?;
- Ok(socket)
- }
-
// `socket` is a UDP socket which quinn will read/write from/to.
- fn setup_quic_endpoint(socket: Socket, max_udp_payload_size: u16) -> Result<Endpoint> {
+ fn setup_quic_endpoint(
+ socket: std::net::UdpSocket,
+ max_udp_payload_size: u16,
+ ) -> Result<Endpoint> {
let endpoint_config = {
let mut endpoint_config = EndpointConfig::default();
endpoint_config
@@ -255,13 +226,8 @@ impl Client {
endpoint_config
};
- Endpoint::new(
- endpoint_config,
- None,
- std::net::UdpSocket::from(socket),
- Arc::new(TokioRuntime),
- )
- .map_err(Error::Endpoint)
+ Endpoint::new(endpoint_config, None, socket, Arc::new(TokioRuntime))
+ .map_err(Error::Endpoint)
}
/// Returns an h3 connection that is ready to be used for sending UDP datagrams.
diff --git a/mullvad-masque-proxy/tests/proxy.rs b/mullvad-masque-proxy/tests/proxy.rs
index e39f431cd6..4efb1e7fc9 100644
--- a/mullvad-masque-proxy/tests/proxy.rs
+++ b/mullvad-masque-proxy/tests/proxy.rs
@@ -170,10 +170,11 @@ async fn setup_masque(mtu: u16) -> anyhow::Result<(UdpSocket, UdpSocket)> {
.await
.context("Failed to bind address")?;
let masque_client_addr = local_socket.local_addr().unwrap();
+ let quinn_socket = UdpSocket::bind(any_localhost_addr).await?;
let client_config = client::ClientConfig::builder()
.client_socket(local_socket)
- .local_addr(any_localhost_addr)
+ .quinn_socket(quinn_socket)
.server_addr(masque_server_addr)
.server_host(HOST.to_owned())
.target_addr(target_udp_addr)
diff --git a/tunnel-obfuscation/Cargo.toml b/tunnel-obfuscation/Cargo.toml
index 452b9f9eed..48cf2a932d 100644
--- a/tunnel-obfuscation/Cargo.toml
+++ b/tunnel-obfuscation/Cargo.toml
@@ -19,6 +19,7 @@ tokio-util = { workspace = true }
udp-over-tcp = { git = "https://github.com/mullvad/udp-over-tcp", rev = "87936ac29b68b902565955f138ab02294bcc8593" }
shadowsocks = { workspace = true }
mullvad-masque-proxy = { path = "../mullvad-masque-proxy" }
+socket2 = { workspace = true, features = ["all"] }
[target.'cfg(target_os="linux")'.dependencies]
nix = { workspace = true }
diff --git a/tunnel-obfuscation/src/quic.rs b/tunnel-obfuscation/src/quic.rs
index ff8aa96a7a..117766ea67 100644
--- a/tunnel-obfuscation/src/quic.rs
+++ b/tunnel-obfuscation/src/quic.rs
@@ -7,7 +7,7 @@ use std::{
net::{Ipv4Addr, Ipv6Addr, SocketAddr},
};
use tokio::net::UdpSocket;
-use tokio_util::sync::{CancellationToken};
+use tokio_util::sync::CancellationToken;
use crate::Obfuscator;
@@ -19,6 +19,9 @@ pub enum Error {
BindError(#[source] io::Error),
#[error("Masque proxy error")]
MasqueProxyError(#[source] mullvad_masque_proxy::client::Error),
+ #[cfg(target_os = "linux")]
+ #[error("Failed to set fwmark on remote socket")]
+ Fwmark(#[source] io::Error),
}
#[derive(Debug)]
@@ -130,18 +133,36 @@ impl Quic {
} else {
SocketAddr::from((Ipv6Addr::UNSPECIFIED, 0))
};
+ let quic_socket = {
+ // family
+ let domain = match quic_client_local_addr {
+ SocketAddr::V4(_) => socket2::Domain::IPV4,
+ SocketAddr::V6(_) => socket2::Domain::IPV6,
+ };
+ let ty = socket2::Type::DGRAM;
+ let protocol = Some(socket2::Protocol::UDP);
+ let socket = socket2::Socket::new(domain, ty, protocol).map_err(Error::BindError)?;
+ socket
+ .bind(&socket2::SockAddr::from(quic_client_local_addr))
+ .map_err(Error::BindError)?;
+
+ #[cfg(target_os = "linux")]
+ if let Some(fwmark) = settings.fwmark {
+ socket.set_mark(fwmark).map_err(Error::Fwmark)?;
+ }
+
+ UdpSocket::from_std(std::net::UdpSocket::from(socket)).map_err(Error::BindError)?
+ };
+
let config_builder = ClientConfig::builder()
.client_socket(local_socket)
- .local_addr(quic_client_local_addr)
+ .quinn_socket(quic_socket)
.server_addr(settings.quic_endpoint)
.server_host(settings.hostname.clone())
.target_addr(settings.wireguard_endpoint)
.auth_header(Some(settings.auth_header()))
.mtu(settings.mtu.unwrap_or(1500));
- #[cfg(target_os = "linux")]
- let config_builder = config_builder.fwmark(settings.fwmark);
-
let config = config_builder.build();
let quic = Quic {
@@ -195,20 +216,19 @@ impl Obfuscator for Quic {
async fn run(self: Box<Self>) -> crate::Result<()> {
let token = CancellationToken::new();
+ let child_token = token.child_token();
+ // This will always cancel `child_token` as soon as `run` is finished or aborted.
+ let _drop_guard = token.drop_guard();
let client = Client::connect(self.config)
.await
.map_err(Error::MasqueProxyError)
.map_err(crate::Error::RunQuicObfuscator)?;
- let local_proxy = tokio::spawn(Quic::run_forwarding(client, token.child_token()));
-
- let result = local_proxy.await
+ tokio::spawn(Quic::run_forwarding(client, child_token))
+ .await
.unwrap()
- .map_err(crate::Error::RunQuicObfuscator);
-
- token.cancel();
- result
+ .map_err(crate::Error::RunQuicObfuscator)
}
fn packet_overhead(&self) -> u16 {
@@ -219,6 +239,7 @@ impl Obfuscator for Quic {
#[cfg(target_os = "android")]
fn remote_socket_fd(&self) -> std::os::unix::io::RawFd {
- unimplemented!()
+ use std::os::fd::AsRawFd;
+ self.config.quinn_socket.as_raw_fd()
}
}