diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2025-07-04 16:49:16 +0200 |
|---|---|---|
| committer | Markus Pettersson <markus.pettersson@mullvad.net> | 2025-07-09 15:16:36 +0200 |
| commit | b4fcf2661ff0318c4822a82613932be29ab62072 (patch) | |
| tree | 957e06d94fa456a901c0348db063690e6e12f9bc | |
| parent | 1a3c9c16b64eb8c30db150a2719a1ab028de9cab (diff) | |
| download | mullvadvpn-b4fcf2661ff0318c4822a82613932be29ab62072.tar.xz mullvadvpn-b4fcf2661ff0318c4822a82613932be29ab62072.zip | |
Add support for QUIC in the relay selector
Parse new 'features' key from relay list API, and add Quic obfuscation
to automatic retry order
| -rw-r--r-- | docs/relay-selector.md | 7 | ||||
| -rw-r--r-- | mullvad-api/src/bin/relay_list.rs | 6 | ||||
| -rw-r--r-- | mullvad-api/src/relay_list.rs | 23 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/relay_list.rs | 11 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/relay_selector/helpers.rs | 22 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/relay_selector/matcher.rs | 7 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/relay_selector/mod.rs | 22 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/relay_selector/query.rs | 22 | ||||
| -rw-r--r-- | mullvad-relay-selector/tests/relay_selector.rs | 60 | ||||
| -rw-r--r-- | mullvad-types/src/relay_list.rs | 117 | ||||
| -rw-r--r-- | talpid-types/src/net/obfuscation.rs | 6 | ||||
| -rw-r--r-- | talpid-wireguard/src/obfuscation.rs | 25 | ||||
| -rw-r--r-- | tunnel-obfuscation/src/quic.rs | 15 |
13 files changed, 288 insertions, 55 deletions
diff --git a/docs/relay-selector.md b/docs/relay-selector.md index 3f74ffd6ae..f400a632a0 100644 --- a/docs/relay-selector.md +++ b/docs/relay-selector.md @@ -56,7 +56,7 @@ constraints the following default ones will take effect - The first attempt will connect to a Wireguard relay on a random port - The second attempt will connect to a Wireguard relay over IPv6 (if IPv6 is configured on the host) on a random port - The third attempt will connect to a Wireguard relay on a random port using Shadowsocks for obfuscation -- The fourth attempt will connect to a Wireguard relay using QUIC for obfuscation (if QUIC is implemented) +- The fourth attempt will connect to a Wireguard relay using QUIC for obfuscation - The fifth attempt will connect to a Wireguard relay on a random port using [UDP2TCP obfuscation](https://github.com/mullvad/udp-over-tcp) - The sixth attempt will connect to a Wireguard relay over IPv6 on a random port using UDP2TCP obfuscation (if IPv6 is configured on the host) @@ -82,6 +82,9 @@ As such, the above algorithm is simplified to the following version: - The UDP2TCP random port is **either** 80 **or** 5001 - The Shadowsocks port is random within a certain range of ports defined by the relay list +### Ports for QUIC +QUIC will use port 443. + If no tunnel has been established after exhausting this list of attempts, the relay selector will loop back to the first default constraint and continue its search from there. @@ -136,5 +139,5 @@ will indirectly change the bridge state to _Auto_ if it was previously set to _O ### Obfuscator caveats -There are two type of obfuscators - _udp2tcp_, and _shadowsocks_. +There are three types of obfuscators - _udp2tcp_, _shadowsocks_ and _quic_. They are used if the obfuscation mode is set _Auto_ and the user has selected WireGuard to be the only tunnel protocol to be used. diff --git a/mullvad-api/src/bin/relay_list.rs b/mullvad-api/src/bin/relay_list.rs index fbf7e8af42..80e3650b08 100644 --- a/mullvad-api/src/bin/relay_list.rs +++ b/mullvad-api/src/bin/relay_list.rs @@ -11,10 +11,8 @@ mod imp { use talpid_types::ErrorExt; pub async fn main() { - let runtime = mullvad_api::Runtime::new( - tokio::runtime::Handle::current(), - &ApiEndpoint::from_env_vars(), - ); + let api_endpoint = ApiEndpoint::from_env_vars(); + let runtime = mullvad_api::Runtime::new(tokio::runtime::Handle::current(), &api_endpoint); let relay_list_request = RelayListProxy::new( runtime.mullvad_rest_handle(ApiConnectionMode::Direct.into_provider()), diff --git a/mullvad-api/src/relay_list.rs b/mullvad-api/src/relay_list.rs index 9f4100408f..e01d61f9f5 100644 --- a/mullvad-api/src/relay_list.rs +++ b/mullvad-api/src/relay_list.rs @@ -167,6 +167,7 @@ fn into_mullvad_relay( relay: Relay, location: location::Location, endpoint_data: relay_list::RelayEndpointData, + features: relay_list::Features, ) -> relay_list::Relay { relay_list::Relay { hostname: relay.hostname, @@ -181,6 +182,7 @@ fn into_mullvad_relay( weight: relay.weight, endpoint_data, location, + features, } } @@ -247,11 +249,21 @@ struct Relay { impl Relay { fn into_openvpn_mullvad_relay(self, location: location::Location) -> relay_list::Relay { - into_mullvad_relay(self, location, relay_list::RelayEndpointData::Openvpn) + into_mullvad_relay( + self, + location, + relay_list::RelayEndpointData::Openvpn, + relay_list::Features::empty(), + ) } fn into_bridge_mullvad_relay(self, location: location::Location) -> relay_list::Relay { - into_mullvad_relay(self, location, relay_list::RelayEndpointData::Bridge) + into_mullvad_relay( + self, + location, + relay_list::RelayEndpointData::Bridge, + relay_list::Features::empty(), + ) } fn convert_to_lowercase(&mut self) { @@ -345,10 +357,16 @@ struct WireGuardRelay { daita: bool, #[serde(default)] shadowsocks_extra_addr_in: Vec<IpAddr>, + #[serde(default)] + features: relay_list::Features, } impl WireGuardRelay { fn into_mullvad_relay(self, location: location::Location) -> relay_list::Relay { + // Sanity check that new 'features' key is in sync with the old Relay keys. + if self.features.daita() { + debug_assert!(self.daita) + } into_mullvad_relay( self.relay, location, @@ -357,6 +375,7 @@ impl WireGuardRelay { daita: self.daita, shadowsocks_extra_addr_in: self.shadowsocks_extra_addr_in, }), + self.features, ) } } diff --git a/mullvad-management-interface/src/types/conversions/relay_list.rs b/mullvad-management-interface/src/types/conversions/relay_list.rs index 11718973c1..a089f8e6fd 100644 --- a/mullvad-management-interface/src/types/conversions/relay_list.rs +++ b/mullvad-management-interface/src/types/conversions/relay_list.rs @@ -285,7 +285,11 @@ impl TryFrom<proto::Relay> for mullvad_types::relay_list::Relay { }) .transpose()?; - Ok(MullvadRelay { + // TODO: Eventually, we will need to decide how to represent extra relay features in the + // protobuf message. + let features = mullvad_types::relay_list::Features::default(); + + let relay = MullvadRelay { hostname: relay.hostname, ipv4_addr_in: relay.ipv4_addr_in.parse().map_err(|_err| { FromProtobufTypeError::InvalidArgument("invalid relay IPv4 address") @@ -311,7 +315,10 @@ impl TryFrom<proto::Relay> for mullvad_types::relay_list::Relay { }) .ok_or("missing relay location") .map_err(FromProtobufTypeError::InvalidArgument)?, - }) + features, + }; + + Ok(relay) } } diff --git a/mullvad-relay-selector/src/relay_selector/helpers.rs b/mullvad-relay-selector/src/relay_selector/helpers.rs index df524501ce..4f340c10f7 100644 --- a/mullvad-relay-selector/src/relay_selector/helpers.rs +++ b/mullvad-relay-selector/src/relay_selector/helpers.rs @@ -15,7 +15,7 @@ use rand::{ seq::{IteratorRandom, SliceRandom}, thread_rng, Rng, }; -use talpid_types::net::obfuscation::ObfuscatorConfig; +use talpid_types::net::{obfuscation::ObfuscatorConfig, IpVersion}; use crate::SelectedObfuscator; @@ -141,6 +141,26 @@ pub fn get_shadowsocks_obfuscator( }) } +pub fn get_quic_obfuscator(relay: Relay, ip_version: IpVersion) -> Option<SelectedObfuscator> { + let quic = relay.features.quic()?; + let config = { + let hostname = quic.hostname().to_string(); + let endpoint = match ip_version { + IpVersion::V4 => SocketAddr::from((quic.in_ipv4()?, quic.port())), + IpVersion::V6 => SocketAddr::from((quic.in_ipv6()?, quic.port())), + }; + let auth_token = quic.auth_token().to_string(); + ObfuscatorConfig::Quic { + hostname, + endpoint, + auth_token, + } + }; + + let obfuscator = SelectedObfuscator { config, relay }; + Some(obfuscator) +} + /// Return an obfuscation config for the wireguard server at `wg_in_addr` or one of `extra_in_addrs` /// (unless empty). `wg_in_addr_port_ranges` contains all valid ports for `wg_in_addr`, and /// `SHADOWSOCKS_EXTRA_PORT_RANGES` contains valid ports for `extra_in_addrs`. diff --git a/mullvad-relay-selector/src/relay_selector/matcher.rs b/mullvad-relay-selector/src/relay_selector/matcher.rs index e14885f2c4..8133677b87 100644 --- a/mullvad-relay-selector/src/relay_selector/matcher.rs +++ b/mullvad-relay-selector/src/relay_selector/matcher.rs @@ -144,9 +144,10 @@ fn filter_on_obfuscation( relay, ) } - - // If Shadowsocks is not a requirement, then there are no relay-specific constraints - _ => true, + // QUIC is only enabled on some relays + ObfuscationQuery::Quic => relay.features.quic().is_some(), + // Other relays are compatible with this query + ObfuscationQuery::Off | ObfuscationQuery::Auto | ObfuscationQuery::Udp2tcp(_) => true, } } diff --git a/mullvad-relay-selector/src/relay_selector/mod.rs b/mullvad-relay-selector/src/relay_selector/mod.rs index 0d5f2f4796..7085ad1ba4 100644 --- a/mullvad-relay-selector/src/relay_selector/mod.rs +++ b/mullvad-relay-selector/src/relay_selector/mod.rs @@ -7,6 +7,7 @@ mod parsed_relays; pub mod query; pub mod relays; +use detailer::resolve_ip_version; use matcher::{filter_matching_bridges, filter_matching_relay_list}; use parsed_relays::ParsedRelays; use relays::{Multihop, Singlehop, WireguardConfig}; @@ -62,13 +63,13 @@ pub static WIREGUARD_RETRY_ORDER: LazyLock<Vec<RelayQuery>> = LazyLock::new(|| { // 1 This works with any wireguard relay RelayQueryBuilder::wireguard().build(), // 2 - RelayQueryBuilder::wireguard().port(443).build(), - // 3 RelayQueryBuilder::wireguard() .ip_version(IpVersion::V6) .build(), - // 4 + // 3 RelayQueryBuilder::wireguard().shadowsocks().build(), + // 4 + RelayQueryBuilder::wireguard().quic().build(), // 5 RelayQueryBuilder::wireguard().udp2tcp().build(), // 6 @@ -215,6 +216,9 @@ struct NormalSelectorConfig<'a> { } /// The return type of [`RelaySelector::get_relay`]. +// There won't ever be many instances of GetRelay floating around, so the 'large' difference in +// size between its variants is negligible. +#[allow(clippy::large_enum_variant)] #[derive(Clone, Debug)] pub enum GetRelay { Wireguard { @@ -915,16 +919,8 @@ impl RelaySelector { Ok(Some(obfuscation)) } ObfuscationQuery::Quic => { - let obfuscator = SelectedObfuscator { - config: ObfuscatorConfig::Quic { - // TODO: do not hardcode port - endpoint: std::net::SocketAddr::from((endpoint.peer.endpoint.ip(), 443)), - // TODO: do not hardcode - hostname: "test.mullvad.net".to_owned(), - }, - relay: obfuscator_relay, - }; - Ok(Some(obfuscator)) + let ip_version = resolve_ip_version(query.wireguard_constraints().ip_version); + Ok(helpers::get_quic_obfuscator(obfuscator_relay, ip_version)) } } } diff --git a/mullvad-relay-selector/src/relay_selector/query.rs b/mullvad-relay-selector/src/relay_selector/query.rs index 4a6d9e70a0..7a2ae3cbd6 100644 --- a/mullvad-relay-selector/src/relay_selector/query.rs +++ b/mullvad-relay-selector/src/relay_selector/query.rs @@ -635,6 +635,12 @@ pub mod builder { quantum_resistant: QuantumResistant, } + /// Quic obfuscation. + /// + /// Quic does not have any user-configurable parameters, so there is no type defined + /// in the mullvad-types crate. + pub struct Quic; + // This impl-block is quantified over all configurations impl<Multihop, Obfuscation, Daita, QuantumResistant> RelayQueryBuilder<Wireguard<Multihop, Obfuscation, Daita, QuantumResistant>> @@ -792,6 +798,22 @@ pub mod builder { protocol, } } + + /// Enable QUIC obufscation. + pub fn quic( + mut self, + ) -> RelayQueryBuilder<Wireguard<Multihop, Quic, Daita, QuantumResistant>> { + self.query.wireguard_constraints.obfuscation = ObfuscationQuery::Quic; + RelayQueryBuilder { + query: self.query, + protocol: Wireguard { + multihop: self.protocol.multihop, + obfuscation: Quic, + daita: self.protocol.daita, + quantum_resistant: self.protocol.quantum_resistant, + }, + } + } } impl<Multihop, Daita, QuantumResistant> diff --git a/mullvad-relay-selector/tests/relay_selector.rs b/mullvad-relay-selector/tests/relay_selector.rs index c104d84015..680f4eba3d 100644 --- a/mullvad-relay-selector/tests/relay_selector.rs +++ b/mullvad-relay-selector/tests/relay_selector.rs @@ -27,9 +27,9 @@ use mullvad_types::{ RelayConstraints, RelayOverride, RelaySettings, TransportPort, }, relay_list::{ - BridgeEndpointData, OpenVpnEndpoint, OpenVpnEndpointData, Relay, RelayEndpointData, - RelayList, RelayListCity, RelayListCountry, ShadowsocksEndpointData, WireguardEndpointData, - WireguardRelayEndpointData, + BridgeEndpointData, Features, OpenVpnEndpoint, OpenVpnEndpointData, Quic, Relay, + RelayEndpointData, RelayList, RelayListCity, RelayListCountry, ShadowsocksEndpointData, + WireguardEndpointData, WireguardRelayEndpointData, }, }; @@ -73,6 +73,16 @@ static RELAYS: LazyLock<RelayList> = LazyLock::new(|| RelayList { shadowsocks_extra_addr_in: vec![], }), location: DUMMY_LOCATION.clone(), + features: Features::default() + .configure_daita() + .configure_quic(Quic::new( + vec![ + "185.213.154.68".parse().unwrap(), + "2a03:1b20:5:f011::a09f".parse().unwrap(), + ], + "Bearer test".to_owned(), + "se9-wireguard.blockerad.eu".to_owned(), + )), }, Relay { hostname: "se10-wireguard".to_string(), @@ -94,6 +104,7 @@ static RELAYS: LazyLock<RelayList> = LazyLock::new(|| RelayList { shadowsocks_extra_addr_in: vec![], }), location: DUMMY_LOCATION.clone(), + features: Features::default(), }, Relay { hostname: "se11-wireguard".to_string(), @@ -115,6 +126,7 @@ static RELAYS: LazyLock<RelayList> = LazyLock::new(|| RelayList { shadowsocks_extra_addr_in: vec![], }), location: DUMMY_LOCATION.clone(), + features: Features::default().configure_daita(), }, Relay { hostname: "se-got-001".to_string(), @@ -129,6 +141,7 @@ static RELAYS: LazyLock<RelayList> = LazyLock::new(|| RelayList { weight: 1, endpoint_data: RelayEndpointData::Openvpn, location: DUMMY_LOCATION.clone(), + features: Features::default(), }, Relay { hostname: "se-got-002".to_string(), @@ -143,6 +156,7 @@ static RELAYS: LazyLock<RelayList> = LazyLock::new(|| RelayList { weight: 1, endpoint_data: RelayEndpointData::Openvpn, location: DUMMY_LOCATION.clone(), + features: Features::default(), }, Relay { hostname: "se-got-br-001".to_string(), @@ -157,6 +171,7 @@ static RELAYS: LazyLock<RelayList> = LazyLock::new(|| RelayList { weight: 1, endpoint_data: RelayEndpointData::Bridge, location: DUMMY_LOCATION.clone(), + features: Features::default(), }, SHADOWSOCKS_RELAY.clone(), ], @@ -241,6 +256,7 @@ static SHADOWSOCKS_RELAY: LazyLock<Relay> = LazyLock::new(|| Relay { shadowsocks_extra_addr_in: SHADOWSOCKS_RELAY_EXTRA_ADDRS.to_vec(), }), location: DUMMY_LOCATION.clone(), + features: Features::default(), }); const SHADOWSOCKS_RELAY_IPV4: Ipv4Addr = Ipv4Addr::new(123, 123, 123, 1); const SHADOWSOCKS_RELAY_IPV6: Ipv6Addr = Ipv6Addr::new(0x123, 0, 0, 0, 0, 0, 0, 2); @@ -319,13 +335,13 @@ fn assert_wireguard_retry_order() { // 1 (wireguard) RelayQueryBuilder::wireguard().build(), // 2 - RelayQueryBuilder::wireguard().port(443).build(), - // 3 RelayQueryBuilder::wireguard() .ip_version(IpVersion::V6) .build(), - // 4 + // 3 RelayQueryBuilder::wireguard().shadowsocks().build(), + // 4 + RelayQueryBuilder::wireguard().quic().build(), // 5 RelayQueryBuilder::wireguard().udp2tcp().build(), // 6 @@ -574,6 +590,7 @@ fn test_wireguard_entry() { shadowsocks_extra_addr_in: vec![], }), location: DUMMY_LOCATION.clone(), + features: Features::default(), }, Relay { hostname: "se10-wireguard".to_string(), @@ -595,6 +612,7 @@ fn test_wireguard_entry() { shadowsocks_extra_addr_in: vec![], }), location: DUMMY_LOCATION.clone(), + features: Features::default(), }, ], }], @@ -872,6 +890,32 @@ fn test_selecting_wireguard_over_shadowsocks_extra_ips() { } } +/// Test whether Quic is always selected as the obfuscation protocol when Quic is selected. +#[test] +fn test_selecting_wireguard_over_quic() { + let relay_selector = RelaySelector::from_list(SelectorConfig::default(), RELAYS.clone()); + + let query = RelayQueryBuilder::wireguard().quic().build(); + assert!(!query.wireguard_constraints().multihop()); + + let relay = relay_selector.get_relay_by_query(query).unwrap(); + match relay { + GetRelay::Wireguard { + obfuscator, + inner: WireguardConfig::Singlehop { .. }, + .. + } => { + assert!(obfuscator.is_some_and(|obfuscator| matches!( + obfuscator.config, + ObfuscatorConfig::Quic { .. }, + ))) + } + wrong_relay => panic!( + "Relay selector should have picked a Wireguard relay with Quic, instead chose {wrong_relay:?}" + ), + } +} + /// Ignore extra IPv4 addresses when overrides are set #[test] fn test_selecting_wireguard_ignore_extra_ips_override_v4() { @@ -1017,7 +1061,7 @@ fn test_selecting_wireguard_endpoint_with_auto_obfuscation() { /// all configurations contain a valid port. #[test] fn test_selected_wireguard_endpoints_use_correct_port_ranges() { - const TCP2UDP_PORTS: [u16; 3] = [80, 443, 5001]; + const TCP2UDP_PORTS: [u16; 2] = [80, 5001]; let relay_selector = default_relay_selector(); // Note that we do *not* specify any port here! let query = RelayQueryBuilder::wireguard().udp2tcp().build(); @@ -1219,6 +1263,7 @@ fn test_include_in_country() { daita: false, }), location: DUMMY_LOCATION.clone(), + features: Features::default(), }, Relay { hostname: "se10-wireguard".to_string(), @@ -1240,6 +1285,7 @@ fn test_include_in_country() { daita: false, }), location: DUMMY_LOCATION.clone(), + features: Features::default(), }, ], }], diff --git a/mullvad-types/src/relay_list.rs b/mullvad-types/src/relay_list.rs index 351c0d0e86..413a23a767 100644 --- a/mullvad-types/src/relay_list.rs +++ b/mullvad-types/src/relay_list.rs @@ -88,6 +88,119 @@ pub struct Relay { pub weight: u64, pub endpoint_data: RelayEndpointData, pub location: Location, + #[serde(default)] + pub features: Features, +} + +/// Extra features enabled on some (Wireguard) relay, such as obfuscation daemons or Daita. +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Features { + daita: Option<Daita>, + quic: Option<Quic>, +} + +impl Features { + /// Equivalent to a relay without any additional features. + pub fn empty() -> Features { + Features { + daita: None, + quic: None, + } + } + + /// Whether Daita is enabled + pub fn daita(&self) -> bool { + self.daita.is_some() + } + + /// Whether Quic is enabled and its config + pub fn quic(&self) -> Option<&Quic> { + self.quic.as_ref() + } + + /// Enable Daita for this relay + pub fn configure_daita(self) -> Self { + let daita = Some(Daita {}); + Self { daita, ..self } + } + + /// Configure QUIC for this relay + pub fn configure_quic(self, options: Quic) -> Self { + let quic = Some(options); + Self { quic, ..self } + } +} + +impl Default for Features { + fn default() -> Self { + Features::empty() + } +} + +/// DAITA doesn't have any configuration options (exposed by the API). +/// +/// Note, an empty struct is not the same as an empty tuple struct according to serde_json! +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Daita {} + +/// Parameters for setting up a QUIC obfuscator (connecting to a masque-proxy running on a relay). +#[derive(Debug, Clone, Deserialize, Serialize)] +pub struct Quic { + /// In-addresses for the QUIC obfuscator. + /// + /// There may be 0, 1 or 2 in IPs, depending on how many masque-proxy daemons running on the + /// relay. Hopefully the API will tell use the correct amount🤞. + addr_in: Vec<IpAddr>, + /// Authorization token + token: String, + /// Hostname where masque proxy is hosted + domain: String, +} + +impl Quic { + pub fn new(addr_in: Vec<IpAddr>, token: String, domain: String) -> Self { + Self { + addr_in, + token, + domain, + } + } + + /// In address as an IPv4 address. + /// + /// Use this if you want to connect to the masque-proxy using IPv4. + pub fn in_ipv4(&self) -> Option<Ipv4Addr> { + let ipv4 = |ipaddr: &IpAddr| match ipaddr { + IpAddr::V4(ipv4_addr) => Some(*ipv4_addr), + IpAddr::V6(_) => None, + }; + self.addr_in.iter().find_map(ipv4) + } + + /// In address as an IPv6 address. + /// + /// Use this if you want to connect to the masque-proxy using IPv6. + pub fn in_ipv6(&self) -> Option<Ipv6Addr> { + let ipv6 = |ipaddr: &IpAddr| match ipaddr { + IpAddr::V4(_) => None, + IpAddr::V6(ipv6_addr) => Some(*ipv6_addr), + }; + self.addr_in.iter().find_map(ipv6) + } + + /// Port of the masque-proxy daemon. + pub const fn port(&self) -> u16 { + // The point of the masque-proxy is to look like a regular web server serving http traffic. + 443 + } + + pub fn hostname(&self) -> &str { + &self.domain + } + + pub fn auth_token(&self) -> &str { + &self.token + } } impl Relay { @@ -117,7 +230,7 @@ impl PartialEq for Relay { /// # Example /// /// ```rust - /// # use mullvad_types::{relay_list::Relay, relay_list::{RelayEndpointData, WireguardRelayEndpointData}}; + /// # use mullvad_types::{relay_list::{Relay, Features}, relay_list::{RelayEndpointData, WireguardRelayEndpointData}}; /// # use talpid_types::net::wireguard::PublicKey; /// /// let relay = Relay { @@ -147,6 +260,7 @@ impl PartialEq for Relay { /// # latitude: 57.71, /// # longitude: 11.97, /// # }, + /// # features: Features::default(), /// }; /// /// let mut different_relay = relay.clone(); @@ -229,6 +343,7 @@ pub struct WireguardRelayEndpointData { /// Public key used by the relay peer pub public_key: wireguard::PublicKey, /// Whether the server supports DAITA + /// FIXME: This has been superceded by [Features] + [Daita]. #[serde(default)] pub daita: bool, /// Optional IP addresses used by Shadowsocks diff --git a/talpid-types/src/net/obfuscation.rs b/talpid-types/src/net/obfuscation.rs index d2735c6a75..161fac92a0 100644 --- a/talpid-types/src/net/obfuscation.rs +++ b/talpid-types/src/net/obfuscation.rs @@ -14,6 +14,7 @@ pub enum ObfuscatorConfig { Quic { hostname: String, endpoint: SocketAddr, + auth_token: String, }, } @@ -28,10 +29,7 @@ impl ObfuscatorConfig { address: *endpoint, protocol: TransportProtocol::Udp, }, - ObfuscatorConfig::Quic { - hostname: _, - endpoint, - } => Endpoint { + ObfuscatorConfig::Quic { endpoint, .. } => Endpoint { address: *endpoint, protocol: TransportProtocol::Udp, }, diff --git a/talpid-wireguard/src/obfuscation.rs b/talpid-wireguard/src/obfuscation.rs index 6677d630cb..1b81dd190a 100644 --- a/talpid-wireguard/src/obfuscation.rs +++ b/talpid-wireguard/src/obfuscation.rs @@ -16,9 +16,6 @@ use tunnel_obfuscation::{ create_obfuscator, quic, shadowsocks, udp2tcp, Settings as ObfuscationSettings, }; -/// Test authentication header to set for the CONNECT request. -const AUTH_HEADER: &str = "test"; - /// Begin running obfuscation machine, if configured. This function will patch `config`'s endpoint /// to point to an endpoint on localhost pub async fn apply_obfuscation_config( @@ -99,16 +96,18 @@ fn settings_from_config( fwmark, }) } - ObfuscatorConfig::Quic { hostname, endpoint } => { - ObfuscationSettings::Quic(quic::Settings { - quic_endpoint: *endpoint, - wireguard_endpoint: SocketAddr::from((Ipv4Addr::LOCALHOST, 51820)), - hostname: hostname.to_owned(), - token: AUTH_HEADER.to_owned(), - #[cfg(target_os = "linux")] - fwmark, - }) - } + ObfuscatorConfig::Quic { + hostname, + endpoint, + auth_token, + } => ObfuscationSettings::Quic(quic::Settings { + quic_endpoint: *endpoint, + wireguard_endpoint: SocketAddr::from((Ipv4Addr::LOCALHOST, 51820)), + hostname: hostname.to_owned(), + token: auth_token.to_owned(), + #[cfg(target_os = "linux")] + fwmark, + }), } } diff --git a/tunnel-obfuscation/src/quic.rs b/tunnel-obfuscation/src/quic.rs index e0b16ecb66..382d998020 100644 --- a/tunnel-obfuscation/src/quic.rs +++ b/tunnel-obfuscation/src/quic.rs @@ -33,13 +33,23 @@ pub struct Settings { pub wireguard_endpoint: SocketAddr, /// Hostname to use for QUIC pub hostname: String, - /// Auth token to use for QUIC. Must NOT be prefixed with "Bearer". + /// Authentication token to set for the CONNECT request when establishing a QUIC connection. + /// Must NOT be prefixed with "Bearer". pub token: String, /// fwmark to apply to use for the QUIC connection #[cfg(target_os = "linux")] pub fwmark: Option<u32>, } +impl Settings { + /// The masque-proxy server expects the Authentication header to be prefixed with "Bearer ", so + /// prefix the auth token with that. + fn auth_header(&self) -> String { + debug_assert!(!self.token.starts_with("Bearer")); + format!("Bearer {token}", token = self.token) + } +} + impl Quic { pub(crate) async fn new(settings: &Settings) -> Result<Self> { let local_socket = UdpSocket::bind(SocketAddr::from((Ipv4Addr::LOCALHOST, 0))) @@ -47,7 +57,6 @@ impl Quic { .map_err(Error::BindError)?; let local_endpoint = local_socket.local_addr().unwrap(); - let token = settings.token.clone(); let config_builder = ClientConfig::builder() .client_socket(local_socket) @@ -55,7 +64,7 @@ impl Quic { .server_addr(settings.quic_endpoint) .server_host(settings.hostname.clone()) .target_addr(settings.wireguard_endpoint) - .auth_header(Some(format!("Bearer {token}").to_owned())); + .auth_header(Some(settings.auth_header())); #[cfg(target_os = "linux")] let config_builder = config_builder.fwmark(settings.fwmark); |
