summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
-rw-r--r--CHANGELOG.md2
-rw-r--r--docs/relay-selector.md23
-rw-r--r--mullvad-cli/src/cmds/bridge.rs46
-rw-r--r--mullvad-cli/src/cmds/relay.rs44
-rw-r--r--mullvad-management-interface/proto/management_interface.proto13
-rw-r--r--mullvad-management-interface/src/types.rs50
-rw-r--r--mullvad-relay-selector/src/lib.rs61
-rw-r--r--mullvad-relay-selector/src/matcher.rs12
-rw-r--r--mullvad-types/src/relay_constraints.rs53
9 files changed, 278 insertions, 26 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md
index 2892b02b14..ca607b3ca9 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -29,6 +29,8 @@ Line wrap the file at 100 chars. Th
- Add device management to desktop app. This simplifies knowing which device is which and adds the
option to log out other devices when there are already 5 connected when logging in.
- Add tray icon tooltip with connection info in desktop app.
+- Add relay and bridge constraints for restricting relay selection to rented or Mullvad-owned
+ relays.
#### Windows
- Detect mounting and dismounting of volumes, such as VeraCrypt volumes or USB drives,
diff --git a/docs/relay-selector.md b/docs/relay-selector.md
index ab6a808d0f..1c6525d5cf 100644
--- a/docs/relay-selector.md
+++ b/docs/relay-selector.md
@@ -11,10 +11,10 @@
The relay selector's main purpose is to pick a single Mullvad relay from a list of relays taking
into account certain user-configurable criteria. Relays can be filtered by their _location_
-(country, city, hostname) and by the protocols and ports they support (transport protocol, tunnel
-protocol, port). The constraints are user specified and stored in the settings. The default value
-for location constraints restricts relay selection to relays from Sweden. The default protocol
-constraints default to _auto_, which implies specific behavior.
+(country, city, hostname), by the protocols and ports they support (transport protocol, tunnel
+protocol, port), and by other constraints. The constraints are user specified and stored in the
+settings. The default value for location constraints restricts relay selection to relays from Sweden.
+The default protocol constraints default to _auto_, which implies specific behavior.
Generally, the filtering process consists of going through each relay in our relay list and
removing relay and endpoint combinations that do not match the constraints outlined above. The
@@ -35,6 +35,8 @@ Endpoints may be filtered by:
like WireGuard
- entry port
- location (country, city, hostname)
+- provider
+- ownership (Mullvad-owned or rented)
### Default constraints for tunnel endpoints
@@ -67,10 +69,15 @@ relay is picked, then a random endpoint that matches the constraints from the re
## Bridge endpoint constraints
-Currently, the only explicit constraints for bridges is the location, and the transport protocol is
-supposedly inferred by the selected bridge- but for now, the daemon only supports TCP bridges, so
-only TCP bridges are being selected. If no location constraint is specified explicitly, then the
-relay location will be used.
+The explicit constraints are:
+
+- location
+- provider
+- ownership
+
+The transport protocol is supposedly inferred by the selected bridge- but for now, the daemon only
+supports TCP bridges, so only TCP bridges are being selected. If no location constraint is specified
+explicitly, then the relay location will be used.
### Selecting a bridge endpoint between filtered relays
diff --git a/mullvad-cli/src/cmds/bridge.rs b/mullvad-cli/src/cmds/bridge.rs
index 40afc93b3b..3f00a7f69a 100644
--- a/mullvad-cli/src/cmds/bridge.rs
+++ b/mullvad-cli/src/cmds/bridge.rs
@@ -59,6 +59,19 @@ fn create_bridge_set_subcommand() -> clap::App<'static> {
.required(true),
),
)
+ .subcommand(
+ clap::App::new("ownership")
+ .about(
+ "Filters bridges based on ownership. The 'list' \
+ command shows the available relays and whether they're rented.",
+ )
+ .arg(
+ clap::Arg::new("ownership")
+ .help("Ownership preference, or 'any' for no preference.")
+ .possible_values(&["any", "owned", "rented"])
+ .required(true),
+ ),
+ )
.subcommand(location::get_subcommand().about(
"Set country or city to select bridge relays from. Use the 'list' \
command to show available alternatives.",
@@ -186,6 +199,9 @@ impl Bridge {
Some(("provider", provider_matches)) => {
Self::handle_set_bridge_provider(provider_matches).await
}
+ Some(("ownership", ownership_matches)) => {
+ Self::handle_set_bridge_ownership(ownership_matches).await
+ }
Some(("custom", custom_matches)) => {
Self::handle_bridge_set_custom_settings(custom_matches).await
}
@@ -220,7 +236,12 @@ impl Bridge {
}
async fn handle_set_bridge_location(matches: &clap::ArgMatches) -> Result<()> {
- Self::update_bridge_settings(Some(location::get_constraint_from_args(matches)), None).await
+ Self::update_bridge_settings(
+ Some(location::get_constraint_from_args(matches)),
+ None,
+ None,
+ )
+ .await
}
async fn handle_set_bridge_provider(matches: &clap::ArgMatches) -> Result<()> {
@@ -231,12 +252,19 @@ impl Bridge {
providers
};
- Self::update_bridge_settings(None, Some(providers)).await
+ Self::update_bridge_settings(None, Some(providers), None).await
+ }
+
+ async fn handle_set_bridge_ownership(matches: &clap::ArgMatches) -> Result<()> {
+ let ownership =
+ super::relay::parse_ownership_constraint(matches.value_of("ownership").unwrap());
+ Self::update_bridge_settings(None, None, Some(ownership)).await
}
async fn update_bridge_settings(
location: Option<types::RelayLocation>,
providers: Option<Vec<String>>,
+ ownership: Option<types::Ownership>,
) -> Result<()> {
let mut rpc = new_rpc_client().await?;
let settings = rpc.get_settings(()).await?.into_inner();
@@ -251,6 +279,9 @@ impl Bridge {
constraints.providers =
types::try_providers_constraint_from_proto(&new_providers).unwrap();
}
+ if let Some(new_ownership) = ownership {
+ constraints.ownership = types::ownership_constraint_from_proto(new_ownership);
+ }
constraints
}
_ => {
@@ -258,10 +289,14 @@ impl Bridge {
let providers =
types::try_providers_constraint_from_proto(&providers.unwrap_or_default())
.unwrap();
+ let ownership = ownership
+ .map(types::ownership_constraint_from_proto)
+ .unwrap_or_default();
BridgeConstraints {
location,
providers,
+ ownership,
}
}
};
@@ -433,8 +468,13 @@ impl Bridge {
city.name, city.code, city.latitude, city.longitude
);
for relay in &city.relays {
+ let ownership = if relay.owned {
+ "Mullvad-owned"
+ } else {
+ "rented"
+ };
println!(
- "\t\t{} ({}) - hosted by {}",
+ "\t\t{} ({}) - hosted by {} ({ownership})",
relay.hostname, relay.ipv4_addr_in, relay.provider
);
}
diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs
index c11ee82158..d0d75dea20 100644
--- a/mullvad-cli/src/cmds/relay.rs
+++ b/mullvad-cli/src/cmds/relay.rs
@@ -124,6 +124,17 @@ impl Command for Relay {
)
)
.subcommand(
+ clap::App::new("ownership")
+ .about("Filters relays based on ownership. The 'list' \
+ command shows the available relays and whether they're rented.")
+ .arg(
+ clap::Arg::new("ownership")
+ .help("Ownership preference, or 'any' for no preference.")
+ .possible_values(&["any", "owned", "rented"])
+ .required(true)
+ )
+ )
+ .subcommand(
clap::App::new("tunnel")
.about("Set tunnel protocol-specific constraints.")
.setting(clap::AppSettings::SubcommandRequiredElseHelp)
@@ -226,6 +237,8 @@ impl Relay {
self.set_hostname(relay_matches).await
} else if let Some(providers_matches) = matches.subcommand_matches("provider") {
self.set_providers(providers_matches).await
+ } else if let Some(ownership_matches) = matches.subcommand_matches("ownership") {
+ self.set_ownership(ownership_matches).await
} else if let Some(matches) = matches.subcommand_matches("tunnel") {
if let Some(tunnel_matches) = matches.subcommand_matches("openvpn") {
self.set_openvpn_constraints(tunnel_matches).await
@@ -483,6 +496,21 @@ impl Relay {
.await
}
+ async fn set_ownership(&self, matches: &clap::ArgMatches) -> Result<()> {
+ let ownership = parse_ownership_constraint(matches.value_of("ownership").unwrap());
+ self.update_constraints(types::RelaySettingsUpdate {
+ r#type: Some(types::relay_settings_update::Type::Normal(
+ types::NormalRelaySettingsUpdate {
+ ownership: Some(types::OwnershipUpdate {
+ ownership: ownership as i32,
+ }),
+ ..Default::default()
+ },
+ )),
+ })
+ .await
+ }
+
async fn set_openvpn_constraints(&self, matches: &clap::ArgMatches) -> Result<()> {
let mut openvpn_constraints = {
let mut rpc = new_rpc_client().await?;
@@ -646,12 +674,17 @@ impl Relay {
(false, true) => "WireGuard",
_ => unreachable!("Bug in relay filtering earlier on"),
};
+ let ownership = if relay.owned {
+ "Mullvad-owned"
+ } else {
+ "rented"
+ };
let mut addresses = vec![&relay.ipv4_addr_in];
if !relay.ipv6_addr_in.is_empty() {
addresses.push(&relay.ipv6_addr_in);
}
println!(
- "\t\t{} ({}) - {}, hosted by {}",
+ "\t\t{} ({}) - {}, hosted by {} ({ownership})",
relay.hostname,
addresses.iter().join(", "),
support_msg,
@@ -801,3 +834,12 @@ fn parse_transport_port(
)),
}
}
+
+pub fn parse_ownership_constraint(constraint: &str) -> types::Ownership {
+ match constraint {
+ "any" => types::Ownership::Any,
+ "owned" => types::Ownership::MullvadOwned,
+ "rented" => types::Ownership::Rented,
+ _ => unreachable!(),
+ }
+}
diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto
index ef69be0706..c14d621cac 100644
--- a/mullvad-management-interface/proto/management_interface.proto
+++ b/mullvad-management-interface/proto/management_interface.proto
@@ -239,10 +239,17 @@ message GeoIpLocation {
string obfuscator_hostname = 11;
}
+enum Ownership {
+ ANY = 0;
+ MULLVAD_OWNED = 1;
+ RENTED = 2;
+}
+
message BridgeSettings {
message BridgeConstraints {
RelayLocation location = 1;
repeated string providers = 2;
+ Ownership ownership = 3;
}
message LocalProxySettings {
@@ -335,6 +342,7 @@ message NormalRelaySettings {
TunnelTypeConstraint tunnel_type = 3;
WireguardConstraints wireguard_constraints = 4;
OpenvpnConstraints openvpn_constraints = 5;
+ Ownership ownership = 6;
}
// Constraints are only updated for fields that are provided
@@ -344,6 +352,7 @@ message NormalRelaySettingsUpdate {
TunnelTypeUpdate tunnel_type = 3;
WireguardConstraints wireguard_constraints = 4;
OpenvpnConstraints openvpn_constraints = 5;
+ OwnershipUpdate ownership = 6;
}
message ProviderUpdate {
@@ -363,6 +372,10 @@ message OpenvpnConstraints {
TransportPort port = 1;
}
+message OwnershipUpdate {
+ Ownership ownership = 1;
+}
+
enum IpVersion {
V4 = 0;
V6 = 1;
diff --git a/mullvad-management-interface/src/types.rs b/mullvad-management-interface/src/types.rs
index d685899256..2b45fbe840 100644
--- a/mullvad-management-interface/src/types.rs
+++ b/mullvad-management-interface/src/types.rs
@@ -501,6 +501,7 @@ impl From<mullvad_types::relay_constraints::BridgeSettings> for BridgeSettings {
.option()
.map(RelayLocation::from),
providers: convert_providers_constraint(&constraints.providers),
+ ownership: convert_ownership_constraint(&constraints.ownership) as i32,
})
}
MullvadBridgeSettings::Custom(proxy_settings) => match proxy_settings {
@@ -553,6 +554,7 @@ impl From<mullvad_types::relay_constraints::RelaySettings> for RelaySettings {
relay_settings::Endpoint::Normal(NormalRelaySettings {
location: constraints.location.option().map(RelayLocation::from),
providers: convert_providers_constraint(&constraints.providers),
+ ownership: convert_ownership_constraint(&constraints.ownership) as i32,
tunnel_type: match constraints.tunnel_protocol {
Constraint::Any => None,
Constraint::Only(talpid_net::TunnelType::Wireguard) => {
@@ -878,6 +880,7 @@ impl TryFrom<RelaySettings> for mullvad_types::relay_constraints::RelaySettings
.map(Constraint::<mullvad_types::relay_constraints::LocationConstraint>::from)
.unwrap_or(Constraint::Any);
let providers = try_providers_constraint_from_proto(&settings.providers)?;
+ let ownership = try_ownership_constraint_from_i32(settings.ownership)?;
let tunnel_protocol = settings
.tunnel_type
.map(Constraint::<net::TunnelType>::try_from)
@@ -899,6 +902,7 @@ impl TryFrom<RelaySettings> for mullvad_types::relay_constraints::RelaySettings
mullvad_constraints::RelayConstraints {
location,
providers,
+ ownership,
tunnel_protocol,
wireguard_constraints,
openvpn_constraints,
@@ -958,6 +962,13 @@ impl TryFrom<RelaySettingsUpdate> for mullvad_types::relay_constraints::RelaySet
} else {
None
};
+ let ownership = if let Some(ref ownership_update) = settings.ownership {
+ Some(try_ownership_constraint_from_i32(
+ ownership_update.ownership,
+ )?)
+ } else {
+ None
+ };
let tunnel_protocol = if let Some(update) = settings.tunnel_type {
Some(
update
@@ -989,6 +1000,7 @@ impl TryFrom<RelaySettingsUpdate> for mullvad_types::relay_constraints::RelaySet
mullvad_constraints::RelayConstraintsUpdate {
location,
providers,
+ ownership,
tunnel_protocol,
wireguard_constraints,
openvpn_constraints,
@@ -1187,11 +1199,13 @@ impl TryFrom<BridgeSettings> for mullvad_types::relay_constraints::BridgeSetting
}
};
let providers = try_providers_constraint_from_proto(&constraints.providers)?;
+ let ownership = try_ownership_constraint_from_i32(constraints.ownership)?;
Ok(mullvad_constraints::BridgeSettings::Normal(
mullvad_constraints::BridgeConstraints {
location,
providers,
+ ownership,
},
))
}
@@ -1475,6 +1489,28 @@ pub fn try_providers_constraint_from_proto(
}
}
+pub fn try_ownership_constraint_from_i32(
+ ownership: i32,
+) -> Result<Constraint<mullvad_types::relay_constraints::Ownership>, FromProtobufTypeError> {
+ Ownership::from_i32(ownership)
+ .map(ownership_constraint_from_proto)
+ .ok_or(FromProtobufTypeError::InvalidArgument(
+ "invalid ownership argument",
+ ))
+}
+
+pub fn ownership_constraint_from_proto(
+ ownership: Ownership,
+) -> Constraint<mullvad_types::relay_constraints::Ownership> {
+ use mullvad_types::relay_constraints::Ownership as MullvadOwnership;
+
+ match ownership {
+ Ownership::Any => Constraint::Any,
+ Ownership::MullvadOwned => Constraint::Only(MullvadOwnership::MullvadOwned),
+ Ownership::Rented => Constraint::Only(MullvadOwnership::Rented),
+ }
+}
+
fn convert_providers_constraint(
providers: &Constraint<mullvad_types::relay_constraints::Providers>,
) -> Vec<String> {
@@ -1484,6 +1520,20 @@ fn convert_providers_constraint(
}
}
+fn convert_ownership_constraint(
+ ownership: &Constraint<mullvad_types::relay_constraints::Ownership>,
+) -> Ownership {
+ use mullvad_types::relay_constraints::Ownership as MullvadOwnership;
+
+ match ownership.as_ref() {
+ Constraint::Any => Ownership::Any,
+ Constraint::Only(ownership) => match ownership {
+ MullvadOwnership::MullvadOwned => Ownership::MullvadOwned,
+ MullvadOwnership::Rented => Ownership::Rented,
+ },
+ }
+}
+
impl From<FromProtobufTypeError> for crate::Status {
fn from(err: FromProtobufTypeError) -> Self {
match err {
diff --git a/mullvad-relay-selector/src/lib.rs b/mullvad-relay-selector/src/lib.rs
index e011d747dc..e21eee96fc 100644
--- a/mullvad-relay-selector/src/lib.rs
+++ b/mullvad-relay-selector/src/lib.rs
@@ -9,8 +9,9 @@ use mullvad_types::{
location::{Coordinates, Location},
relay_constraints::{
BridgeSettings, BridgeState, Constraint, InternalBridgeConstraints, LocationConstraint,
- Match, ObfuscationSettings, OpenVpnConstraints, Providers, RelayConstraints, RelaySettings,
- SelectedObfuscation, Set, TransportPort, Udp2TcpObfuscationSettings, WireguardConstraints,
+ Match, ObfuscationSettings, OpenVpnConstraints, Ownership, Providers, RelayConstraints,
+ RelaySettings, SelectedObfuscation, Set, TransportPort, Udp2TcpObfuscationSettings,
+ WireguardConstraints,
},
relay_list::{Relay, RelayList, Udp2TcpEndpointData},
CustomTunnelEndpoint,
@@ -322,6 +323,7 @@ impl RelaySelector {
Constraint::Only(TunnelType::OpenVpn) => self.get_openvpn_endpoint(
&relay_constraints.location,
&relay_constraints.providers,
+ &relay_constraints.ownership,
relay_constraints.openvpn_constraints.clone(),
bridge_state,
retry_attempt,
@@ -330,6 +332,7 @@ impl RelaySelector {
Constraint::Only(TunnelType::Wireguard) => self.get_wireguard_endpoint(
&relay_constraints.location,
&relay_constraints.providers,
+ &relay_constraints.ownership,
&relay_constraints.wireguard_constraints,
retry_attempt,
),
@@ -373,6 +376,7 @@ impl RelaySelector {
&self,
location: &Constraint<LocationConstraint>,
providers: &Constraint<Providers>,
+ ownership: &Constraint<Ownership>,
openvpn_constraints: OpenVpnConstraints,
bridge_state: BridgeState,
retry_attempt: u32,
@@ -380,6 +384,7 @@ impl RelaySelector {
let mut relay_matcher = RelayMatcher {
location: location.clone(),
providers: providers.clone(),
+ ownership: ownership.clone(),
tunnel: openvpn_constraints,
};
@@ -480,12 +485,14 @@ impl RelaySelector {
&self,
location: &Constraint<LocationConstraint>,
providers: &Constraint<Providers>,
+ ownership: &Constraint<Ownership>,
wireguard_constraints: &WireguardConstraints,
retry_attempt: u32,
) -> Result<NormalSelectedRelay, Error> {
let mut entry_relay_matcher = RelayMatcher {
location: location.clone(),
providers: providers.clone(),
+ ownership: ownership.clone(),
tunnel: wireguard_constraints.clone().into(),
};
@@ -628,6 +635,7 @@ impl RelaySelector {
retry_attempt,
&original_constraints.location,
&original_constraints.providers,
+ &original_constraints.ownership,
);
let mut relay_constraints = original_constraints.clone();
@@ -738,6 +746,7 @@ impl RelaySelector {
let bridge_constraints = InternalBridgeConstraints {
location: settings.location.clone(),
providers: settings.providers.clone(),
+ ownership: settings.ownership.clone(),
// FIXME: This is temporary while talpid-core only supports TCP proxies
transport_protocol: Constraint::Only(TransportProtocol::Tcp),
};
@@ -782,11 +791,13 @@ impl RelaySelector {
BridgeSettings::Normal(settings) => InternalBridgeConstraints {
location: settings.location.clone(),
providers: settings.providers.clone(),
+ ownership: settings.ownership.clone(),
transport_protocol: Constraint::Only(TransportProtocol::Tcp),
},
BridgeSettings::Custom(_bridge_settings) => InternalBridgeConstraints {
location: Constraint::Any,
providers: Constraint::Any,
+ ownership: Constraint::Any,
transport_protocol: Constraint::Only(TransportProtocol::Tcp),
},
};
@@ -949,6 +960,7 @@ impl RelaySelector {
retry_attempt: u32,
location_constraint: &Constraint<LocationConstraint>,
providers_constraint: &Constraint<Providers>,
+ ownership_constraint: &Constraint<Ownership>,
) -> (Constraint<u16>, TransportProtocol, TunnelType) {
#[cfg(target_os = "windows")]
{
@@ -958,6 +970,7 @@ impl RelaySelector {
&& !relay.tunnels.openvpn.is_empty()
&& location_constraint.matches(relay)
&& providers_constraint.matches(relay)
+ && ownership_constraint.matches(relay)
});
if location_supports_openvpn {
let (preferred_port, preferred_protocol) =
@@ -971,6 +984,7 @@ impl RelaySelector {
&& !relay.tunnels.wireguard.is_empty()
&& location_constraint.matches(relay)
&& providers_constraint.matches(relay)
+ && ownership_constraint.matches(relay)
});
// If location does not support WireGuard, defer to preferred OpenVPN tunnel
// constraints
@@ -1061,10 +1075,10 @@ impl RelaySelector {
relay: &Relay,
constraints: &InternalBridgeConstraints,
) -> Option<Relay> {
- if !constraints.location.matches(relay) {
- return None;
- }
- if !constraints.providers.matches(relay) {
+ if !constraints.location.matches(relay)
+ || !constraints.providers.matches(relay)
+ || !constraints.ownership.matches(relay)
+ {
return None;
}
@@ -1282,7 +1296,7 @@ mod test {
ipv6_addr_in: Some("2a03:1b20:5:f011::a10f".parse().unwrap()),
include_in_country: true,
active: true,
- owned: true,
+ owned: false,
provider: "31173".to_string(),
weight: 1,
tunnels: RelayTunnels {
@@ -1713,6 +1727,7 @@ mod test {
const WIREGUARD_MULTIHOP_CONSTRAINTS: RelayConstraints = RelayConstraints {
location: Constraint::Any,
providers: Constraint::Any,
+ ownership: Constraint::Any,
wireguard_constraints: WireguardConstraints {
use_multihop: true,
port: Constraint::Any,
@@ -1728,6 +1743,7 @@ mod test {
const WIREGUARD_SINGLEHOP_CONSTRAINTS: RelayConstraints = RelayConstraints {
location: Constraint::Any,
providers: Constraint::Any,
+ ownership: Constraint::Any,
wireguard_constraints: WireguardConstraints {
use_multihop: false,
port: Constraint::Any,
@@ -1878,4 +1894,35 @@ mod test {
.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();
+ for i in 0..10 {
+ constraints.ownership = Constraint::Only(Ownership::MullvadOwned);
+ let relay = relay_selector
+ .get_tunnel_endpoint(&constraints, BridgeState::Auto, i)
+ .unwrap();
+ assert!(matches!(
+ relay,
+ NormalSelectedRelay {
+ exit_relay: Relay { owned: true, .. },
+ ..
+ }
+ ));
+
+ constraints.ownership = Constraint::Only(Ownership::Rented);
+ let relay = relay_selector
+ .get_tunnel_endpoint(&constraints, BridgeState::Auto, i)
+ .unwrap();
+ assert!(matches!(
+ relay,
+ NormalSelectedRelay {
+ exit_relay: Relay { owned: false, .. },
+ ..
+ }
+ ));
+ }
+ }
}
diff --git a/mullvad-relay-selector/src/matcher.rs b/mullvad-relay-selector/src/matcher.rs
index ffd07b9121..3e88777b81 100644
--- a/mullvad-relay-selector/src/matcher.rs
+++ b/mullvad-relay-selector/src/matcher.rs
@@ -1,8 +1,8 @@
use mullvad_types::{
endpoint::{MullvadEndpoint, MullvadWireguardEndpoint},
relay_constraints::{
- Constraint, LocationConstraint, Match, OpenVpnConstraints, Providers, RelayConstraints,
- WireguardConstraints,
+ Constraint, LocationConstraint, Match, OpenVpnConstraints, Ownership, Providers,
+ RelayConstraints, WireguardConstraints,
},
relay_list::{Relay, RelayTunnels, WireguardEndpointData},
};
@@ -14,6 +14,7 @@ use talpid_types::net::{all_of_the_internet, wireguard, IpVersion, TunnelType};
pub struct RelayMatcher<T: TunnelMatcher> {
pub location: Constraint<LocationConstraint>,
pub providers: Constraint<Providers>,
+ pub ownership: Constraint<Ownership>,
pub tunnel: T,
}
@@ -22,6 +23,7 @@ impl From<RelayConstraints> for RelayMatcher<AnyTunnelMatcher> {
Self {
location: constraints.location,
providers: constraints.providers,
+ ownership: constraints.ownership,
tunnel: AnyTunnelMatcher {
wireguard: constraints.wireguard_constraints.into(),
openvpn: constraints.openvpn_constraints,
@@ -37,6 +39,7 @@ impl RelayMatcher<AnyTunnelMatcher> {
tunnel: self.tunnel.wireguard,
location: self.location,
providers: self.providers,
+ ownership: self.ownership,
}
}
}
@@ -51,7 +54,10 @@ impl<T: TunnelMatcher> RelayMatcher<T> {
/// Filter a relay and its endpoints based on constraints.
/// Only matching endpoints are included in the returned Relay.
pub fn filter_matching_relay(&self, relay: &Relay) -> Option<Relay> {
- if !self.location.matches(relay) || !self.providers.matches(relay) {
+ if !self.location.matches(relay)
+ || !self.providers.matches(relay)
+ || !self.ownership.matches(relay)
+ {
return None;
}
diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs
index e3fd854df3..7dff97d1e4 100644
--- a/mullvad-types/src/relay_constraints.rs
+++ b/mullvad-types/src/relay_constraints.rs
@@ -185,6 +185,8 @@ pub struct RelayConstraints {
#[cfg_attr(target_os = "android", jnix(skip))]
pub providers: Constraint<Providers>,
#[cfg_attr(target_os = "android", jnix(skip))]
+ pub ownership: Constraint<Ownership>,
+ #[cfg_attr(target_os = "android", jnix(skip))]
pub tunnel_protocol: Constraint<TunnelType>,
#[cfg_attr(target_os = "android", jnix(skip))]
pub wireguard_constraints: WireguardConstraints,
@@ -199,6 +201,7 @@ impl Default for RelayConstraints {
tunnel_protocol: Constraint::Only(TunnelType::Wireguard),
location: Constraint::default(),
providers: Constraint::default(),
+ ownership: Constraint::default(),
wireguard_constraints: WireguardConstraints::default(),
openvpn_constraints: OpenVpnConstraints::default(),
}
@@ -210,6 +213,7 @@ impl RelayConstraints {
RelayConstraints {
location: update.location.unwrap_or_else(|| self.location.clone()),
providers: update.providers.unwrap_or_else(|| self.providers.clone()),
+ ownership: update.ownership.unwrap_or_else(|| self.ownership.clone()),
tunnel_protocol: update
.tunnel_protocol
.unwrap_or_else(|| self.tunnel_protocol.clone()),
@@ -250,8 +254,14 @@ impl fmt::Display for RelayConstraints {
}
write!(f, " using ")?;
match self.providers {
- Constraint::Any => write!(f, "any provider"),
- Constraint::Only(ref constraint) => constraint.fmt(f),
+ Constraint::Any => write!(f, "any provider")?,
+ Constraint::Only(ref constraint) => constraint.fmt(f)?,
+ }
+ match self.ownership {
+ Constraint::Any => Ok(()),
+ Constraint::Only(ref constraint) => {
+ write!(f, " and {}", constraint)
+ }
}
}
}
@@ -318,6 +328,31 @@ impl Set<LocationConstraint> for LocationConstraint {
}
}
+/// Limits the set of servers to choose based on ownership.
+#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)]
+pub enum Ownership {
+ MullvadOwned,
+ Rented,
+}
+
+impl Match<Relay> for Ownership {
+ fn matches(&self, relay: &Relay) -> bool {
+ match self {
+ Ownership::MullvadOwned => relay.owned,
+ Ownership::Rented => !relay.owned,
+ }
+ }
+}
+
+impl fmt::Display for Ownership {
+ fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> {
+ match self {
+ Ownership::MullvadOwned => write!(f, "Mullvad-owned servers"),
+ Ownership::Rented => write!(f, "rented servers"),
+ }
+ }
+}
+
/// Limits the set of [`crate::relay_list::Relay`]s used by a `RelaySelector` based on
/// provider.
pub type Provider = String;
@@ -519,6 +554,7 @@ pub struct ObfuscationSettings {
pub struct BridgeConstraints {
pub location: Constraint<LocationConstraint>,
pub providers: Constraint<Providers>,
+ pub ownership: Constraint<Ownership>,
}
impl fmt::Display for BridgeConstraints {
@@ -529,8 +565,14 @@ impl fmt::Display for BridgeConstraints {
}
write!(f, " using ")?;
match self.providers {
- Constraint::Any => write!(f, "any provider"),
- Constraint::Only(ref constraint) => constraint.fmt(f),
+ Constraint::Any => write!(f, "any provider")?,
+ Constraint::Only(ref constraint) => constraint.fmt(f)?,
+ }
+ match self.ownership {
+ Constraint::Any => Ok(()),
+ Constraint::Only(ref constraint) => {
+ write!(f, " and {}", constraint)
+ }
}
}
}
@@ -562,6 +604,7 @@ impl fmt::Display for BridgeState {
pub struct InternalBridgeConstraints {
pub location: Constraint<LocationConstraint>,
pub providers: Constraint<Providers>,
+ pub ownership: Constraint<Ownership>,
pub transport_protocol: Constraint<TransportProtocol>,
}
@@ -613,6 +656,8 @@ pub struct RelayConstraintsUpdate {
#[cfg_attr(target_os = "android", jnix(default))]
pub providers: Option<Constraint<Providers>>,
#[cfg_attr(target_os = "android", jnix(default))]
+ pub ownership: Option<Constraint<Ownership>>,
+ #[cfg_attr(target_os = "android", jnix(default))]
pub tunnel_protocol: Option<Constraint<TunnelType>>,
#[cfg_attr(target_os = "android", jnix(default))]
pub wireguard_constraints: Option<WireguardConstraints>,