diff options
| author | David Lönnhager <david.l@mullvad.net> | 2022-05-04 16:28:25 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2022-05-09 17:19:13 +0200 |
| commit | aeef04647359ceb42370e5fed0b16a07a388b852 (patch) | |
| tree | 783c92515d0c784e31cbd870df44a3a8bb112a5a | |
| parent | e08b1c0e00c8e1526b428818c4feed05e8a7f334 (diff) | |
| download | mullvadvpn-aeef04647359ceb42370e5fed0b16a07a388b852.tar.xz mullvadvpn-aeef04647359ceb42370e5fed0b16a07a388b852.zip | |
Add server ownership constraint to the daemon and CLI
| -rw-r--r-- | mullvad-cli/src/cmds/bridge.rs | 46 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/relay.rs | 44 | ||||
| -rw-r--r-- | mullvad-management-interface/proto/management_interface.proto | 13 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types.rs | 50 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/lib.rs | 26 | ||||
| -rw-r--r-- | mullvad-relay-selector/src/matcher.rs | 12 | ||||
| -rw-r--r-- | mullvad-types/src/relay_constraints.rs | 53 |
7 files changed, 227 insertions, 17 deletions
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..d1470a1ad5 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; } 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>, |
