summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorJoakim Hulthe <joakim.hulthe@mullvad.net>2025-04-11 16:41:31 +0200
committerJoakim Hulthe <joakim.hulthe@mullvad.net>2025-04-11 16:41:31 +0200
commit104b9638077d9ae2696c56c31ec90d8bb253fb70 (patch)
treedb1c515d7d28168fd8da7052e647af927e7a8c85
parent0bb1e6f09266cb623b9115d934cd2c9a54215774 (diff)
parent42236ffb51f097179d7cc4763a4217ba89d427e5 (diff)
downloadmullvadvpn-104b9638077d9ae2696c56c31ec90d8bb253fb70.tar.xz
mullvadvpn-104b9638077d9ae2696c56c31ec90d8bb253fb70.zip
Merge branch 'masque-args'
-rw-r--r--mullvad-masque-proxy/examples/masque-client.rs28
-rw-r--r--mullvad-masque-proxy/src/client/mod.rs20
-rw-r--r--mullvad-masque-proxy/tests/proxy.rs1
3 files changed, 40 insertions, 9 deletions
diff --git a/mullvad-masque-proxy/examples/masque-client.rs b/mullvad-masque-proxy/examples/masque-client.rs
index 62fe53f1f9..f873b14b2f 100644
--- a/mullvad-masque-proxy/examples/masque-client.rs
+++ b/mullvad-masque-proxy/examples/masque-client.rs
@@ -1,3 +1,4 @@
+use anyhow::Context;
use clap::Parser;
use mullvad_masque_proxy::client::{ClientConfig, Error};
use tokio::net::UdpSocket;
@@ -5,6 +6,7 @@ use tokio::net::UdpSocket;
use std::{
net::{Ipv4Addr, SocketAddr},
path::PathBuf,
+ time::Duration,
};
#[derive(Parser, Debug)]
@@ -25,9 +27,9 @@ pub struct ClientArgs {
#[arg(long, short = 'H')]
server_hostname: String,
- /// Local bind port
- #[arg(long, short = 'p', default_value = "0")]
- bind_port: u16,
+ /// Bind address
+ #[arg(long, short = 'b', default_value = "127.0.0.1:0")]
+ bind_addr: SocketAddr,
/// Maximum packet size
#[arg(long, short = 'S', default_value = "1280")]
@@ -37,6 +39,17 @@ pub struct ClientArgs {
#[cfg(target_os = "linux")]
#[arg(long)]
fwmark: Option<u16>,
+
+ /// 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)]
+ idle_timeout: Option<Duration>,
+}
+
+/// Parse a duration from a decimal number of seconds
+fn duration_from_seconds(s: &str) -> anyhow::Result<Duration> {
+ let seconds: f64 = s.parse().context("Expected a decimal number, e.g. 1.0")?;
+ Ok(Duration::from_secs_f64(seconds))
}
#[tokio::main]
@@ -51,10 +64,11 @@ async fn main() {
target_addr,
root_cert_path,
server_hostname,
- bind_port,
+ bind_addr,
mtu,
#[cfg(target_os = "linux")]
fwmark,
+ idle_timeout,
} = ClientArgs::parse();
let tls_config = match root_cert_path {
@@ -65,8 +79,7 @@ async fn main() {
let _keylog = rustls::KeyLogFile::new();
- let unbound_local_addr: SocketAddr = (Ipv4Addr::UNSPECIFIED, bind_port).into();
- let local_socket = UdpSocket::bind(unbound_local_addr)
+ let local_socket = UdpSocket::bind(bind_addr)
.await
.expect("Failed to bind address");
let local_addr = local_socket.local_addr().unwrap();
@@ -80,7 +93,8 @@ async fn main() {
.server_host(server_hostname)
.target_addr(target_addr)
.mtu(mtu)
- .tls_config(tls_config);
+ .tls_config(tls_config)
+ .idle_timeout(idle_timeout);
#[cfg(target_os = "linux")]
let config = config.fwmark(fwmark);
diff --git a/mullvad-masque-proxy/src/client/mod.rs b/mullvad-masque-proxy/src/client/mod.rs
index 1e2a1a9adf..f913318595 100644
--- a/mullvad-masque-proxy/src/client/mod.rs
+++ b/mullvad-masque-proxy/src/client/mod.rs
@@ -5,6 +5,7 @@ use std::{
net::{Ipv4Addr, SocketAddr},
path::Path,
sync::{Arc, LazyLock},
+ time::Duration,
};
use tokio::{
net::UdpSocket,
@@ -17,7 +18,8 @@ use h3::{client, ext::Protocol, proto::varint::VarInt, quic::StreamId};
use h3_datagram::{datagram::Datagram, datagram_traits::HandleDatagramsExt};
use http::{header, uri::Scheme, Response, StatusCode};
use quinn::{
- crypto::rustls::QuicClientConfig, Endpoint, EndpointConfig, TokioRuntime, TransportConfig,
+ crypto::rustls::QuicClientConfig, Endpoint, EndpointConfig, IdleTimeout, TokioRuntime,
+ TransportConfig,
};
use crate::{
@@ -99,6 +101,8 @@ pub enum Error {
ParseCerts,
#[error("Failed to fragment a packet - it is too large")]
PacketTooLarge(#[from] fragment::PacketTooLarge),
+ #[error("The provided idle timeout was invalid")]
+ InvalidIdleTimeout(quinn::VarIntBoundsExceeded),
}
#[derive(TypedBuilder)]
@@ -130,6 +134,10 @@ pub struct ClientConfig {
#[cfg(target_os = "linux")]
#[builder(default)]
pub fwmark: Option<u16>,
+
+ /// Optional timeout when no data is sent in the proxy.
+ #[builder(default)]
+ pub idle_timeout: Option<Duration>,
}
impl Client {
@@ -138,7 +146,15 @@ impl Client {
.expect("Failed to construct a valid TLS configuration");
let mut client_config = quinn::ClientConfig::new(Arc::new(quic_client_config));
- let transport_config = TransportConfig::default();
+ let mut transport_config = TransportConfig::default();
+ transport_config.max_idle_timeout(
+ config
+ .idle_timeout
+ .map(IdleTimeout::try_from)
+ .transpose()
+ .map_err(Error::InvalidIdleTimeout)?,
+ );
+
// TODO: Set datagram_receive_buffer_size if needed
// TODO: Set datagram_send_buffer_size if needed
// When would it be needed? If we need to buffer more packets or buffer less packets for
diff --git a/mullvad-masque-proxy/tests/proxy.rs b/mullvad-masque-proxy/tests/proxy.rs
index 40231c02ef..04eacfe78f 100644
--- a/mullvad-masque-proxy/tests/proxy.rs
+++ b/mullvad-masque-proxy/tests/proxy.rs
@@ -174,6 +174,7 @@ async fn setup_masque(mtu: u16) -> anyhow::Result<(UdpSocket, UdpSocket)> {
.server_host(HOST.to_owned())
.target_addr(target_udp_addr)
.mtu(mtu)
+ .idle_timeout(Some(Duration::from_secs(10)))
.build();
let client = client::Client::connect(client_config)