summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2025-07-04 16:49:16 +0200
committerMarkus Pettersson <markus.pettersson@mullvad.net>2025-07-09 15:16:36 +0200
commitb4fcf2661ff0318c4822a82613932be29ab62072 (patch)
tree957e06d94fa456a901c0348db063690e6e12f9bc
parent1a3c9c16b64eb8c30db150a2719a1ab028de9cab (diff)
downloadmullvadvpn-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.md7
-rw-r--r--mullvad-api/src/bin/relay_list.rs6
-rw-r--r--mullvad-api/src/relay_list.rs23
-rw-r--r--mullvad-management-interface/src/types/conversions/relay_list.rs11
-rw-r--r--mullvad-relay-selector/src/relay_selector/helpers.rs22
-rw-r--r--mullvad-relay-selector/src/relay_selector/matcher.rs7
-rw-r--r--mullvad-relay-selector/src/relay_selector/mod.rs22
-rw-r--r--mullvad-relay-selector/src/relay_selector/query.rs22
-rw-r--r--mullvad-relay-selector/tests/relay_selector.rs60
-rw-r--r--mullvad-types/src/relay_list.rs117
-rw-r--r--talpid-types/src/net/obfuscation.rs6
-rw-r--r--talpid-wireguard/src/obfuscation.rs25
-rw-r--r--tunnel-obfuscation/src/quic.rs15
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);