summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Relay.kt6
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayEndpointData.kt17
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardEndpointData.kt8
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardRelayEndpointData.kt (renamed from android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayTunnels.kt)2
-rw-r--r--android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt2
-rw-r--r--gui/src/main/daemon-rpc.ts44
-rw-r--r--gui/src/main/index.ts16
-rw-r--r--gui/src/shared/daemon-rpc-types.ts35
-rw-r--r--mullvad-api/src/relay_list.rs313
-rw-r--r--mullvad-cli/src/cmds/bridge.rs3
-rw-r--r--mullvad-cli/src/cmds/relay.rs14
-rw-r--r--mullvad-jni/src/classes.rs6
-rw-r--r--mullvad-management-interface/proto/management_interface.proto24
-rw-r--r--mullvad-management-interface/src/types.rs150
-rw-r--r--mullvad-relay-selector/src/lib.rs359
-rw-r--r--mullvad-relay-selector/src/matcher.rs192
-rw-r--r--mullvad-types/src/endpoint.rs45
-rw-r--r--mullvad-types/src/relay_constraints.rs17
-rw-r--r--mullvad-types/src/relay_list.rs159
19 files changed, 590 insertions, 822 deletions
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Relay.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Relay.kt
index 23f9d87f77..dbb74b129a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Relay.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/Relay.kt
@@ -7,8 +7,8 @@ import kotlinx.parcelize.Parcelize
data class Relay(
val hostname: String,
val active: Boolean,
- val tunnels: RelayTunnels
+ val endpointData: RelayEndpointData
) : Parcelable {
- val hasWireguardTunnels
- get() = !tunnels.wireguard.isEmpty()
+ val isWireguardRelay
+ get() = endpointData is RelayEndpointData.Wireguard
}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayEndpointData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayEndpointData.kt
new file mode 100644
index 0000000000..ecc2d4d002
--- /dev/null
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayEndpointData.kt
@@ -0,0 +1,17 @@
+package net.mullvad.mullvadvpn.model
+
+import android.os.Parcelable
+import kotlinx.parcelize.Parcelize
+
+sealed class RelayEndpointData : Parcelable {
+ @Parcelize
+ object Openvpn : RelayEndpointData()
+
+ @Parcelize
+ object Bridge : RelayEndpointData()
+
+ @Parcelize
+ data class Wireguard(
+ val wireguardRelayEndpointData: WireguardRelayEndpointData
+ ) : RelayEndpointData()
+}
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardEndpointData.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardEndpointData.kt
deleted file mode 100644
index aee9b56082..0000000000
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardEndpointData.kt
+++ /dev/null
@@ -1,8 +0,0 @@
-package net.mullvad.mullvadvpn.model
-
-import android.os.Parcelable
-import kotlinx.parcelize.Parcelize
-
-@Suppress("PARCELABLE_PRIMARY_CONSTRUCTOR_IS_EMPTY")
-@Parcelize
-class WireguardEndpointData() : Parcelable
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayTunnels.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardRelayEndpointData.kt
index 8856f6b4bd..b3ef17f98a 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/RelayTunnels.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/model/WireguardRelayEndpointData.kt
@@ -4,4 +4,4 @@ import android.os.Parcelable
import kotlinx.parcelize.Parcelize
@Parcelize
-data class RelayTunnels(val wireguard: ArrayList<WireguardEndpointData>) : Parcelable
+object WireguardRelayEndpointData : Parcelable
diff --git a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt
index aed15f9508..915e6ca181 100644
--- a/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt
+++ b/android/app/src/main/kotlin/net/mullvad/mullvadvpn/relaylist/RelayList.kt
@@ -16,7 +16,7 @@ class RelayList {
val relays = mutableListOf<Relay>()
val relayCity = RelayCity(relayCountry, city.name, city.code, false, relays)
- val validCityRelays = city.relays.filter { relay -> relay.hasWireguardTunnels }
+ val validCityRelays = city.relays.filter { relay -> relay.isWireguardRelay }
for (relay in validCityRelays) {
relays.add(Relay(relayCity, relay.hostname, relay.active))
diff --git a/gui/src/main/daemon-rpc.ts b/gui/src/main/daemon-rpc.ts
index e704979a40..a868208ac2 100644
--- a/gui/src/main/daemon-rpc.ts
+++ b/gui/src/main/daemon-rpc.ts
@@ -29,24 +29,22 @@ import {
ILocation,
IObfuscationEndpoint,
IOpenVpnConstraints,
- IOpenVpnTunnelData,
IProxyEndpoint,
IRelayList,
IRelayListCity,
IRelayListCountry,
IRelayListHostname,
ISettings,
- IShadowsocksEndpointData,
ITunnelOptions,
ITunnelStateRelayInfo,
IWireguardConstraints,
- IWireguardTunnelData,
LoggedInDeviceState,
LoggedOutDeviceState,
ObfuscationType,
Ownership,
ProxySettings,
ProxyType,
+ RelayEndpointType,
RelayLocation,
RelayProtocol,
RelaySettings,
@@ -732,34 +730,17 @@ function convertFromRelayListCity(city: grpcTypes.RelayListCity.AsObject): IRela
function convertFromRelayListRelay(relay: grpcTypes.Relay.AsObject): IRelayListHostname {
return {
...relay,
- tunnels: relay.tunnels && {
- ...relay.tunnels,
- openvpn: relay.tunnels.openvpnList.map(convertFromOpenvpnList),
- wireguard: relay.tunnels.wireguardList.map(convertFromWireguardList),
- },
- bridges: relay.bridges && {
- shadowsocks: relay.bridges.shadowsocksList.map(convertFromShadowsocksList),
- },
+ endpointType: convertFromRelayType(relay.endpointType),
};
}
-function convertFromOpenvpnList(
- openvpn: grpcTypes.OpenVpnEndpointData.AsObject,
-): IOpenVpnTunnelData {
- return {
- ...openvpn,
- protocol: convertFromTransportProtocol(openvpn.protocol),
- };
-}
-
-function convertFromWireguardList(
- wireguard: grpcTypes.WireguardEndpointData.AsObject,
-): IWireguardTunnelData {
- return {
- ...wireguard,
- portRanges: wireguard.portRangesList,
- publicKey: convertFromWireguardKey(wireguard.publicKey),
+function convertFromRelayType(relayType: grpcTypes.Relay.RelayType): RelayEndpointType {
+ const protocolMap: Record<grpcTypes.Relay.RelayType, RelayEndpointType> = {
+ [grpcTypes.Relay.RelayType.OPENVPN]: 'openvpn',
+ [grpcTypes.Relay.RelayType.BRIDGE]: 'bridge',
+ [grpcTypes.Relay.RelayType.WIREGUARD]: 'wireguard',
};
+ return protocolMap[relayType];
}
function convertFromWireguardKey(publicKey: Uint8Array | string): string {
@@ -769,15 +750,6 @@ function convertFromWireguardKey(publicKey: Uint8Array | string): string {
return Buffer.from(publicKey).toString('base64');
}
-function convertFromShadowsocksList(
- shadowsocks: grpcTypes.ShadowsocksEndpointData.AsObject,
-): IShadowsocksEndpointData {
- return {
- ...shadowsocks,
- protocol: convertFromTransportProtocol(shadowsocks.protocol),
- };
-}
-
function convertFromTransportProtocol(protocol: grpcTypes.TransportProtocol): RelayProtocol {
const protocolMap: Record<grpcTypes.TransportProtocol, RelayProtocol> = {
[grpcTypes.TransportProtocol.TCP]: 'tcp',
diff --git a/gui/src/main/index.ts b/gui/src/main/index.ts
index 83b76292c4..b8a8a9ca28 100644
--- a/gui/src/main/index.ts
+++ b/gui/src/main/index.ts
@@ -1026,23 +1026,19 @@ class ApplicationMain {
.map((city) => ({
...city,
relays: city.relays.filter((relay) => {
- if (relay.tunnels) {
+ if (relay.endpointType != 'bridge') {
switch (tunnelProtocol) {
case 'openvpn':
- return relay.tunnels.openvpn.length > 0;
+ return relay.endpointType == 'openvpn';
case 'wireguard':
- return relay.tunnels.wireguard.length > 0;
+ return relay.endpointType == 'wireguard';
case 'any': {
const useMultihop =
'normal' in relaySettings &&
relaySettings.normal.wireguardConstraints.useMultihop;
- if (useMultihop) {
- return relay.tunnels.wireguard.length > 0;
- } else {
- return relay.tunnels.openvpn.length > 0 || relay.tunnels.wireguard.length > 0;
- }
+ return !useMultihop || relay.endpointType == 'wireguard';
}
default:
return false;
@@ -1072,9 +1068,7 @@ class ApplicationMain {
cities: country.cities
.map((city) => ({
...city,
- relays: city.relays.filter(
- (relay) => relay.bridges && relay.bridges.shadowsocks.length > 0,
- ),
+ relays: city.relays.filter((relay) => relay.endpointType == 'bridge'),
}))
.filter((city) => city.relays.length > 0),
}))
diff --git a/gui/src/shared/daemon-rpc-types.ts b/gui/src/shared/daemon-rpc-types.ts
index 8bee8003b0..d0507cab98 100644
--- a/gui/src/shared/daemon-rpc-types.ts
+++ b/gui/src/shared/daemon-rpc-types.ts
@@ -250,41 +250,10 @@ export interface IRelayListHostname {
active: boolean;
weight: number;
owned: boolean;
- tunnels?: IRelayTunnels;
- bridges?: IRelayBridges;
+ endpointType: RelayEndpointType;
}
-export interface IRelayTunnels {
- openvpn: IOpenVpnTunnelData[];
- wireguard: IWireguardTunnelData[];
-}
-
-export interface IRelayBridges {
- shadowsocks: IShadowsocksEndpointData[];
-}
-
-export interface IOpenVpnTunnelData {
- port: number;
- protocol: RelayProtocol;
-}
-
-export interface IWireguardTunnelData {
- portRanges: Array<IPortRange>;
- // Public key of the tunnel.
- publicKey: string;
-}
-
-export interface IPortRange {
- first: number;
- last: number;
-}
-
-export interface IShadowsocksEndpointData {
- port: number;
- cipher: string;
- password: string;
- protocol: RelayProtocol;
-}
+export type RelayEndpointType = 'wireguard' | 'openvpn' | 'bridge';
export interface ITunnelOptions {
openvpn: {
diff --git a/mullvad-api/src/relay_list.rs b/mullvad-api/src/relay_list.rs
index a2d699e248..951493db02 100644
--- a/mullvad-api/src/relay_list.rs
+++ b/mullvad-api/src/relay_list.rs
@@ -106,10 +106,6 @@ impl ServerRelayList {
}
}
- Self::add_openvpn_relays(&mut countries, openvpn);
- Self::add_wireguard_relays(&mut countries, wireguard);
- Self::add_bridge_relays(&mut countries, bridge);
-
relay_list::RelayList {
etag: etag.map(|mut tag| {
if tag.starts_with('"') {
@@ -117,160 +113,15 @@ impl ServerRelayList {
}
tag
}),
+ openvpn: openvpn.extract_relays(&mut countries),
+ wireguard: wireguard.extract_relays(&mut countries),
+ bridge: bridge.extract_relays(&mut countries),
countries: countries
.into_iter()
.map(|(_key, country)| country)
.collect(),
}
}
-
- fn add_openvpn_relays(
- countries: &mut BTreeMap<String, relay_list::RelayListCountry>,
- openvpn: OpenVpn,
- ) {
- let openvpn_endpoint_data = openvpn.ports;
- for mut openvpn_relay in openvpn.relays.into_iter() {
- openvpn_relay.convert_to_lowercase();
- if let Some((country_code, city_code)) = split_location_code(&openvpn_relay.location) {
- if let Some(country) = countries.get_mut(country_code) {
- if let Some(city) = country
- .cities
- .iter_mut()
- .find(|city| city.code == city_code)
- {
- let location = location::Location {
- country: country.name.clone(),
- country_code: country.code.clone(),
- city: city.name.clone(),
- city_code: city.code.clone(),
- latitude: city.latitude,
- longitude: city.longitude,
- };
- match city
- .relays
- .iter_mut()
- .find(|r| r.hostname == openvpn_relay.hostname)
- {
- Some(relay) => relay.tunnels.openvpn = openvpn_endpoint_data.clone(),
- None => {
- let mut relay = relay(openvpn_relay, location);
- relay.tunnels.openvpn = openvpn_endpoint_data.clone();
- city.relays.push(relay);
- }
- };
- }
- };
- }
- }
- }
-
- fn add_wireguard_relays(
- countries: &mut BTreeMap<String, relay_list::RelayListCountry>,
- wireguard: Wireguard,
- ) {
- let Wireguard {
- port_ranges,
- ipv4_gateway,
- ipv6_gateway,
- relays,
- } = wireguard;
-
- let wireguard_endpoint_data =
- |public_key: wireguard::PublicKey| relay_list::WireguardEndpointData {
- port_ranges: port_ranges.clone(),
- ipv4_gateway,
- ipv6_gateway,
- public_key,
- };
-
- for mut wireguard_relay in relays {
- wireguard_relay.relay.convert_to_lowercase();
- if let Some((country_code, city_code)) =
- split_location_code(&wireguard_relay.relay.location)
- {
- if let Some(country) = countries.get_mut(country_code) {
- if let Some(city) = country
- .cities
- .iter_mut()
- .find(|city| city.code == city_code)
- {
- let location = location::Location {
- country: country.name.clone(),
- country_code: country.code.clone(),
- city: city.name.clone(),
- city_code: city.code.clone(),
- latitude: city.latitude,
- longitude: city.longitude,
- };
- match city
- .relays
- .iter_mut()
- .find(|r| r.hostname == wireguard_relay.relay.hostname)
- {
- Some(relay) => relay
- .tunnels
- .wireguard
- .push(wireguard_endpoint_data(wireguard_relay.public_key)),
- None => {
- let mut relay = relay(wireguard_relay.relay, location);
- relay.ipv6_addr_in = Some(wireguard_relay.ipv6_addr_in);
- relay.tunnels.wireguard =
- vec![wireguard_endpoint_data(wireguard_relay.public_key)];
- city.relays.push(relay);
- }
- };
- }
- };
- }
- }
- }
-
- fn add_bridge_relays(
- countries: &mut BTreeMap<String, relay_list::RelayListCountry>,
- bridges: Bridges,
- ) {
- let Bridges {
- relays,
- shadowsocks,
- } = bridges;
-
- for mut bridge_relay in relays {
- bridge_relay.convert_to_lowercase();
- if let Some((country_code, city_code)) = split_location_code(&bridge_relay.location) {
- if let Some(country) = countries.get_mut(country_code) {
- if let Some(city) = country
- .cities
- .iter_mut()
- .find(|city| city.code == city_code)
- {
- let location = location::Location {
- country: country.name.clone(),
- country_code: country.code.clone(),
- city: city.name.clone(),
- city_code: city.code.clone(),
- latitude: city.latitude,
- longitude: city.longitude,
- };
-
- match city
- .relays
- .iter_mut()
- .find(|r| r.hostname == bridge_relay.hostname)
- {
- Some(relay) => {
- relay.bridges.shadowsocks = shadowsocks.clone();
- }
- None => {
- let mut relay = relay(bridge_relay, location);
- relay.bridges.shadowsocks = shadowsocks.clone();
- city.relays.push(relay);
- }
- };
- }
- };
- }
- }
- }
}
/// Splits a location code into a country code and a city code. The input is expected to be in a
@@ -301,19 +152,21 @@ fn location_to_city(location: &Location, code: String) -> relay_list::RelayListC
}
}
-fn relay(relay: Relay, location: location::Location) -> relay_list::Relay {
+fn into_mullvad_relay(
+ relay: Relay,
+ location: location::Location,
+ endpoint_data: relay_list::RelayEndpointData,
+) -> relay_list::Relay {
relay_list::Relay {
hostname: relay.hostname,
ipv4_addr_in: relay.ipv4_addr_in,
- ipv6_addr_in: None,
+ ipv6_addr_in: relay.ipv6_addr_in,
include_in_country: relay.include_in_country,
active: relay.active,
owned: relay.owned,
provider: relay.provider,
weight: relay.weight,
- tunnels: Default::default(),
- bridges: Default::default(),
- obfuscators: Default::default(),
+ endpoint_data,
location: Some(location),
}
}
@@ -328,10 +181,44 @@ struct Location {
#[derive(Debug, serde::Deserialize)]
struct OpenVpn {
- ports: Vec<relay_list::OpenVpnEndpointData>,
+ #[serde(flatten)]
+ ports: relay_list::OpenVpnEndpointData,
relays: Vec<Relay>,
}
+impl OpenVpn {
+ /// Consumes `self` and appends all its relays to `countries`.
+ fn extract_relays(
+ self,
+ countries: &mut BTreeMap<String, relay_list::RelayListCountry>,
+ ) -> relay_list::OpenVpnEndpointData {
+ for mut openvpn_relay in self.relays.into_iter() {
+ openvpn_relay.convert_to_lowercase();
+ if let Some((country_code, city_code)) = split_location_code(&openvpn_relay.location) {
+ if let Some(country) = countries.get_mut(country_code) {
+ if let Some(city) = country
+ .cities
+ .iter_mut()
+ .find(|city| city.code == city_code)
+ {
+ let location = location::Location {
+ country: country.name.clone(),
+ country_code: country.code.clone(),
+ city: city.name.clone(),
+ city_code: city.code.clone(),
+ latitude: city.latitude,
+ longitude: city.longitude,
+ };
+ let relay = openvpn_relay.into_openvpn_mullvad_relay(location);
+ city.relays.push(relay);
+ }
+ };
+ }
+ }
+ self.ports
+ }
+}
+
#[derive(Debug, serde::Deserialize)]
struct Relay {
hostname: String,
@@ -340,11 +227,20 @@ struct Relay {
location: String,
provider: String,
ipv4_addr_in: Ipv4Addr,
+ ipv6_addr_in: Option<Ipv6Addr>,
weight: u64,
include_in_country: bool,
}
impl Relay {
+ fn into_openvpn_mullvad_relay(self, location: location::Location) -> relay_list::Relay {
+ into_mullvad_relay(self, location, relay_list::RelayEndpointData::Openvpn)
+ }
+
+ fn into_bridge_mullvad_relay(self, location: location::Location) -> relay_list::Relay {
+ into_mullvad_relay(self, location, relay_list::RelayEndpointData::Bridge)
+ }
+
fn convert_to_lowercase(&mut self) {
self.hostname = self.hostname.to_lowercase();
self.location = self.location.to_lowercase();
@@ -359,16 +255,115 @@ struct Wireguard {
relays: Vec<WireGuardRelay>,
}
+impl From<&Wireguard> for relay_list::WireguardEndpointData {
+ fn from(wg: &Wireguard) -> Self {
+ Self {
+ port_ranges: wg.port_ranges.clone(),
+ ipv4_gateway: wg.ipv4_gateway,
+ ipv6_gateway: wg.ipv6_gateway,
+ udp2tcp_ports: vec![],
+ }
+ }
+}
+
+impl Wireguard {
+ /// Consumes `self` and appends all its relays to `countries`.
+ fn extract_relays(
+ self,
+ countries: &mut BTreeMap<String, relay_list::RelayListCountry>,
+ ) -> relay_list::WireguardEndpointData {
+ let endpoint_data = relay_list::WireguardEndpointData::from(&self);
+ let relays = self.relays;
+
+ for mut wireguard_relay in relays {
+ wireguard_relay.relay.convert_to_lowercase();
+ if let Some((country_code, city_code)) =
+ split_location_code(&wireguard_relay.relay.location)
+ {
+ if let Some(country) = countries.get_mut(country_code) {
+ if let Some(city) = country
+ .cities
+ .iter_mut()
+ .find(|city| city.code == city_code)
+ {
+ let location = location::Location {
+ country: country.name.clone(),
+ country_code: country.code.clone(),
+ city: city.name.clone(),
+ city_code: city.code.clone(),
+ latitude: city.latitude,
+ longitude: city.longitude,
+ };
+
+ let relay = wireguard_relay.into_mullvad_relay(location);
+ city.relays.push(relay);
+ }
+ };
+ }
+ }
+
+ endpoint_data
+ }
+}
+
#[derive(Debug, serde::Deserialize)]
struct WireGuardRelay {
#[serde(flatten)]
relay: Relay,
- ipv6_addr_in: Ipv6Addr,
public_key: wireguard::PublicKey,
}
+impl WireGuardRelay {
+ fn into_mullvad_relay(self, location: location::Location) -> relay_list::Relay {
+ into_mullvad_relay(
+ self.relay,
+ location,
+ relay_list::RelayEndpointData::Wireguard(relay_list::WireguardRelayEndpointData {
+ public_key: self.public_key,
+ }),
+ )
+ }
+}
+
#[derive(Debug, serde::Deserialize)]
struct Bridges {
shadowsocks: Vec<relay_list::ShadowsocksEndpointData>,
relays: Vec<Relay>,
}
+
+impl Bridges {
+ /// Consumes `self` and appends all its relays to `countries`.
+ fn extract_relays(
+ self,
+ countries: &mut BTreeMap<String, relay_list::RelayListCountry>,
+ ) -> relay_list::BridgeEndpointData {
+ for mut bridge_relay in self.relays {
+ bridge_relay.convert_to_lowercase();
+ if let Some((country_code, city_code)) = split_location_code(&bridge_relay.location) {
+ if let Some(country) = countries.get_mut(country_code) {
+ if let Some(city) = country
+ .cities
+ .iter_mut()
+ .find(|city| city.code == city_code)
+ {
+ let location = location::Location {
+ country: country.name.clone(),
+ country_code: country.code.clone(),
+ city: city.name.clone(),
+ city_code: city.code.clone(),
+ latitude: city.latitude,
+ longitude: city.longitude,
+ };
+
+ let relay = bridge_relay.into_bridge_mullvad_relay(location);
+ city.relays.push(relay);
+ }
+ };
+ }
+ }
+
+ relay_list::BridgeEndpointData {
+ shadowsocks: self.shadowsocks,
+ }
+ }
+}
diff --git a/mullvad-cli/src/cmds/bridge.rs b/mullvad-cli/src/cmds/bridge.rs
index b253ae63a9..27532f1416 100644
--- a/mullvad-cli/src/cmds/bridge.rs
+++ b/mullvad-cli/src/cmds/bridge.rs
@@ -439,8 +439,7 @@ impl Bridge {
.filter_map(|mut city| {
city.relays.retain(|relay| {
relay.active
- && relay.bridges.is_some()
- && !relay.bridges.as_ref().unwrap().shadowsocks.is_empty()
+ && relay.endpoint_type == (types::relay::RelayType::Bridge as i32)
});
if !city.relays.is_empty() {
Some(city)
diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs
index 46d4567724..8e68adda6c 100644
--- a/mullvad-cli/src/cmds/relay.rs
+++ b/mullvad-cli/src/cmds/relay.rs
@@ -682,13 +682,9 @@ impl Relay {
city.name, city.code, city.latitude, city.longitude
);
for relay in &city.relays {
- let tunnels = relay.tunnels.as_ref().unwrap();
- let supports_openvpn = !tunnels.openvpn.is_empty();
- let supports_wireguard = !tunnels.wireguard.is_empty();
- let support_msg = match (supports_openvpn, supports_wireguard) {
- (true, true) => "OpenVPN and WireGuard",
- (true, false) => "OpenVPN",
- (false, true) => "WireGuard",
+ let support_msg = match relay.endpoint_type {
+ i if i == i32::from(types::relay::RelayType::Openvpn) => "OpenVPN",
+ i if i == i32::from(types::relay::RelayType::Wireguard) => "WireGuard",
_ => unreachable!("Bug in relay filtering earlier on"),
};
let ownership = if relay.owned {
@@ -737,9 +733,7 @@ impl Relay {
.filter_map(|mut city| {
city.relays.retain(|relay| {
relay.active
- && relay.tunnels.is_some()
- && !(relay.tunnels.as_ref().unwrap().openvpn.is_empty()
- && relay.tunnels.as_ref().unwrap().wireguard.is_empty())
+ && relay.endpoint_type != (types::relay::RelayType::Bridge as i32)
});
if !city.relays.is_empty() {
Some(city)
diff --git a/mullvad-jni/src/classes.rs b/mullvad-jni/src/classes.rs
index 026d7462c5..9a124f2a12 100644
--- a/mullvad-jni/src/classes.rs
+++ b/mullvad-jni/src/classes.rs
@@ -28,6 +28,9 @@ pub const CLASSES: &[&str] = &[
"net/mullvad/mullvadvpn/model/PublicKey",
"net/mullvad/mullvadvpn/model/Relay",
"net/mullvad/mullvadvpn/model/RelayConstraints",
+ "net/mullvad/mullvadvpn/model/RelayEndpointData$Bridge",
+ "net/mullvad/mullvadvpn/model/RelayEndpointData$Openvpn",
+ "net/mullvad/mullvadvpn/model/RelayEndpointData$Wireguard",
"net/mullvad/mullvadvpn/model/RelayList",
"net/mullvad/mullvadvpn/model/RelayListCity",
"net/mullvad/mullvadvpn/model/RelayListCountry",
@@ -36,7 +39,6 @@ pub const CLASSES: &[&str] = &[
"net/mullvad/mullvadvpn/model/RelaySettingsUpdate$CustomTunnelEndpoint",
"net/mullvad/mullvadvpn/model/RelaySettingsUpdate$Normal",
"net/mullvad/mullvadvpn/model/RelayConstraintsUpdate",
- "net/mullvad/mullvadvpn/model/RelayTunnels",
"net/mullvad/mullvadvpn/model/Settings",
"net/mullvad/mullvadvpn/model/TunnelState$Error",
"net/mullvad/mullvadvpn/model/TunnelState$Connected",
@@ -46,7 +48,7 @@ pub const CLASSES: &[&str] = &[
"net/mullvad/mullvadvpn/model/VoucherSubmission",
"net/mullvad/mullvadvpn/model/VoucherSubmissionResult",
"net/mullvad/mullvadvpn/model/LoginResult",
- "net/mullvad/mullvadvpn/model/WireguardEndpointData",
+ "net/mullvad/mullvadvpn/model/WireguardRelayEndpointData",
"net/mullvad/mullvadvpn/service/MullvadDaemon",
"net/mullvad/mullvadvpn/service/MullvadVpnService",
"net/mullvad/talpid/net/Endpoint",
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index 2f39f496d6..4c6ee5ab71 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -6,6 +6,7 @@ import "google/protobuf/empty.proto";
import "google/protobuf/timestamp.proto";
import "google/protobuf/wrappers.proto";
import "google/protobuf/duration.proto";
+import "google/protobuf/any.proto";
service ManagementService {
// Control and get tunnel state
@@ -509,6 +510,12 @@ message RelayListCity {
}
message Relay {
+ enum RelayType {
+ OPENVPN = 0;
+ BRIDGE = 1;
+ WIREGUARD = 2;
+ }
+
string hostname = 1;
string ipv4_addr_in = 2;
string ipv6_addr_in = 3;
@@ -517,11 +524,15 @@ message Relay {
bool owned = 6;
string provider = 7;
fixed64 weight = 8;
- RelayTunnels tunnels = 9;
- RelayBridges bridges = 10;
+ RelayType endpoint_type = 9;
+ google.protobuf.Any endpoint_data = 10;
Location location = 11;
}
+message WireguardRelayEndpointData {
+ bytes public_key = 1;
+}
+
message Location {
string country = 1;
string country_code = 2;
@@ -531,15 +542,6 @@ message Location {
double longitude = 6;
}
-message RelayTunnels {
- repeated OpenVpnEndpointData openvpn = 1;
- repeated WireguardEndpointData wireguard = 2;
-}
-
-message RelayBridges {
- repeated ShadowsocksEndpointData shadowsocks = 1;
-}
-
enum TransportProtocol {
UDP = 0;
TCP = 1;
diff --git a/mullvad-management-interface/src/types.rs b/mullvad-management-interface/src/types.rs
index cc2086d66e..d3404e2298 100644
--- a/mullvad-management-interface/src/types.rs
+++ b/mullvad-management-interface/src/types.rs
@@ -718,6 +718,8 @@ impl From<mullvad_types::relay_list::RelayListCountry> for RelayListCountry {
impl From<mullvad_types::relay_list::Relay> for Relay {
fn from(relay: mullvad_types::relay_list::Relay) -> Self {
+ use mullvad_types::relay_list::RelayEndpointData as MullvadEndpointData;
+
Self {
hostname: relay.hostname,
ipv4_addr_in: relay.ipv4_addr_in.to_string(),
@@ -730,51 +732,20 @@ impl From<mullvad_types::relay_list::Relay> for Relay {
owned: relay.owned,
provider: relay.provider,
weight: relay.weight,
- tunnels: Some(RelayTunnels {
- openvpn: relay
- .tunnels
- .openvpn
- .iter()
- .map(|endpoint| OpenVpnEndpointData {
- port: u32::from(endpoint.port),
- protocol: i32::from(TransportProtocol::from(endpoint.protocol)),
- })
- .collect(),
- wireguard: relay
- .tunnels
- .wireguard
- .iter()
- .map(|endpoint| {
- let port_ranges = endpoint
- .port_ranges
- .iter()
- .map(|range| PortRange {
- first: u32::from(range.0),
- last: u32::from(range.1),
- })
- .collect();
- WireguardEndpointData {
- port_ranges,
- ipv4_gateway: endpoint.ipv4_gateway.to_string(),
- ipv6_gateway: endpoint.ipv6_gateway.to_string(),
- public_key: endpoint.public_key.as_bytes().to_vec(),
- }
- })
- .collect(),
- }),
- bridges: Some(RelayBridges {
- shadowsocks: relay
- .bridges
- .shadowsocks
- .into_iter()
- .map(|endpoint| ShadowsocksEndpointData {
- port: u32::from(endpoint.port),
- cipher: endpoint.cipher,
- password: endpoint.password,
- protocol: i32::from(TransportProtocol::from(endpoint.protocol)),
- })
- .collect(),
- }),
+ endpoint_type: match &relay.endpoint_data {
+ MullvadEndpointData::Openvpn => relay::RelayType::Openvpn as i32,
+ MullvadEndpointData::Bridge => relay::RelayType::Bridge as i32,
+ MullvadEndpointData::Wireguard(_) => relay::RelayType::Wireguard as i32,
+ },
+ endpoint_data: match relay.endpoint_data {
+ MullvadEndpointData::Wireguard(data) => Some(to_proto_any(
+ "mullvad_daemon.management_interface/WireguardRelayEndpointData",
+ WireguardRelayEndpointData {
+ public_key: data.public_key.as_bytes().to_vec(),
+ },
+ )),
+ _ => None,
+ },
location: relay.location.map(|location| Location {
country: location.country,
country_code: location.country_code,
@@ -787,6 +758,76 @@ impl From<mullvad_types::relay_list::Relay> for Relay {
}
}
+impl TryFrom<Relay> for mullvad_types::relay_list::Relay {
+ type Error = FromProtobufTypeError;
+
+ fn try_from(relay: Relay) -> Result<Self, Self::Error> {
+ use mullvad_types::{
+ location::Location as MullvadLocation,
+ relay_list::{Relay as MullvadRelay, RelayEndpointData as MullvadEndpointData},
+ };
+
+ let endpoint_data = match relay.endpoint_type {
+ i if i == relay::RelayType::Openvpn as i32 => MullvadEndpointData::Openvpn,
+ i if i == relay::RelayType::Bridge as i32 => MullvadEndpointData::Bridge,
+ i if i == relay::RelayType::Wireguard as i32 => {
+ let data = relay
+ .endpoint_data
+ .ok_or(FromProtobufTypeError::InvalidArgument(
+ "missing endpoint wg data",
+ ))?;
+ let data: WireguardRelayEndpointData = try_from_proto_any(
+ "mullvad_daemon.management_interface/WireguardRelayEndpointData",
+ data,
+ )
+ .ok_or(FromProtobufTypeError::InvalidArgument(
+ "invalid endpoint wg data",
+ ))?;
+ MullvadEndpointData::Wireguard(
+ mullvad_types::relay_list::WireguardRelayEndpointData {
+ public_key: bytes_to_pubkey(&data.public_key)?,
+ },
+ )
+ }
+ _ => {
+ return Err(FromProtobufTypeError::InvalidArgument(
+ "invalid relay endpoint type",
+ ))
+ }
+ };
+
+ let ipv6_addr_in = if relay.ipv6_addr_in.is_empty() {
+ None
+ } else {
+ Some(relay.ipv4_addr_in.parse().map_err(|_err| {
+ FromProtobufTypeError::InvalidArgument("invalid relay IPv6 address")
+ })?)
+ };
+
+ Ok(MullvadRelay {
+ hostname: relay.hostname,
+ ipv4_addr_in: relay.ipv4_addr_in.parse().map_err(|_err| {
+ FromProtobufTypeError::InvalidArgument("invalid relay IPv4 address")
+ })?,
+ ipv6_addr_in,
+ include_in_country: relay.include_in_country,
+ active: relay.active,
+ owned: relay.owned,
+ provider: relay.provider,
+ weight: relay.weight,
+ endpoint_data,
+ location: relay.location.map(|location| MullvadLocation {
+ country: location.country,
+ country_code: location.country_code,
+ city: location.city,
+ city_code: location.city_code,
+ latitude: location.latitude,
+ longitude: location.longitude,
+ }),
+ })
+ }
+}
+
impl From<TransportProtocol> for talpid_types::net::TransportProtocol {
fn from(protocol: TransportProtocol) -> Self {
match protocol {
@@ -1591,3 +1632,22 @@ impl From<FromProtobufTypeError> for crate::Status {
}
}
}
+
+/// Converts any message to `google.protobuf.Any`.
+fn to_proto_any<T: prost::Message>(type_name: &str, message: T) -> prost_types::Any {
+ prost_types::Any {
+ type_url: format!("type.googleapis.com/{type_name}"),
+ value: message.encode_to_vec(),
+ }
+}
+
+/// Tries to convert a message from `google.protobuf.Any` to `T`.
+fn try_from_proto_any<T: prost::Message + Default>(
+ type_name: &str,
+ any_value: prost_types::Any,
+) -> Option<T> {
+ if any_value.type_url != format!("type.googleapis.com/{type_name}") {
+ return None;
+ }
+ T::decode(any_value.value.as_slice()).ok()
+}
diff --git a/mullvad-relay-selector/src/lib.rs b/mullvad-relay-selector/src/lib.rs
index e7b85fbc4c..01d4f845aa 100644
--- a/mullvad-relay-selector/src/lib.rs
+++ b/mullvad-relay-selector/src/lib.rs
@@ -3,7 +3,6 @@
use chrono::{DateTime, Local};
use ipnetwork::IpNetwork;
-use matcher::AnyTunnelMatcher;
use mullvad_types::{
endpoint::{MullvadEndpoint, MullvadWireguardEndpoint},
location::{Coordinates, Location},
@@ -13,7 +12,7 @@ use mullvad_types::{
RelaySettings, SelectedObfuscation, Set, TransportPort, Udp2TcpObfuscationSettings,
WireguardConstraints,
},
- relay_list::{Relay, RelayList, Udp2TcpEndpointData},
+ relay_list::{BridgeEndpointData, Relay, RelayEndpointData, RelayList},
CustomTunnelEndpoint,
};
use parking_lot::{Mutex, MutexGuard};
@@ -33,7 +32,7 @@ use talpid_types::{
ErrorExt,
};
-use self::matcher::{RelayMatcher, TunnelMatcher, WireguardMatcher};
+use matcher::{OpenVpnMatcher, RelayMatcher, TunnelMatcher, WireguardMatcher};
mod matcher;
pub mod updater;
@@ -41,12 +40,8 @@ pub mod updater;
const DATE_TIME_FORMAT_STR: &str = "%Y-%m-%d %H:%M:%S%.3f";
const RELAYS_FILENAME: &str = "relays.json";
-const DEFAULT_WIREGUARD_PORT: u16 = 51820;
-const WIREGUARD_EXIT_CONSTRAINTS: WireguardMatcher = WireguardMatcher {
- peer: None,
- port: Constraint::Only(DEFAULT_WIREGUARD_PORT),
- ip_version: Constraint::Only(IpVersion::V4),
-};
+const WIREGUARD_EXIT_PORT: Constraint<u16> = Constraint::Only(51820);
+const WIREGUARD_EXIT_IP_VERSION: Constraint<IpVersion> = Constraint::Only(IpVersion::V4);
const UDP2TCP_PORTS: [u16; 3] = [80, 443, 5001];
@@ -95,7 +90,15 @@ impl ParsedRelays {
}
}
- pub fn from_relay_list(relay_list: RelayList, last_updated: SystemTime) -> Self {
+ pub fn from_relay_list(mut relay_list: RelayList, last_updated: SystemTime) -> Self {
+ // Append data for obfuscation protocols ourselves, since the API does not provide it.
+ if relay_list.wireguard.udp2tcp_ports.is_empty() {
+ relay_list
+ .wireguard
+ .udp2tcp_ports
+ .extend(UDP2TCP_PORTS.into_iter());
+ }
+
let mut relays = Vec::new();
for country in &relay_list.countries {
let country_name = country.name.clone();
@@ -115,26 +118,6 @@ impl ParsedRelays {
latitude,
longitude,
});
-
- Self::filter_invalid_relays(&mut relay_with_location);
-
- // TODO: The WireGuard data is incorrectly modelled.
- // Using a vector here suggests that a relay may use multiple key pairs at a
- // time. This is incorrect and will never be the case.
- //
- // Currently, the `wireguard` vector will have 0 or 1 entries.
- // This should be changed into e.g. using an Option<_> instead.
- //
-
- if !relay.tunnels.wireguard.is_empty() {
- for port in UDP2TCP_PORTS {
- relay_with_location
- .obfuscators
- .udp2tcp
- .push(Udp2TcpEndpointData { port });
- }
- }
-
relays.push(relay_with_location);
}
}
@@ -147,36 +130,6 @@ impl ParsedRelays {
}
}
- fn filter_invalid_relays(relay: &mut Relay) {
- let total_openvpn_endpoints = relay.tunnels.openvpn.len();
- let openvpn_endpoints = &mut relay.tunnels.openvpn;
- openvpn_endpoints.retain(|data| data.port != 0);
-
- if openvpn_endpoints.len() < total_openvpn_endpoints {
- log::error!(
- "Relay {} contained {} invalid OpenVPN endpoints out of {} endpoints",
- relay.hostname,
- total_openvpn_endpoints - openvpn_endpoints.len(),
- total_openvpn_endpoints
- );
- }
-
- let total_wireguard_endpoints = relay.tunnels.wireguard.len();
- let wireguard_endpoints = &mut relay.tunnels.wireguard;
- wireguard_endpoints.retain(|data| {
- !data.port_ranges.is_empty() && data.port_ranges.iter().all(|(start, end)| start <= end)
- });
-
- if wireguard_endpoints.len() < total_wireguard_endpoints {
- log::error!(
- "Relay {} contained {} invalid WireGuard endpoints out of {} endpoints",
- relay.hostname,
- total_wireguard_endpoints - wireguard_endpoints.len(),
- total_wireguard_endpoints
- );
- }
- }
-
pub fn from_file(path: impl AsRef<Path>) -> Result<Self, Error> {
log::debug!("Reading relays from {}", path.as_ref().display());
let (last_modified, file) =
@@ -349,7 +302,15 @@ impl RelaySelector {
return None;
}
- let matcher = RelayMatcher::from(relay_constraints.clone());
+ let (openvpn_data, wireguard_data) = {
+ let relays = self.parsed_relays.lock();
+ (
+ relays.locations.openvpn.clone(),
+ relays.locations.wireguard.clone(),
+ )
+ };
+
+ let matcher = RelayMatcher::new(relay_constraints.clone(), openvpn_data, wireguard_data);
let mut matching_locations: Vec<Location> = self
.parsed_relays
.lock()
@@ -385,11 +346,14 @@ impl RelaySelector {
location: location.clone(),
providers: providers.clone(),
ownership: *ownership,
- tunnel: openvpn_constraints,
+ tunnel: OpenVpnMatcher::new(
+ openvpn_constraints,
+ self.parsed_relays.lock().locations.openvpn.clone(),
+ ),
};
- if relay_matcher.tunnel.port.is_any() && bridge_state == BridgeState::On {
- relay_matcher.tunnel.port = Constraint::Only(TransportPort {
+ if relay_matcher.tunnel.constraints.port.is_any() && bridge_state == BridgeState::On {
+ relay_matcher.tunnel.constraints.port = Constraint::Only(TransportPort {
protocol: TransportProtocol::Tcp,
port: Constraint::Any,
});
@@ -401,7 +365,7 @@ impl RelaySelector {
let (preferred_port, preferred_protocol) =
Self::preferred_openvpn_constraints(retry_attempt);
- let should_try_preferred = match &mut preferred_relay_matcher.tunnel.port {
+ let should_try_preferred = match &mut preferred_relay_matcher.tunnel.constraints.port {
any @ Constraint::Any => {
*any = Constraint::Only(TransportPort {
protocol: preferred_protocol,
@@ -434,7 +398,7 @@ impl RelaySelector {
) -> Result<NormalSelectedRelay, Error> {
let mut exit_matcher = RelayMatcher {
location: exit_location,
- tunnel: WIREGUARD_EXIT_CONSTRAINTS.clone(),
+ tunnel: self.wireguard_exit_matcher(),
..entry_matcher.clone()
};
@@ -493,7 +457,10 @@ impl RelaySelector {
location: location.clone(),
providers: providers.clone(),
ownership: *ownership,
- tunnel: wireguard_constraints.clone().into(),
+ tunnel: WireguardMatcher::new(
+ wireguard_constraints.clone(),
+ self.parsed_relays.lock().locations.wireguard.clone(),
+ ),
};
let mut preferred_matcher: RelayMatcher<WireguardMatcher> = entry_relay_matcher.clone();
@@ -521,7 +488,15 @@ impl RelaySelector {
&self,
relay_constraints: &RelayConstraints,
) -> Result<NormalSelectedRelay, Error> {
- let mut matcher: RelayMatcher<AnyTunnelMatcher> = relay_constraints.clone().into();
+ let (openvpn_data, wireguard_data) = {
+ let relays = self.parsed_relays.lock();
+ (
+ relays.locations.openvpn.clone(),
+ relays.locations.wireguard.clone(),
+ )
+ };
+ let mut matcher =
+ RelayMatcher::new(relay_constraints.clone(), openvpn_data, wireguard_data);
let mut selected_entry_relay = None;
let mut selected_entry_endpoint = None;
@@ -536,7 +511,7 @@ impl RelaySelector {
// Pick the entry relay first if its location constraint is a subset of the exit location.
if relay_constraints.wireguard_constraints.use_multihop {
- matcher.tunnel.wireguard = WIREGUARD_EXIT_CONSTRAINTS.clone();
+ matcher.tunnel.wireguard = self.wireguard_exit_matcher();
if relay_constraints
.wireguard_constraints
.entry_location
@@ -826,8 +801,8 @@ impl RelaySelector {
.lock()
.relays()
.iter()
- .filter(|relay| relay.active)
- .filter_map(|relay| Self::matching_bridge_relay(relay, constraints))
+ .filter(|relay| relay.active && Self::matching_bridge_relay(relay, constraints))
+ .cloned()
.collect();
if matching_relays.is_empty() {
@@ -849,7 +824,7 @@ impl RelaySelector {
self.pick_random_relay(&matching_relays)
};
relay.and_then(|relay| {
- self.pick_random_bridge(relay)
+ self.pick_random_bridge(&self.parsed_relays.lock().locations.bridge, relay)
.map(|bridge| (bridge, relay.clone()))
})
}
@@ -931,21 +906,17 @@ impl RelaySelector {
_endpoint: &MullvadWireguardEndpoint,
retry_attempt: u32,
) -> Option<SelectedObfuscator> {
+ let udp2tcp_ports = &self.parsed_relays.lock().locations.wireguard.udp2tcp_ports;
let udp2tcp_endpoint = if obfuscation_settings.port.is_only() {
- relay
- .obfuscators
- .udp2tcp
+ udp2tcp_ports
.iter()
- .find(|&candidate| obfuscation_settings.port.matches_eq(&candidate.port))
+ .find(|&candidate| obfuscation_settings.port == Constraint::Only(*candidate))
} else {
- relay
- .obfuscators
- .udp2tcp
- .get(retry_attempt as usize % relay.obfuscators.udp2tcp.len())
+ udp2tcp_ports.get(retry_attempt as usize % udp2tcp_ports.len())
};
udp2tcp_endpoint
.map(|udp2tcp_endpoint| ObfuscatorConfig::Udp2Tcp {
- endpoint: SocketAddr::new(relay.ipv4_addr_in.into(), udp2tcp_endpoint.port),
+ endpoint: SocketAddr::new(relay.ipv4_addr_in.into(), *udp2tcp_endpoint),
})
.map(|config| SelectedObfuscator {
config,
@@ -967,7 +938,7 @@ impl RelaySelector {
let location_supports_openvpn =
self.parsed_relays.lock().relays().iter().any(|relay| {
relay.active
- && !relay.tunnels.openvpn.is_empty()
+ && relay.endpoint_data == RelayEndpointData::Openvpn
&& location_constraint.matches(relay)
&& providers_constraint.matches(relay)
&& ownership_constraint.matches(relay)
@@ -981,7 +952,7 @@ impl RelaySelector {
let location_supports_wireguard = self.parsed_relays.lock().relays().iter().any(|relay| {
relay.active
- && !relay.tunnels.wireguard.is_empty()
+ && matches!(relay.endpoint_data, RelayEndpointData::Wireguard(_))
&& location_constraint.matches(relay)
&& providers_constraint.matches(relay)
&& ownership_constraint.matches(relay)
@@ -1071,27 +1042,11 @@ impl RelaySelector {
.ok_or(Error::NoRelay)
}
- fn matching_bridge_relay(
- relay: &Relay,
- constraints: &InternalBridgeConstraints,
- ) -> Option<Relay> {
- if !constraints.location.matches(relay)
- || !constraints.providers.matches(relay)
- || !constraints.ownership.matches(relay)
- {
- return None;
- }
-
- let mut filtered_relay = relay.clone();
- filtered_relay
- .bridges
- .shadowsocks
- .retain(|bridge| constraints.transport_protocol.matches_eq(&bridge.protocol));
- if filtered_relay.bridges.shadowsocks.is_empty() {
- return None;
- }
-
- Some(filtered_relay)
+ fn matching_bridge_relay(relay: &Relay, constraints: &InternalBridgeConstraints) -> bool {
+ constraints.location.matches(relay)
+ && constraints.providers.matches(relay)
+ && constraints.ownership.matches(relay)
+ && relay.endpoint_data == RelayEndpointData::Bridge
}
/// Picks a relay using [Self::pick_random_relay_fn], using the `weight` member of each relay
@@ -1136,10 +1091,15 @@ impl RelaySelector {
}
/// Picks a random bridge from a relay.
- fn pick_random_bridge(&self, relay: &Relay) -> Option<ProxySettings> {
- relay
- .bridges
- .shadowsocks
+ fn pick_random_bridge(
+ &self,
+ data: &BridgeEndpointData,
+ relay: &Relay,
+ ) -> Option<ProxySettings> {
+ if relay.endpoint_data != RelayEndpointData::Bridge {
+ return None;
+ }
+ data.shadowsocks
.choose(&mut rand::thread_rng())
.map(|shadowsocks_endpoint| {
log::info!(
@@ -1149,9 +1109,7 @@ impl RelaySelector {
shadowsocks_endpoint.port,
shadowsocks_endpoint.protocol
);
- shadowsocks_endpoint
- .clone()
- .to_proxy_settings(relay.ipv4_addr_in.into())
+ shadowsocks_endpoint.to_proxy_settings(relay.ipv4_addr_in.into())
})
}
@@ -1181,6 +1139,14 @@ impl RelaySelector {
Ok(bundled_relays)
}
}
+
+ fn wireguard_exit_matcher(&self) -> WireguardMatcher {
+ let mut tunnel =
+ WireguardMatcher::from_endpoint(self.parsed_relays.lock().locations.wireguard.clone());
+ tunnel.ip_version = WIREGUARD_EXIT_IP_VERSION;
+ tunnel.port = WIREGUARD_EXIT_PORT;
+ tunnel
+ }
}
#[derive(Debug)]
@@ -1242,8 +1208,8 @@ mod test {
use mullvad_types::{
relay_constraints::{BridgeConstraints, RelayConstraints},
relay_list::{
- OpenVpnEndpointData, Relay, RelayBridges, RelayListCity, RelayListCountry,
- RelayObfuscators, RelayTunnels, WireguardEndpointData,
+ OpenVpnEndpoint, OpenVpnEndpointData, Relay, RelayListCity, RelayListCountry,
+ WireguardEndpointData, WireguardRelayEndpointData,
},
};
use talpid_types::net::wireguard::PublicKey;
@@ -1271,23 +1237,9 @@ mod test {
owned: true,
provider: "31173".to_string(),
weight: 1,
- tunnels: RelayTunnels {
- openvpn: vec![],
- wireguard: vec![
- WireguardEndpointData {
- port_ranges: vec![(53, 53), (4000, 33433), (33565, 51820), (52000, 60000)],
- ipv4_gateway: "10.64.0.1".parse().unwrap(),
- ipv6_gateway: "fc00:bbbb:bbbb:bb01::1".parse().unwrap(),
- public_key: PublicKey::from_base64("BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=").unwrap(),
- },
- ],
- },
- bridges: RelayBridges {
- shadowsocks: vec![],
- },
- obfuscators: RelayObfuscators {
- udp2tcp: vec![],
- },
+ endpoint_data: RelayEndpointData::Wireguard(WireguardRelayEndpointData {
+ public_key: PublicKey::from_base64("BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=").unwrap(),
+ }),
location: None,
},
Relay {
@@ -1299,23 +1251,9 @@ mod test {
owned: false,
provider: "31173".to_string(),
weight: 1,
- tunnels: RelayTunnels {
- openvpn: vec![],
- wireguard: vec![
- WireguardEndpointData {
- port_ranges: vec![(53, 53), (4000, 33433), (33565, 51820), (52000, 60000)],
- ipv4_gateway: "10.64.0.1".parse().unwrap(),
- ipv6_gateway: "fc00:bbbb:bbbb:bb01::1".parse().unwrap(),
- public_key: PublicKey::from_base64("veGD6/aEY6sMfN3Ls7YWPmNgu3AheO7nQqsFT47YSws=").unwrap(),
- },
- ],
- },
- bridges: RelayBridges {
- shadowsocks: vec![],
- },
- obfuscators: RelayObfuscators {
- udp2tcp: vec![],
- },
+ endpoint_data: RelayEndpointData::Wireguard(WireguardRelayEndpointData {
+ public_key: PublicKey::from_base64("BLNHNoGO88LjV/wDBa7CUUwUzPq/fO2UwcGLy56hKy4=").unwrap(),
+ }),
location: None,
},
Relay {
@@ -1327,81 +1265,7 @@ mod test {
owned: true,
provider: "31173".to_string(),
weight: 1,
- tunnels: RelayTunnels {
- openvpn: vec![
- OpenVpnEndpointData {
- port: 1194,
- protocol: TransportProtocol::Udp,
- },
- OpenVpnEndpointData {
- port: 443,
- protocol: TransportProtocol::Tcp,
- },
- OpenVpnEndpointData {
- port: 80,
- protocol: TransportProtocol::Tcp,
- },
- ],
- wireguard: vec![],
- },
- bridges: RelayBridges {
- shadowsocks: vec![],
- },
- obfuscators: RelayObfuscators {
- udp2tcp: vec![],
- },
- location: None,
- },
- Relay {
- hostname: "se11-wireguard-filtered".to_string(),
- ipv4_addr_in: "185.213.154.69".parse().unwrap(),
- ipv6_addr_in: Some("2a03:1b20:5:f011::a10f".parse().unwrap()),
- include_in_country: true,
- active: true,
- owned: true,
- provider: "31173".to_string(),
- weight: 1,
- tunnels: RelayTunnels {
- openvpn: vec![],
- wireguard: vec![
- WireguardEndpointData {
- port_ranges: vec![],
- ipv4_gateway: "10.64.0.1".parse().unwrap(),
- ipv6_gateway: "fc00:bbbb:bbbb:bb01::1".parse().unwrap(),
- public_key: PublicKey::from_base64("veGD6/aEY6sMfN3Ls7YWPmNgu3AheO7nQqsFT47YSws=").unwrap(),
- },
- ],
- },
- bridges: RelayBridges {
- shadowsocks: vec![],
- },
- obfuscators: RelayObfuscators {
- udp2tcp: vec![],
- },
- location: None,
- },
- Relay {
- hostname: "se-got-010-filtered".to_string(),
- ipv4_addr_in: "185.213.154.69".parse().unwrap(),
- ipv6_addr_in: Some("2a03:1b20:5:f011::a10f".parse().unwrap()),
- include_in_country: true,
- active: true,
- owned: true,
- provider: "31173".to_string(),
- weight: 1,
- tunnels: RelayTunnels {
- openvpn: vec![OpenVpnEndpointData{
- port: 0,
- protocol: TransportProtocol::Udp,
- }],
- wireguard: vec![],
- },
- bridges: RelayBridges {
- shadowsocks: vec![],
- },
- obfuscators: RelayObfuscators {
- udp2tcp: vec![],
- },
+ endpoint_data: RelayEndpointData::Openvpn,
location: None,
}
],
@@ -1409,6 +1273,31 @@ mod test {
],
}
],
+ openvpn: OpenVpnEndpointData {
+ ports: vec![
+ OpenVpnEndpoint {
+ port: 1194,
+ protocol: TransportProtocol::Udp,
+ },
+ OpenVpnEndpoint {
+ port: 443,
+ protocol: TransportProtocol::Tcp,
+ },
+ OpenVpnEndpoint {
+ port: 80,
+ protocol: TransportProtocol::Tcp,
+ },
+ ],
+ },
+ bridge: BridgeEndpointData {
+ shadowsocks: vec![],
+ },
+ wireguard: WireguardEndpointData {
+ port_ranges: vec![(53, 53), (4000, 33433), (33565, 51820), (52000, 60000)],
+ ipv4_gateway: "10.64.0.1".parse().unwrap(),
+ ipv6_gateway: "fc00:bbbb:bbbb:bb01::1".parse().unwrap(),
+ udp2tcp_ports: vec![],
+ },
};
}
@@ -1870,32 +1759,6 @@ mod test {
}
#[test]
- fn test_filtering_invalid_endpoint_relays() {
- let relay_selector = new_relay_selector();
- let mut constraints = RelayConstraints {
- location: Constraint::Only(LocationConstraint::Hostname(
- "se".to_string(),
- "got".to_string(),
- "se11-wireguard-filtered".to_string(),
- )),
- ..RelayConstraints::default()
- };
- relay_selector
- .get_tunnel_endpoint(&constraints, BridgeState::Off, 0)
- .expect_err("Successfully selected a relay that should be filtered");
-
- constraints.location = Constraint::Only(LocationConstraint::Hostname(
- "se".to_string(),
- "got".to_string(),
- "se-got-010-filtered".to_string(),
- ));
-
- relay_selector
- .get_tunnel_endpoint(&constraints, BridgeState::Off, 0)
- .expect_err("Successfully selected a relay that should be filtered");
- }
-
- #[test]
fn test_ownership() {
let relay_selector = new_relay_selector();
let mut constraints = RelayConstraints::default();
diff --git a/mullvad-relay-selector/src/matcher.rs b/mullvad-relay-selector/src/matcher.rs
index 350a106745..260dc7e7db 100644
--- a/mullvad-relay-selector/src/matcher.rs
+++ b/mullvad-relay-selector/src/matcher.rs
@@ -4,11 +4,16 @@ use mullvad_types::{
Constraint, LocationConstraint, Match, OpenVpnConstraints, Ownership, Providers,
RelayConstraints, WireguardConstraints,
},
- relay_list::{Relay, RelayTunnels, WireguardEndpointData},
+ relay_list::{
+ OpenVpnEndpoint, OpenVpnEndpointData, Relay, RelayEndpointData, WireguardEndpointData,
+ },
+};
+use rand::{
+ prelude::{IteratorRandom, SliceRandom},
+ Rng,
};
-use rand::{seq::SliceRandom, Rng};
use std::net::{IpAddr, SocketAddr};
-use talpid_types::net::{all_of_the_internet, wireguard, IpVersion, TunnelType};
+use talpid_types::net::{all_of_the_internet, wireguard, Endpoint, IpVersion, TunnelType};
#[derive(Clone)]
pub struct RelayMatcher<T: TunnelMatcher> {
@@ -18,22 +23,24 @@ pub struct RelayMatcher<T: TunnelMatcher> {
pub tunnel: T,
}
-impl From<RelayConstraints> for RelayMatcher<AnyTunnelMatcher> {
- fn from(constraints: RelayConstraints) -> Self {
+impl RelayMatcher<AnyTunnelMatcher> {
+ pub fn new(
+ constraints: RelayConstraints,
+ openvpn_data: OpenVpnEndpointData,
+ wireguard_data: WireguardEndpointData,
+ ) -> Self {
Self {
location: constraints.location,
providers: constraints.providers,
ownership: constraints.ownership,
tunnel: AnyTunnelMatcher {
- wireguard: constraints.wireguard_constraints.into(),
- openvpn: constraints.openvpn_constraints,
+ wireguard: WireguardMatcher::new(constraints.wireguard_constraints, wireguard_data),
+ openvpn: OpenVpnMatcher::new(constraints.openvpn_constraints, openvpn_data),
tunnel_type: constraints.tunnel_protocol,
},
}
}
-}
-impl RelayMatcher<AnyTunnelMatcher> {
pub fn into_wireguard_matcher(self) -> RelayMatcher<WireguardMatcher> {
RelayMatcher {
tunnel: self.tunnel.wireguard,
@@ -83,35 +90,65 @@ pub trait TunnelMatcher: Clone {
impl TunnelMatcher for OpenVpnMatcher {
fn filter_matching_endpoints(&self, relay: &Relay) -> Option<Relay> {
- let tunnels = relay
- .tunnels
- .openvpn
- .iter()
- .filter(|endpoint| self.matches(endpoint))
- .cloned()
- .collect::<Vec<_>>();
- if tunnels.is_empty() {
+ if !self.matches(&self.data) || !matches!(relay.endpoint_data, RelayEndpointData::Openvpn) {
return None;
}
- let mut relay = relay.clone();
- relay.tunnels = RelayTunnels {
- openvpn: tunnels,
- wireguard: vec![],
- };
- Some(relay)
+ Some(relay.clone())
}
fn mullvad_endpoint(&self, relay: &Relay) -> Option<MullvadEndpoint> {
- relay
- .tunnels
- .openvpn
- .choose(&mut rand::thread_rng())
- .cloned()
- .map(|endpoint| endpoint.into_mullvad_endpoint(relay.ipv4_addr_in.into()))
+ self.get_transport_port().map(|endpoint| {
+ MullvadEndpoint::OpenVpn(Endpoint::new(
+ relay.ipv4_addr_in,
+ endpoint.port,
+ endpoint.protocol,
+ ))
+ })
+ }
+}
+
+#[derive(Debug, Clone)]
+pub struct OpenVpnMatcher {
+ pub constraints: OpenVpnConstraints,
+ pub data: OpenVpnEndpointData,
+}
+
+impl OpenVpnMatcher {
+ pub fn new(constraints: OpenVpnConstraints, data: OpenVpnEndpointData) -> Self {
+ Self { constraints, data }
+ }
+
+ fn get_transport_port(&self) -> Option<&OpenVpnEndpoint> {
+ match self.constraints.port {
+ Constraint::Any => self.data.ports.choose(&mut rand::thread_rng()),
+ Constraint::Only(transport_port) => self
+ .data
+ .ports
+ .iter()
+ .filter(|endpoint| {
+ transport_port
+ .port
+ .map(|port| port == endpoint.port)
+ .unwrap_or(true)
+ && transport_port.protocol == endpoint.protocol
+ })
+ .choose(&mut rand::thread_rng()),
+ }
}
}
-pub type OpenVpnMatcher = OpenVpnConstraints;
+impl Match<OpenVpnEndpointData> for OpenVpnMatcher {
+ fn matches(&self, endpoint: &OpenVpnEndpointData) -> bool {
+ match self.constraints.port {
+ Constraint::Any => true,
+ Constraint::Only(transport_port) => endpoint.ports.iter().any(|endpoint| {
+ transport_port.protocol == endpoint.protocol
+ && (transport_port.port.is_any()
+ || transport_port.port == Constraint::Only(endpoint.port))
+ }),
+ }
+ }
+}
#[derive(Clone)]
pub struct AnyTunnelMatcher {
@@ -132,11 +169,10 @@ impl TunnelMatcher for AnyTunnelMatcher {
let openvpn_relay = self.openvpn.filter_matching_endpoints(relay);
match (wireguard_relay, openvpn_relay) {
- (Some(mut matched_relay), Some(openvpn_relay)) => {
- matched_relay.tunnels.openvpn = openvpn_relay.tunnels.openvpn;
- Some(matched_relay)
- }
(Some(relay), None) | (None, Some(relay)) => Some(relay),
+ (Some(_), Some(_)) => {
+ unreachable!("relay cannot match multiple endpoint types")
+ }
_ => None,
}
}
@@ -150,15 +186,10 @@ impl TunnelMatcher for AnyTunnelMatcher {
fn mullvad_endpoint(&self, relay: &Relay) -> Option<MullvadEndpoint> {
#[cfg(not(target_os = "android"))]
match self.tunnel_type {
- Constraint::Any => vec![
- self.openvpn.mullvad_endpoint(relay),
- self.wireguard.mullvad_endpoint(relay),
- ]
- .into_iter()
- .flatten()
- .collect::<Vec<_>>()
- .choose(&mut rand::thread_rng())
- .cloned(),
+ Constraint::Any => self
+ .openvpn
+ .mullvad_endpoint(relay)
+ .or_else(|| self.wireguard.mullvad_endpoint(relay)),
Constraint::Only(TunnelType::OpenVpn) => self.openvpn.mullvad_endpoint(relay),
Constraint::Only(TunnelType::Wireguard) => self.wireguard.mullvad_endpoint(relay),
}
@@ -168,25 +199,47 @@ impl TunnelMatcher for AnyTunnelMatcher {
}
}
-#[derive(Clone)]
+#[derive(Default, Clone)]
pub struct WireguardMatcher {
/// The peer is an already selected peer relay to be used with multihop.
/// It's stored here so we can exclude it from further selections being made.
pub peer: Option<Relay>,
pub port: Constraint<u16>,
pub ip_version: Constraint<IpVersion>,
+
+ pub data: WireguardEndpointData,
}
impl WireguardMatcher {
+ pub fn new(constraints: WireguardConstraints, data: WireguardEndpointData) -> Self {
+ Self {
+ peer: None,
+ port: constraints.port,
+ ip_version: constraints.ip_version,
+ data,
+ }
+ }
+
+ pub fn from_endpoint(data: WireguardEndpointData) -> Self {
+ Self {
+ data,
+ ..Default::default()
+ }
+ }
+
fn wg_data_to_endpoint(
&self,
relay: &Relay,
- data: WireguardEndpointData,
+ data: &WireguardEndpointData,
) -> Option<MullvadEndpoint> {
let host = self.get_address_for_wireguard_relay(relay)?;
- let port = self.get_port_for_wireguard_relay(&data)?;
+ let port = self.get_port_for_wireguard_relay(data)?;
let peer_config = wireguard::PeerConfig {
- public_key: data.public_key,
+ public_key: relay
+ .endpoint_data
+ .unwrap_wireguard_ref()
+ .public_key
+ .clone(),
endpoint: SocketAddr::new(host, port),
allowed_ips: all_of_the_internet(),
psk: None,
@@ -244,28 +297,6 @@ impl WireguardMatcher {
}
}
-impl From<WireguardConstraints> for WireguardMatcher {
- fn from(constraints: WireguardConstraints) -> Self {
- Self {
- peer: None,
- port: constraints.port,
- ip_version: constraints.ip_version,
- }
- }
-}
-
-impl Match<WireguardEndpointData> for WireguardMatcher {
- fn matches(&self, endpoint: &WireguardEndpointData) -> bool {
- match self.port {
- Constraint::Any => true,
- Constraint::Only(port) => endpoint
- .port_ranges
- .iter()
- .any(|range| (port >= range.0 && port <= range.1)),
- }
- }
-}
-
impl TunnelMatcher for WireguardMatcher {
fn filter_matching_endpoints(&self, relay: &Relay) -> Option<Relay> {
if self
@@ -276,30 +307,13 @@ impl TunnelMatcher for WireguardMatcher {
{
return None;
}
-
- let tunnels = relay
- .tunnels
- .wireguard
- .iter()
- .filter(|endpoint| self.matches(*endpoint))
- .cloned()
- .collect::<Vec<_>>();
- if tunnels.is_empty() {
+ if !matches!(relay.endpoint_data, RelayEndpointData::Wireguard(..)) {
return None;
}
- let mut relay = relay.clone();
- relay.tunnels = RelayTunnels {
- wireguard: tunnels,
- openvpn: vec![],
- };
- Some(relay)
+ Some(relay.clone())
}
fn mullvad_endpoint(&self, relay: &Relay) -> Option<MullvadEndpoint> {
- relay
- .tunnels
- .wireguard
- .choose(&mut rand::thread_rng())
- .and_then(|wg_tunnel| self.wg_data_to_endpoint(relay, (*wg_tunnel).clone()))
+ self.wg_data_to_endpoint(relay, &self.data)
}
}
diff --git a/mullvad-types/src/endpoint.rs b/mullvad-types/src/endpoint.rs
index bfba11a481..3258032afe 100644
--- a/mullvad-types/src/endpoint.rs
+++ b/mullvad-types/src/endpoint.rs
@@ -1,12 +1,6 @@
-use serde::{Deserialize, Serialize};
-use std::{
- fmt,
- net::{Ipv4Addr, Ipv6Addr},
-};
+use std::net::{Ipv4Addr, Ipv6Addr};
use talpid_types::net::{wireguard, Endpoint, TransportProtocol};
-use crate::relay_list::{OpenVpnEndpointData, WireguardEndpointData};
-
/// Contains server data needed to connect to a single mullvad endpoint
#[derive(Debug, Clone)]
pub enum MullvadEndpoint {
@@ -45,40 +39,3 @@ impl MullvadEndpoint {
}
}
}
-/// TunnelEndpointData contains data required to connect to a given tunnel endpoint.
-/// Different endpoint types can require different types of data.
-#[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)]
-pub enum TunnelEndpointData {
- /// Extra parameters for an OpenVPN tunnel endpoint.
- #[serde(rename = "openvpn")]
- OpenVpn(OpenVpnEndpointData),
- /// Extra parameters for a Wireguard tunnel endpoint.
- #[serde(rename = "wireguard")]
- Wireguard(WireguardEndpointData),
-}
-impl From<OpenVpnEndpointData> for TunnelEndpointData {
- fn from(endpoint_data: OpenVpnEndpointData) -> TunnelEndpointData {
- TunnelEndpointData::OpenVpn(endpoint_data)
- }
-}
-
-impl From<WireguardEndpointData> for TunnelEndpointData {
- fn from(endpoint_data: WireguardEndpointData) -> TunnelEndpointData {
- TunnelEndpointData::Wireguard(endpoint_data)
- }
-}
-
-impl fmt::Display for TunnelEndpointData {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- match self {
- TunnelEndpointData::OpenVpn(openvpn_data) => {
- write!(f, "OpenVPN ")?;
- openvpn_data.fmt(f)
- }
- TunnelEndpointData::Wireguard(wireguard_data) => {
- write!(f, "Wireguard ")?;
- wireguard_data.fmt(f)
- }
- }
- }
-}
diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs
index 7311928fec..85db48534a 100644
--- a/mullvad-types/src/relay_constraints.rs
+++ b/mullvad-types/src/relay_constraints.rs
@@ -3,7 +3,7 @@
use crate::{
location::{CityCode, CountryCode, Hostname},
- relay_list::{OpenVpnEndpointData, Relay},
+ relay_list::Relay,
CustomTunnelEndpoint,
};
#[cfg(target_os = "android")]
@@ -440,21 +440,6 @@ impl fmt::Display for OpenVpnConstraints {
}
}
-impl Match<OpenVpnEndpointData> for OpenVpnConstraints {
- fn matches(&self, endpoint: &OpenVpnEndpointData) -> bool {
- match self.port {
- Constraint::Any => true,
- Constraint::Only(transport_port) => {
- transport_port.protocol == endpoint.protocol
- && match transport_port.port {
- Constraint::Any => true,
- Constraint::Only(port) => port == endpoint.port,
- }
- }
- }
- }
-}
-
/// [`Constraint`]s applicable to WireGuard relay servers.
#[derive(Debug, Default, Clone, Eq, PartialEq, Deserialize, Serialize)]
#[serde(default)]
diff --git a/mullvad-types/src/relay_list.rs b/mullvad-types/src/relay_list.rs
index e5a40a8f4e..643bcbd798 100644
--- a/mullvad-types/src/relay_list.rs
+++ b/mullvad-types/src/relay_list.rs
@@ -1,36 +1,34 @@
-use crate::{
- endpoint::MullvadEndpoint,
- location::{CityCode, CountryCode, Location},
-};
+use crate::location::{CityCode, CountryCode, Location};
#[cfg(target_os = "android")]
use jnix::IntoJava;
use serde::{Deserialize, Serialize};
-use std::{
- fmt,
- net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr},
-};
+use std::net::{IpAddr, Ipv4Addr, Ipv6Addr, SocketAddr};
use talpid_types::net::{
openvpn::{ProxySettings, ShadowsocksProxySettings},
- wireguard, Endpoint, TransportProtocol,
+ wireguard, TransportProtocol,
};
/// Stores a list of relays for each country obtained from the API using
/// `mullvad_api::RelayListProxy`. This can also be passed to frontends.
-#[derive(Debug, Clone, Deserialize, Serialize)]
+#[derive(Default, Debug, Clone, Deserialize, Serialize)]
#[cfg_attr(target_os = "android", derive(IntoJava))]
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
pub struct RelayList {
#[cfg_attr(target_os = "android", jnix(skip))]
pub etag: Option<String>,
pub countries: Vec<RelayListCountry>,
+ #[cfg_attr(target_os = "android", jnix(skip))]
+ #[serde(rename = "openvpn")]
+ pub openvpn: OpenVpnEndpointData,
+ #[cfg_attr(target_os = "android", jnix(skip))]
+ pub bridge: BridgeEndpointData,
+ #[cfg_attr(target_os = "android", jnix(skip))]
+ pub wireguard: WireguardEndpointData,
}
impl RelayList {
pub fn empty() -> Self {
- Self {
- etag: None,
- countries: Vec::new(),
- }
+ Self::default()
}
}
@@ -78,60 +76,45 @@ pub struct Relay {
pub provider: String,
#[cfg_attr(target_os = "android", jnix(skip))]
pub weight: u64,
- #[serde(skip_serializing_if = "RelayTunnels::is_empty", default)]
- pub tunnels: RelayTunnels,
- #[serde(skip_serializing_if = "RelayBridges::is_empty", default)]
- #[cfg_attr(target_os = "android", jnix(skip))]
- pub bridges: RelayBridges,
- #[serde(skip_serializing_if = "RelayObfuscators::is_empty", default)]
- #[cfg_attr(target_os = "android", jnix(skip))]
- pub obfuscators: RelayObfuscators,
+ pub endpoint_data: RelayEndpointData,
#[cfg_attr(target_os = "android", jnix(skip))]
pub location: Option<Location>,
}
-/// Provides protocol-specific information about a [`Relay`].
-#[derive(Debug, Default, Clone, Deserialize, Serialize)]
-#[serde(default)]
+/// Specifies the type of a relay or relay-specific endpoint data.
+#[derive(Debug, Clone, PartialEq, Eq, Deserialize, Serialize)]
+#[serde(rename_all = "snake_case")]
#[cfg_attr(target_os = "android", derive(IntoJava))]
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
-pub struct RelayTunnels {
- #[cfg_attr(target_os = "android", jnix(skip))]
- pub openvpn: Vec<OpenVpnEndpointData>,
- pub wireguard: Vec<WireguardEndpointData>,
+pub enum RelayEndpointData {
+ Openvpn,
+ Bridge,
+ Wireguard(WireguardRelayEndpointData),
}
-impl RelayTunnels {
- pub fn is_empty(&self) -> bool {
- self.openvpn.is_empty() && self.wireguard.is_empty()
- }
-
- pub fn clear(&mut self) {
- self.openvpn.clear();
- self.wireguard.clear();
+impl RelayEndpointData {
+ pub fn unwrap_wireguard_ref(&self) -> &WireguardRelayEndpointData {
+ if let RelayEndpointData::Wireguard(wg) = &self {
+ return wg;
+ }
+ panic!("not a wireguard endpoint");
}
}
-/// Data needed to connect to an OpenVPN endpoint at a [`Relay`].
-#[derive(Debug, Copy, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
+/// Data needed to connect to OpenVPN endpoints.
+#[derive(Debug, Default, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub struct OpenVpnEndpointData {
- pub port: u16,
- pub protocol: TransportProtocol,
-}
-
-impl OpenVpnEndpointData {
- pub fn into_mullvad_endpoint(self, host: IpAddr) -> MullvadEndpoint {
- MullvadEndpoint::OpenVpn(Endpoint::new(host, self.port, self.protocol))
- }
+ pub ports: Vec<OpenVpnEndpoint>,
}
-impl fmt::Display for OpenVpnEndpointData {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- write!(f, "{} port {}", self.protocol, self.port)
- }
+/// Data needed to connect to OpenVPN endpoints.
+#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
+pub struct OpenVpnEndpoint {
+ pub port: u16,
+ pub protocol: TransportProtocol,
}
-/// Data needed to connect to a WireGuard endpoint at a [`Relay`].
+/// Contains data about all WireGuard endpoints, such as valid port ranges.
#[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Debug)]
#[cfg_attr(target_os = "android", derive(IntoJava))]
#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
@@ -142,45 +125,36 @@ pub struct WireguardEndpointData {
/// Gateways to be used with the tunnel
pub ipv4_gateway: Ipv4Addr,
pub ipv6_gateway: Ipv6Addr,
- /// The peer's public key
- pub public_key: wireguard::PublicKey,
+ pub udp2tcp_ports: Vec<u16>,
}
-impl fmt::Display for WireguardEndpointData {
- fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
- write!(
- f,
- "gateways {} - {} port_ranges {{ {} }} public_key {}",
- self.ipv4_gateway,
- self.ipv6_gateway,
- self.port_ranges
- .iter()
- .map(|range| format!("[{} - {}]", range.0, range.1))
- .collect::<Vec<_>>()
- .join(","),
- self.public_key,
- )
+impl Default for WireguardEndpointData {
+ fn default() -> Self {
+ Self {
+ port_ranges: vec![],
+ ipv4_gateway: "0.0.0.0".parse().unwrap(),
+ ipv6_gateway: "::".parse().unwrap(),
+ udp2tcp_ports: vec![],
+ }
}
}
-/// Used by `mullvad_api::RelayListProxy` to store bridge servers for a [`Relay`].
-#[derive(Debug, Default, Clone, Deserialize, Serialize)]
-#[serde(default)]
-pub struct RelayBridges {
- pub shadowsocks: Vec<ShadowsocksEndpointData>,
+/// Contains data about specific WireGuard endpoints, i.e. their public keys.
+#[derive(Clone, Eq, PartialEq, Hash, Deserialize, Serialize, Debug)]
+#[cfg_attr(target_os = "android", derive(IntoJava))]
+#[cfg_attr(target_os = "android", jnix(package = "net.mullvad.mullvadvpn.model"))]
+#[cfg_attr(target_os = "android", jnix(skip_all))]
+pub struct WireguardRelayEndpointData {
+ /// Public key used by the relay peer
+ pub public_key: wireguard::PublicKey,
}
-impl RelayBridges {
- pub fn is_empty(&self) -> bool {
- self.shadowsocks.is_empty()
- }
-
- pub fn clear(&mut self) {
- self.shadowsocks.clear();
- }
+#[derive(Debug, Default, Clone, Deserialize, Serialize)]
+pub struct BridgeEndpointData {
+ pub shadowsocks: Vec<ShadowsocksEndpointData>,
}
-/// Data needed to connect to a Shadowsocks endpoint at a [`Relay`].
+/// Data needed to connect to Shadowsocks endpoints.
#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
pub struct ShadowsocksEndpointData {
pub port: u16,
@@ -198,24 +172,3 @@ impl ShadowsocksEndpointData {
})
}
}
-
-#[derive(Debug, Default, Clone, Deserialize, Serialize)]
-#[serde(default)]
-pub struct RelayObfuscators {
- pub udp2tcp: Vec<Udp2TcpEndpointData>,
-}
-
-impl RelayObfuscators {
- pub fn is_empty(&self) -> bool {
- self.udp2tcp.is_empty()
- }
-
- pub fn clear(&mut self) {
- self.udp2tcp.clear();
- }
-}
-
-#[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)]
-pub struct Udp2TcpEndpointData {
- pub port: u16,
-}