diff options
| -rw-r--r-- | Cargo.lock | 2 | ||||
| -rw-r--r-- | mullvad-masque-proxy/Cargo.toml | 1 | ||||
| -rw-r--r-- | mullvad-masque-proxy/examples/masque-client.rs | 14 | ||||
| -rw-r--r-- | mullvad-masque-proxy/src/client/mod.rs | 56 | ||||
| -rw-r--r-- | mullvad-masque-proxy/tests/proxy.rs | 3 | ||||
| -rw-r--r-- | tunnel-obfuscation/Cargo.toml | 1 | ||||
| -rw-r--r-- | tunnel-obfuscation/src/quic.rs | 47 |
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() } } |
