diff options
| author | David Lönnhager <david.l@mullvad.net> | 2021-04-29 12:30:52 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2021-04-29 12:30:52 +0200 |
| commit | b45459236d1832e944885963cee37d81033139eb (patch) | |
| tree | 85877256be72252f95f5374c00abd0bccac5710c | |
| parent | 6f1427902092012c8e975494bb5f177861d25979 (diff) | |
| parent | a726f5d732b12cc3e7aa669b5988233432a4d6cc (diff) | |
| download | mullvadvpn-b45459236d1832e944885963cee37d81033139eb.tar.xz mullvadvpn-b45459236d1832e944885963cee37d81033139eb.zip | |
Merge branch 'improve-proto-conversions'
| -rw-r--r-- | Cargo.lock | 2 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 942 | ||||
| -rw-r--r-- | mullvad-daemon/src/relays.rs | 10 | ||||
| -rw-r--r-- | mullvad-management-interface/Cargo.toml | 2 | ||||
| -rw-r--r-- | mullvad-management-interface/src/lib.rs | 6 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types.rs | 1018 | ||||
| -rw-r--r-- | mullvad-types/src/relay_constraints.rs | 2 | ||||
| -rw-r--r-- | mullvad-types/src/settings/mod.rs | 4 |
8 files changed, 1078 insertions, 908 deletions
diff --git a/Cargo.lock b/Cargo.lock index 66d327ae81..9d3b2f2c53 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -1351,10 +1351,12 @@ dependencies = [ "futures", "lazy_static", "mullvad-paths", + "mullvad-types", "nix 0.19.1", "parity-tokio-ipc", "prost", "prost-types", + "talpid-types", "tokio", "tonic", "tonic-build", diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 05bd38e1dc..a9b07a1214 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -10,18 +10,12 @@ use mullvad_rpc::{rest::Error as RestError, StatusCode}; use mullvad_types::settings::DnsOptions; use mullvad_types::{ account::AccountToken, - location::GeoIpLocation, - relay_constraints::{ - BridgeConstraints, BridgeSettings, BridgeState, Constraint, LocationConstraint, - OpenVpnConstraints, Providers, RelayConstraintsUpdate, RelaySettings, RelaySettingsUpdate, - WireguardConstraints, - }, - relay_list::{Relay, RelayList, RelayListCountry}, - settings::{Settings, TunnelOptions}, + relay_constraints::{BridgeSettings, BridgeState, RelaySettingsUpdate}, + relay_list::RelayList, + settings::Settings, states::{TargetState, TunnelState}, version, - wireguard::{self, RotationInterval, RotationIntervalError}, - ConnectionConfig, + wireguard::{RotationInterval, RotationIntervalError}, }; use parking_lot::RwLock; use std::{ @@ -30,10 +24,7 @@ use std::{ sync::{mpsc, Arc}, time::Duration, }; -use talpid_types::{ - net::{IpVersion, TransportProtocol, TunnelType}, - ErrorExt, -}; +use talpid_types::ErrorExt; #[derive(err_derive::Error, Debug)] #[error(no_from)] @@ -97,7 +88,7 @@ impl ManagementService for ManagementServiceImpl { let (tx, rx) = oneshot::channel(); self.send_command_to_daemon(DaemonCommand::GetState(tx))?; let state = self.wait_for_result(rx).await?; - Ok(Response::new(convert_state(state))) + Ok(Response::new(types::TunnelState::from(state))) } // Control the daemon and receive events @@ -157,7 +148,7 @@ impl ManagementService for ManagementServiceImpl { self.wait_for_result(rx) .await? .ok_or(Status::not_found("no version cache")) - .map(|version_info| convert_version_info(&version_info)) + .map(types::AppVersionInfo::from) .map(Response::new) } @@ -176,7 +167,12 @@ impl ManagementService for ManagementServiceImpl { ) -> ServiceResult<()> { log::debug!("update_relay_settings"); let (tx, rx) = oneshot::channel(); - let constraints_update = convert_relay_settings_update(&request.into_inner())?; + let constraints_update = + RelaySettingsUpdate::try_from(request.into_inner()).map_err(|error| match error { + types::FromProtobufTypeError::InvalidArgument(error) => { + Status::invalid_argument(error) + } + })?; let message = DaemonCommand::UpdateRelaySettings(tx, constraints_update); self.send_command_to_daemon(message)?; @@ -200,9 +196,9 @@ impl ManagementService for ManagementServiceImpl { tokio::sync::mpsc::channel(cmp::max(1, locations.countries.len())); tokio::spawn(async move { - for country in &locations.countries { + for country in locations.countries.into_iter() { if let Err(error) = stream_tx - .send(Ok(convert_relay_list_country(country))) + .send(Ok(types::RelayListCountry::from(country))) .await { log::error!( @@ -222,7 +218,7 @@ impl ManagementService for ManagementServiceImpl { self.send_command_to_daemon(DaemonCommand::GetCurrentLocation(tx))?; let result = self.wait_for_result(rx).await?; match result { - Some(geoip) => Ok(Response::new(convert_geoip_location(geoip))), + Some(geoip) => Ok(Response::new(types::GeoIpLocation::from(geoip))), None => Err(Status::not_found("no location was found")), } } @@ -231,78 +227,12 @@ impl ManagementService for ManagementServiceImpl { &self, request: Request<types::BridgeSettings>, ) -> ServiceResult<()> { - use talpid_types::net; - use types::bridge_settings::Type as BridgeSettingType; - - let settings = request - .into_inner() - .r#type - .ok_or(Status::invalid_argument("no settings provided"))?; - - let settings = match settings { - BridgeSettingType::Normal(constraints) => { - let location = match constraints.location { - None => Constraint::Any, - Some(location) => convert_proto_location(location), - }; - let providers = if constraints.providers.is_empty() { - Constraint::Any - } else { - Constraint::Only( - Providers::new(constraints.providers.clone().into_iter()).map_err( - |_| Status::invalid_argument("must specify at least one provider"), - )?, - ) - }; - - BridgeSettings::Normal(BridgeConstraints { - location, - providers, - }) - } - BridgeSettingType::Local(proxy_settings) => { - let peer = proxy_settings - .peer - .parse() - .map_err(|_| Status::invalid_argument("failed to parse peer address"))?; - let proxy_settings = - net::openvpn::ProxySettings::Local(net::openvpn::LocalProxySettings { - port: proxy_settings.port as u16, - peer, - }); - BridgeSettings::Custom(proxy_settings) - } - BridgeSettingType::Remote(proxy_settings) => { - let address = proxy_settings - .address - .parse() - .map_err(|_| Status::invalid_argument("failed to parse IP address"))?; - let auth = proxy_settings.auth.map(|auth| net::openvpn::ProxyAuth { - username: auth.username, - password: auth.password, - }); - let proxy_settings = - net::openvpn::ProxySettings::Remote(net::openvpn::RemoteProxySettings { - address, - auth, - }); - BridgeSettings::Custom(proxy_settings) - } - BridgeSettingType::Shadowsocks(proxy_settings) => { - let peer = proxy_settings - .peer - .parse() - .map_err(|_| Status::invalid_argument("failed to parse peer address"))?; - let proxy_settings = net::openvpn::ProxySettings::Shadowsocks( - net::openvpn::ShadowsocksProxySettings { - peer, - password: proxy_settings.password, - cipher: proxy_settings.cipher, - }, - ); - BridgeSettings::Custom(proxy_settings) - } - }; + let settings = + BridgeSettings::try_from(request.into_inner()).map_err(|error| match error { + types::FromProtobufTypeError::InvalidArgument(error) => { + Status::invalid_argument(error) + } + })?; log::debug!("set_bridge_settings({:?})", settings); @@ -315,14 +245,12 @@ impl ManagementService for ManagementServiceImpl { } async fn set_bridge_state(&self, request: Request<types::BridgeState>) -> ServiceResult<()> { - use types::bridge_state::State; - - let bridge_state = match State::from_i32(request.into_inner().state) { - Some(State::Auto) => BridgeState::Auto, - Some(State::On) => BridgeState::On, - Some(State::Off) => BridgeState::Off, - None => return Err(Status::invalid_argument("unknown bridge state")), - }; + let bridge_state = + BridgeState::try_from(request.into_inner()).map_err(|error| match error { + types::FromProtobufTypeError::InvalidArgument(error) => { + Status::invalid_argument(error) + } + })?; log::debug!("set_bridge_state({:?})", bridge_state); let (tx, rx) = oneshot::channel(); @@ -342,7 +270,7 @@ impl ManagementService for ManagementServiceImpl { self.send_command_to_daemon(DaemonCommand::GetSettings(tx))?; self.wait_for_result(rx) .await - .map(|settings| Response::new(convert_settings(&settings))) + .map(|settings| Response::new(types::Settings::from(&settings))) } async fn set_allow_lan(&self, request: Request<bool>) -> ServiceResult<()> { @@ -640,7 +568,7 @@ impl ManagementService for ManagementServiceImpl { self.send_command_to_daemon(DaemonCommand::GenerateWireguardKey(tx))?; self.wait_for_result(rx) .await? - .map(|event| Response::new(convert_wireguard_key_event(&event))) + .map(|event| Response::new(types::KeygenEvent::from(event))) .map_err(map_daemon_error) } @@ -650,7 +578,7 @@ impl ManagementService for ManagementServiceImpl { self.send_command_to_daemon(DaemonCommand::GetWireguardKey(tx))?; let key = self.wait_for_result(rx).await?.map_err(map_daemon_error)?; match key { - Some(key) => Ok(Response::new(convert_public_key(&key))), + Some(key) => Ok(Response::new(types::PublicKey::from(key))), None => Err(Status::not_found("no WireGuard key was found")), } } @@ -761,790 +689,6 @@ impl ManagementServiceImpl { } } -fn convert_settings(settings: &Settings) -> types::Settings { - types::Settings { - account_token: settings.get_account_token().unwrap_or_default(), - relay_settings: Some(convert_relay_settings(&settings.get_relay_settings())), - bridge_settings: Some(convert_bridge_settings(&settings.bridge_settings)), - bridge_state: Some(convert_bridge_state(settings.get_bridge_state())), - allow_lan: settings.allow_lan, - block_when_disconnected: settings.block_when_disconnected, - auto_connect: settings.auto_connect, - tunnel_options: Some(convert_tunnel_options(&settings.tunnel_options)), - show_beta_releases: settings.show_beta_releases, - } -} - -fn convert_relay_settings_update( - settings: &types::RelaySettingsUpdate, -) -> Result<RelaySettingsUpdate, Status> { - use mullvad_types::CustomTunnelEndpoint; - use talpid_types::net::{self, openvpn, wireguard}; - use types::{ - connection_config::Config as ProtoConnectionConfig, - relay_settings_update::Type as ProtoUpdateType, - }; - - let update_value = settings - .r#type - .clone() - .ok_or(Status::invalid_argument("missing relay settings"))?; - - match update_value { - ProtoUpdateType::Custom(settings) => { - let config = settings - .config - .ok_or(Status::invalid_argument("missing relay settings"))?; - let config = config - .config - .ok_or(Status::invalid_argument("missing relay settings"))?; - let config = match config { - ProtoConnectionConfig::Openvpn(config) => { - let address = match config.address.parse() { - Ok(address) => address, - Err(_) => return Err(Status::invalid_argument("invalid address")), - }; - - ConnectionConfig::OpenVpn(openvpn::ConnectionConfig { - endpoint: net::Endpoint { - address, - protocol: convert_proto_transport_protocol(config.protocol)?, - }, - username: config.username.clone(), - password: config.password.clone(), - }) - } - ProtoConnectionConfig::Wireguard(config) => { - let tunnel = config - .tunnel - .ok_or(Status::invalid_argument("missing tunnel config"))?; - - // Copy the private key to an array - if tunnel.private_key.len() != 32 { - return Err(Status::invalid_argument("invalid private key")); - } - - let mut private_key = [0; 32]; - let buffer = &tunnel.private_key[..private_key.len()]; - private_key.copy_from_slice(buffer); - - let peer = config - .peer - .ok_or(Status::invalid_argument("missing peer config"))?; - - // Copy the public key to an array - if peer.public_key.len() != 32 { - return Err(Status::invalid_argument("invalid public key")); - } - - let mut public_key = [0; 32]; - let buffer = &peer.public_key[..public_key.len()]; - public_key.copy_from_slice(buffer); - - let ipv4_gateway = match config.ipv4_gateway.parse() { - Ok(address) => address, - Err(_) => return Err(Status::invalid_argument("invalid IPv4 gateway")), - }; - let ipv6_gateway = if !config.ipv6_gateway.is_empty() { - let address = match config.ipv6_gateway.parse() { - Ok(address) => address, - Err(_) => return Err(Status::invalid_argument("invalid IPv6 gateway")), - }; - Some(address) - } else { - None - }; - - let endpoint = match peer.endpoint.parse() { - Ok(address) => address, - Err(_) => return Err(Status::invalid_argument("invalid peer address")), - }; - - let mut tunnel_addresses = Vec::new(); - for address in tunnel.addresses { - let address = address - .parse() - .map_err(|_| Status::invalid_argument("invalid address"))?; - tunnel_addresses.push(address); - } - - let mut allowed_ips = Vec::new(); - for address in peer.allowed_ips { - let address = address - .parse() - .map_err(|_| Status::invalid_argument("invalid address"))?; - allowed_ips.push(address); - } - - ConnectionConfig::Wireguard(wireguard::ConnectionConfig { - tunnel: wireguard::TunnelConfig { - private_key: wireguard::PrivateKey::from(private_key), - addresses: tunnel_addresses, - }, - peer: wireguard::PeerConfig { - public_key: wireguard::PublicKey::from(public_key), - allowed_ips, - endpoint, - protocol: convert_proto_transport_protocol(peer.protocol)?, - }, - ipv4_gateway, - ipv6_gateway, - }) - } - }; - - Ok(RelaySettingsUpdate::CustomTunnelEndpoint( - CustomTunnelEndpoint { - host: settings.host.clone(), - config, - }, - )) - } - - ProtoUpdateType::Normal(settings) => { - // If `location` isn't provided, no changes are made. - // If `location` is provided, but is an empty vector, - // then the constraint is set to `Constraint::Any`. - let location = settings.location.map(convert_proto_location); - - let tunnel_protocol = if let Some(update) = settings.tunnel_type { - match update.tunnel_type { - Some(constraint) => match types::TunnelType::from_i32(constraint.tunnel_type) { - Some(types::TunnelType::Openvpn) => { - Some(Constraint::Only(TunnelType::OpenVpn)) - } - Some(types::TunnelType::Wireguard) => { - Some(Constraint::Only(TunnelType::Wireguard)) - } - None => return Err(Status::invalid_argument("invalid tunnel protocol")), - }, - None => Some(Constraint::Any), - } - } else { - None - }; - - let transport_protocol = if let Some(ref constraints) = settings.openvpn_constraints { - match &constraints.protocol { - Some(constraint) => { - Some(convert_proto_transport_protocol(constraint.protocol)?) - } - None => None, - } - } else { - None - }; - - let providers = if let Some(ref provider_update) = settings.providers { - if !provider_update.providers.is_empty() { - Some(Constraint::Only( - Providers::new(provider_update.providers.clone().into_iter()).map_err( - |_| Status::invalid_argument("must specify at least one provider"), - )?, - )) - } else { - Some(Constraint::Any) - } - } else { - None - }; - let ip_version = if let Some(ref constraints) = settings.wireguard_constraints { - match &constraints.ip_version { - Some(constraint) => match types::IpVersion::from_i32(constraint.protocol) { - Some(types::IpVersion::V4) => Some(IpVersion::V4), - Some(types::IpVersion::V6) => Some(IpVersion::V6), - None => { - return Err(Status::invalid_argument("invalid ip protocol version")) - } - }, - None => None, - } - } else { - None - }; - - Ok(RelaySettingsUpdate::Normal(RelayConstraintsUpdate { - location, - providers, - tunnel_protocol, - wireguard_constraints: settings.wireguard_constraints.map(|constraints| { - WireguardConstraints { - port: if constraints.port != 0 { - Constraint::Only(constraints.port as u16) - } else { - Constraint::Any - }, - ip_version: Constraint::from(ip_version), - } - }), - openvpn_constraints: settings.openvpn_constraints.map(|constraints| { - OpenVpnConstraints { - port: if constraints.port != 0 { - Constraint::Only(constraints.port as u16) - } else { - Constraint::Any - }, - protocol: Constraint::from(transport_protocol), - } - }), - })) - } - } -} - -fn convert_relay_settings(settings: &RelaySettings) -> types::RelaySettings { - use types::relay_settings; - - let endpoint = match settings { - RelaySettings::CustomTunnelEndpoint(endpoint) => { - relay_settings::Endpoint::Custom(types::CustomRelaySettings { - host: endpoint.host.clone(), - config: Some(convert_connection_config(&endpoint.config)), - }) - } - RelaySettings::Normal(constraints) => { - relay_settings::Endpoint::Normal(types::NormalRelaySettings { - location: convert_location_constraint(&constraints.location), - providers: convert_providers_constraint(&constraints.providers), - tunnel_type: match constraints.tunnel_protocol { - Constraint::Any => None, - Constraint::Only(TunnelType::Wireguard) => Some(types::TunnelType::Wireguard), - Constraint::Only(TunnelType::OpenVpn) => Some(types::TunnelType::Openvpn), - } - .map(|tunnel_type| types::TunnelTypeConstraint { - tunnel_type: i32::from(tunnel_type), - }), - - wireguard_constraints: Some(types::WireguardConstraints { - port: u32::from(constraints.wireguard_constraints.port.unwrap_or(0)), - ip_version: constraints - .wireguard_constraints - .ip_version - .option() - .map(|version| match version { - IpVersion::V4 => types::IpVersion::V4, - IpVersion::V6 => types::IpVersion::V6, - }) - .map(|version| types::IpVersionConstraint { - protocol: i32::from(version), - }), - }), - - openvpn_constraints: Some(types::OpenvpnConstraints { - port: u32::from(constraints.openvpn_constraints.port.unwrap_or(0)), - protocol: constraints - .openvpn_constraints - .protocol - .as_ref() - .option() - .map(|protocol| match protocol { - TransportProtocol::Tcp => types::TransportProtocol::Tcp, - TransportProtocol::Udp => types::TransportProtocol::Udp, - }) - .map(|protocol| types::TransportProtocolConstraint { - protocol: i32::from(protocol), - }), - }), - }) - } - }; - - types::RelaySettings { - endpoint: Some(endpoint), - } -} - -fn convert_connection_config(config: &ConnectionConfig) -> types::ConnectionConfig { - use types::connection_config; - - types::ConnectionConfig { - config: Some(match config { - ConnectionConfig::OpenVpn(config) => { - connection_config::Config::Openvpn(connection_config::OpenvpnConfig { - address: config.endpoint.address.to_string(), - protocol: match config.endpoint.protocol { - TransportProtocol::Tcp => i32::from(types::TransportProtocol::Tcp), - TransportProtocol::Udp => i32::from(types::TransportProtocol::Udp), - }, - username: config.username.clone(), - password: config.password.clone(), - }) - } - ConnectionConfig::Wireguard(config) => { - connection_config::Config::Wireguard(connection_config::WireguardConfig { - tunnel: Some(connection_config::wireguard_config::TunnelConfig { - private_key: config.tunnel.private_key.to_bytes().to_vec(), - addresses: config - .tunnel - .addresses - .iter() - .map(|address| address.to_string()) - .collect(), - }), - peer: Some(connection_config::wireguard_config::PeerConfig { - public_key: config.peer.public_key.as_bytes().to_vec(), - allowed_ips: config - .peer - .allowed_ips - .iter() - .map(|address| address.to_string()) - .collect(), - endpoint: config.peer.endpoint.to_string(), - protocol: i32::from(match config.peer.protocol { - TransportProtocol::Udp => types::TransportProtocol::Udp, - TransportProtocol::Tcp => types::TransportProtocol::Tcp, - }), - }), - ipv4_gateway: config.ipv4_gateway.to_string(), - ipv6_gateway: config - .ipv6_gateway - .as_ref() - .map(|address| address.to_string()) - .unwrap_or_default(), - }) - } - }), - } -} - -fn convert_bridge_settings(settings: &BridgeSettings) -> types::BridgeSettings { - use talpid_types::net; - use types::bridge_settings::{self, Type as BridgeSettingType}; - - let settings = match settings { - BridgeSettings::Normal(constraints) => { - BridgeSettingType::Normal(types::bridge_settings::BridgeConstraints { - location: convert_location_constraint(&constraints.location), - providers: convert_providers_constraint(&constraints.providers), - }) - } - BridgeSettings::Custom(proxy_settings) => match proxy_settings { - net::openvpn::ProxySettings::Local(proxy_settings) => { - BridgeSettingType::Local(bridge_settings::LocalProxySettings { - port: u32::from(proxy_settings.port), - peer: proxy_settings.peer.to_string(), - }) - } - net::openvpn::ProxySettings::Remote(proxy_settings) => { - BridgeSettingType::Remote(bridge_settings::RemoteProxySettings { - address: proxy_settings.address.to_string(), - auth: proxy_settings.auth.as_ref().map(|auth| { - bridge_settings::RemoteProxyAuth { - username: auth.username.clone(), - password: auth.password.clone(), - } - }), - }) - } - net::openvpn::ProxySettings::Shadowsocks(proxy_settings) => { - BridgeSettingType::Shadowsocks(bridge_settings::ShadowsocksProxySettings { - peer: proxy_settings.peer.to_string(), - password: proxy_settings.password.clone(), - cipher: proxy_settings.cipher.clone(), - }) - } - }, - }; - - types::BridgeSettings { - r#type: Some(settings), - } -} - -fn convert_wireguard_key_event( - event: &mullvad_types::wireguard::KeygenEvent, -) -> types::KeygenEvent { - use mullvad_types::wireguard::KeygenEvent::*; - use types::keygen_event::KeygenEvent as ProtoEvent; - - types::KeygenEvent { - event: match event { - NewKey(_) => i32::from(ProtoEvent::NewKey), - TooManyKeys => i32::from(ProtoEvent::TooManyKeys), - GenerationFailure => i32::from(ProtoEvent::GenerationFailure), - }, - new_key: if let NewKey(key) = event { - Some(convert_public_key(&key)) - } else { - None - }, - } -} - -fn convert_public_key(public_key: &wireguard::PublicKey) -> types::PublicKey { - types::PublicKey { - key: public_key.key.as_bytes().to_vec(), - created: Some(types::Timestamp { - seconds: public_key.created.timestamp(), - nanos: 0, - }), - } -} - -fn convert_location_constraint( - location: &Constraint<LocationConstraint>, -) -> Option<types::RelayLocation> { - location.as_ref().option().map(|location| match location { - LocationConstraint::Country(country) => types::RelayLocation { - country: country.to_string(), - ..Default::default() - }, - LocationConstraint::City(country, city) => types::RelayLocation { - country: country.to_string(), - city: city.to_string(), - ..Default::default() - }, - LocationConstraint::Hostname(country, city, hostname) => types::RelayLocation { - country: country.to_string(), - city: city.to_string(), - hostname: hostname.to_string(), - }, - }) -} - -fn convert_providers_constraint(providers: &Constraint<Providers>) -> Vec<String> { - match providers.as_ref() { - Constraint::Any => vec![], - Constraint::Only(providers) => Vec::from(providers.clone()), - } -} - -fn convert_bridge_state(state: &BridgeState) -> types::BridgeState { - let state = match state { - BridgeState::Auto => types::bridge_state::State::Auto, - BridgeState::On => types::bridge_state::State::On, - BridgeState::Off => types::bridge_state::State::Off, - }; - types::BridgeState { - state: i32::from(state), - } -} - -fn convert_tunnel_options(options: &TunnelOptions) -> types::TunnelOptions { - types::TunnelOptions { - openvpn: Some(types::tunnel_options::OpenvpnOptions { - mssfix: u32::from(options.openvpn.mssfix.unwrap_or_default()), - }), - wireguard: Some(types::tunnel_options::WireguardOptions { - mtu: u32::from(options.wireguard.options.mtu.unwrap_or_default()), - rotation_interval: options - .wireguard - .rotation_interval - .map(|ivl| types::Duration::from(Duration::from(ivl))), - }), - generic: Some(types::tunnel_options::GenericOptions { - enable_ipv6: options.generic.enable_ipv6, - }), - #[cfg(not(target_os = "android"))] - dns_options: Some(types::DnsOptions { - custom: options.dns_options.custom, - addresses: options - .dns_options - .addresses - .iter() - .map(|addr| addr.to_string()) - .collect(), - }), - #[cfg(target_os = "android")] - dns_options: None, - } -} - -fn convert_relay_list_country(country: &RelayListCountry) -> types::RelayListCountry { - let mut proto_country = types::RelayListCountry { - name: country.name.clone(), - code: country.code.clone(), - cities: Vec::with_capacity(country.cities.len()), - }; - - for city in &country.cities { - proto_country.cities.push(types::RelayListCity { - name: city.name.clone(), - code: city.code.clone(), - latitude: city.latitude, - longitude: city.longitude, - relays: city - .relays - .iter() - .map(|relay| convert_relay(relay)) - .collect(), - }); - } - - proto_country -} - -fn convert_relay(relay: &Relay) -> types::Relay { - types::Relay { - hostname: relay.hostname.clone(), - ipv4_addr_in: relay.ipv4_addr_in.to_string(), - ipv6_addr_in: relay - .ipv6_addr_in - .map(|addr| addr.to_string()) - .unwrap_or_default(), - include_in_country: relay.include_in_country, - active: relay.active, - owned: relay.owned, - provider: relay.provider.clone(), - weight: relay.weight, - tunnels: Some(types::RelayTunnels { - openvpn: relay - .tunnels - .openvpn - .iter() - .map(|endpoint| { - let protocol = match endpoint.protocol { - TransportProtocol::Udp => types::TransportProtocol::Udp, - TransportProtocol::Tcp => types::TransportProtocol::Tcp, - }; - types::OpenVpnEndpointData { - port: u32::from(endpoint.port), - protocol: i32::from(protocol), - } - }) - .collect(), - wireguard: relay - .tunnels - .wireguard - .iter() - .map(|endpoint| { - let port_ranges = endpoint - .port_ranges - .iter() - .map(|range| types::PortRange { - first: u32::from(range.0), - last: u32::from(range.1), - }) - .collect(); - types::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(types::RelayBridges { - shadowsocks: relay - .bridges - .shadowsocks - .iter() - .map(|endpoint| { - let protocol = match endpoint.protocol { - TransportProtocol::Udp => types::TransportProtocol::Udp, - TransportProtocol::Tcp => types::TransportProtocol::Tcp, - }; - types::ShadowsocksEndpointData { - port: u32::from(endpoint.port), - cipher: endpoint.cipher.clone(), - password: endpoint.password.clone(), - protocol: i32::from(protocol), - } - }) - .collect(), - }), - location: relay.location.as_ref().map(|location| types::Location { - country: location.country.clone(), - country_code: location.country_code.clone(), - city: location.city.clone(), - city_code: location.city_code.clone(), - latitude: location.latitude, - longitude: location.longitude, - }), - } -} - -fn convert_state(state: TunnelState) -> types::TunnelState { - use talpid_types::tunnel::{ - ActionAfterDisconnect, ErrorStateCause, FirewallPolicyError, ParameterGenerationError, - }; - use types::{ - error_state::{ - firewall_policy_error::ErrorType as PolicyErrorType, Cause as ProtoErrorCause, - FirewallPolicyError as ProtoFirewallPolicyError, - GenerationError as ProtoGenerationError, - }, - tunnel_state::{self, State as ProtoState}, - }; - use TunnelState::*; - - let map_firewall_error = |firewall_error: &FirewallPolicyError| match firewall_error { - FirewallPolicyError::Generic => ProtoFirewallPolicyError { - r#type: i32::from(PolicyErrorType::Generic), - ..Default::default() - }, - #[cfg(windows)] - FirewallPolicyError::Locked(blocking_app) => { - let (lock_pid, lock_name) = match blocking_app { - Some(app) => (app.pid, app.name.clone()), - None => (0, "".to_string()), - }; - - ProtoFirewallPolicyError { - r#type: i32::from(PolicyErrorType::Locked), - lock_pid, - lock_name, - } - } - }; - - let state = match state { - Disconnected => ProtoState::Disconnected(tunnel_state::Disconnected {}), - Connecting { endpoint, location } => ProtoState::Connecting(tunnel_state::Connecting { - relay_info: Some(types::TunnelStateRelayInfo { - tunnel_endpoint: Some(convert_endpoint(endpoint)), - location: location.map(convert_geoip_location), - }), - }), - Connected { endpoint, location } => ProtoState::Connected(tunnel_state::Connected { - relay_info: Some(types::TunnelStateRelayInfo { - tunnel_endpoint: Some(convert_endpoint(endpoint)), - location: location.map(convert_geoip_location), - }), - }), - Disconnecting(after_disconnect) => ProtoState::Disconnecting(tunnel_state::Disconnecting { - after_disconnect: match after_disconnect { - ActionAfterDisconnect::Nothing => i32::from(types::AfterDisconnect::Nothing), - ActionAfterDisconnect::Block => i32::from(types::AfterDisconnect::Block), - ActionAfterDisconnect::Reconnect => i32::from(types::AfterDisconnect::Reconnect), - }, - }), - Error(error_state) => ProtoState::Error(tunnel_state::Error { - error_state: Some(types::ErrorState { - cause: match error_state.cause() { - ErrorStateCause::AuthFailed(_) => i32::from(ProtoErrorCause::AuthFailed), - ErrorStateCause::Ipv6Unavailable => i32::from(ProtoErrorCause::Ipv6Unavailable), - ErrorStateCause::SetFirewallPolicyError(_) => { - i32::from(ProtoErrorCause::SetFirewallPolicyError) - } - ErrorStateCause::SetDnsError => i32::from(ProtoErrorCause::SetDnsError), - ErrorStateCause::StartTunnelError => { - i32::from(ProtoErrorCause::StartTunnelError) - } - ErrorStateCause::TunnelParameterError(_) => { - i32::from(ProtoErrorCause::TunnelParameterError) - } - ErrorStateCause::IsOffline => i32::from(ProtoErrorCause::IsOffline), - #[cfg(target_os = "android")] - ErrorStateCause::VpnPermissionDenied => { - i32::from(ProtoErrorCause::VpnPermissionDenied) - } - }, - blocking_error: error_state.block_failure().map(map_firewall_error), - auth_fail_reason: if let ErrorStateCause::AuthFailed(reason) = error_state.cause() { - reason.clone().unwrap_or_default() - } else { - "".to_string() - }, - parameter_error: if let ErrorStateCause::TunnelParameterError(reason) = - error_state.cause() - { - match reason { - ParameterGenerationError::NoMatchingRelay => { - i32::from(ProtoGenerationError::NoMatchingRelay) - } - ParameterGenerationError::NoMatchingBridgeRelay => { - i32::from(ProtoGenerationError::NoMatchingBridgeRelay) - } - ParameterGenerationError::NoWireguardKey => { - i32::from(ProtoGenerationError::NoWireguardKey) - } - ParameterGenerationError::CustomTunnelHostResultionError => { - i32::from(ProtoGenerationError::CustomTunnelHostResolutionError) - } - } - } else { - 0 - }, - policy_error: if let ErrorStateCause::SetFirewallPolicyError(reason) = - error_state.cause() - { - Some(map_firewall_error(reason)) - } else { - None - }, - }), - }), - }; - - types::TunnelState { state: Some(state) } -} - -fn convert_endpoint(endpoint: talpid_types::net::TunnelEndpoint) -> types::TunnelEndpoint { - use talpid_types::net; - - types::TunnelEndpoint { - address: endpoint.endpoint.address.to_string(), - protocol: match endpoint.endpoint.protocol { - TransportProtocol::Tcp => i32::from(types::TransportProtocol::Tcp), - TransportProtocol::Udp => i32::from(types::TransportProtocol::Udp), - }, - tunnel_type: match endpoint.tunnel_type { - net::TunnelType::Wireguard => i32::from(types::TunnelType::Wireguard), - net::TunnelType::OpenVpn => i32::from(types::TunnelType::Openvpn), - }, - proxy: endpoint.proxy.map(|proxy_ep| types::ProxyEndpoint { - address: proxy_ep.endpoint.address.to_string(), - protocol: match proxy_ep.endpoint.protocol { - TransportProtocol::Tcp => i32::from(types::TransportProtocol::Tcp), - TransportProtocol::Udp => i32::from(types::TransportProtocol::Udp), - }, - proxy_type: match proxy_ep.proxy_type { - net::proxy::ProxyType::Shadowsocks => i32::from(types::ProxyType::Shadowsocks), - net::proxy::ProxyType::Custom => i32::from(types::ProxyType::Custom), - }, - }), - } -} - -fn convert_geoip_location(geoip: GeoIpLocation) -> types::GeoIpLocation { - types::GeoIpLocation { - ipv4: geoip.ipv4.map(|ip| ip.to_string()).unwrap_or_default(), - ipv6: geoip.ipv6.map(|ip| ip.to_string()).unwrap_or_default(), - country: geoip.country, - city: geoip.city.unwrap_or_default(), - latitude: geoip.latitude, - longitude: geoip.longitude, - mullvad_exit_ip: geoip.mullvad_exit_ip, - hostname: geoip.hostname.unwrap_or_default(), - bridge_hostname: geoip.bridge_hostname.unwrap_or_default(), - } -} - -fn convert_version_info(version_info: &version::AppVersionInfo) -> types::AppVersionInfo { - types::AppVersionInfo { - supported: version_info.supported, - latest_stable: version_info.latest_stable.clone(), - latest_beta: version_info.latest_beta.clone(), - suggested_upgrade: version_info.suggested_upgrade.clone().unwrap_or_default(), - } -} - -fn convert_proto_location(location: types::RelayLocation) -> Constraint<LocationConstraint> { - if !location.hostname.is_empty() { - Constraint::Only(LocationConstraint::Hostname( - location.country, - location.city, - location.hostname, - )) - } else if !location.city.is_empty() { - Constraint::Only(LocationConstraint::City(location.country, location.city)) - } else if !location.country.is_empty() { - Constraint::Only(LocationConstraint::Country(location.country)) - } else { - Constraint::Any - } -} - -fn convert_proto_transport_protocol(protocol: i32) -> Result<TransportProtocol, Status> { - match types::TransportProtocol::from_i32(protocol) { - Some(types::TransportProtocol::Udp) => Ok(TransportProtocol::Udp), - Some(types::TransportProtocol::Tcp) => Ok(TransportProtocol::Tcp), - None => Err(Status::invalid_argument("invalid transport protocol")), - } -} - pub struct ManagementInterfaceServer { subscriptions: Arc<RwLock<Vec<EventsListenerSender>>>, socket_path: String, @@ -1623,7 +767,9 @@ impl EventListener for ManagementInterfaceEventBroadcaster { /// Sends a new state update to all `new_state` subscribers of the management interface. fn notify_new_state(&self, new_state: TunnelState) { self.notify(types::DaemonEvent { - event: Some(daemon_event::Event::TunnelState(convert_state(new_state))), + event: Some(daemon_event::Event::TunnelState(types::TunnelState::from( + new_state, + ))), }) } @@ -1631,7 +777,9 @@ impl EventListener for ManagementInterfaceEventBroadcaster { fn notify_settings(&self, settings: Settings) { log::debug!("Broadcasting new settings"); self.notify(types::DaemonEvent { - event: Some(daemon_event::Event::Settings(convert_settings(&settings))), + event: Some(daemon_event::Event::Settings(types::Settings::from( + &settings, + ))), }) } @@ -1642,8 +790,10 @@ impl EventListener for ManagementInterfaceEventBroadcaster { countries: Vec::new(), }; new_list.countries.reserve(relay_list.countries.len()); - for country in &relay_list.countries { - new_list.countries.push(convert_relay_list_country(country)); + for country in relay_list.countries.into_iter() { + new_list + .countries + .push(types::RelayListCountry::from(country)); } self.notify(types::DaemonEvent { event: Some(daemon_event::Event::RelayList(new_list)), @@ -1652,17 +802,19 @@ impl EventListener for ManagementInterfaceEventBroadcaster { fn notify_app_version(&self, app_version_info: version::AppVersionInfo) { log::debug!("Broadcasting new app version info"); - let new_info = convert_version_info(&app_version_info); self.notify(types::DaemonEvent { - event: Some(daemon_event::Event::VersionInfo(new_info)), + event: Some(daemon_event::Event::VersionInfo( + types::AppVersionInfo::from(app_version_info), + )), }) } fn notify_key_event(&self, key_event: mullvad_types::wireguard::KeygenEvent) { log::debug!("Broadcasting new wireguard key event"); - let new_event = convert_wireguard_key_event(&key_event); self.notify(types::DaemonEvent { - event: Some(daemon_event::Event::KeyEvent(new_event)), + event: Some(daemon_event::Event::KeyEvent(types::KeygenEvent::from( + key_event, + ))), }) } } diff --git a/mullvad-daemon/src/relays.rs b/mullvad-daemon/src/relays.rs index c029baaa54..54d664b640 100644 --- a/mullvad-daemon/src/relays.rs +++ b/mullvad-daemon/src/relays.rs @@ -218,7 +218,7 @@ impl RelaySelector { pub fn get_tunnel_endpoint( &mut self, relay_constraints: &RelayConstraints, - bridge_state: &BridgeState, + bridge_state: BridgeState, retry_attempt: u32, wg_key_exists: bool, ) -> Result<(Relay, MullvadEndpoint), Error> { @@ -250,12 +250,12 @@ impl RelaySelector { fn preferred_constraints( &self, original_constraints: &RelayConstraints, - bridge_state: &BridgeState, + bridge_state: BridgeState, retry_attempt: u32, wg_key_exists: bool, ) -> RelayConstraints { let (preferred_port, preferred_protocol, preferred_tunnel) = - if *bridge_state != BridgeState::On { + if bridge_state != BridgeState::On { self.preferred_tunnel_constraints( retry_attempt, &original_constraints.location, @@ -277,7 +277,7 @@ impl RelaySelector { Constraint::Any => { if original_constraints.openvpn_constraints.port.is_any() && original_constraints.openvpn_constraints.protocol.is_any() - || *bridge_state == BridgeState::On + || bridge_state == BridgeState::On { relay_constraints.openvpn_constraints = OpenVpnConstraints { port: preferred_port, @@ -297,7 +297,7 @@ impl RelaySelector { Constraint::Only(TunnelType::OpenVpn) => { let openvpn_constraints = &mut relay_constraints.openvpn_constraints; *openvpn_constraints = original_constraints.openvpn_constraints; - if *bridge_state == BridgeState::On && openvpn_constraints.protocol.is_any() { + if bridge_state == BridgeState::On && openvpn_constraints.protocol.is_any() { // FIXME: This is temporary while talpid-core only supports TCP proxies openvpn_constraints.protocol = Constraint::Only(TransportProtocol::Tcp); } else if openvpn_constraints.port.is_any() && openvpn_constraints.protocol.is_any() diff --git a/mullvad-management-interface/Cargo.toml b/mullvad-management-interface/Cargo.toml index cb28f4b9ed..6b4cce66ac 100644 --- a/mullvad-management-interface/Cargo.toml +++ b/mullvad-management-interface/Cargo.toml @@ -9,7 +9,9 @@ publish = false [dependencies] err-derive = "0.3.0" +mullvad-types = { path = "../mullvad-types" } mullvad-paths = { path = "../mullvad-paths" } +talpid-types = { path = "../talpid-types" } tonic = "0.3.1" tower = "0.3" prost = "0.6" diff --git a/mullvad-management-interface/src/lib.rs b/mullvad-management-interface/src/lib.rs index abf4282923..cc4c266af7 100644 --- a/mullvad-management-interface/src/lib.rs +++ b/mullvad-management-interface/src/lib.rs @@ -1,8 +1,4 @@ -pub mod types { - tonic::include_proto!("mullvad_daemon.management_interface"); - - pub use prost_types::{Duration, Timestamp}; -} +pub mod types; use parity_tokio_ipc::Endpoint as IpcEndpoint; #[cfg(unix)] diff --git a/mullvad-management-interface/src/types.rs b/mullvad-management-interface/src/types.rs new file mode 100644 index 0000000000..753149f35e --- /dev/null +++ b/mullvad-management-interface/src/types.rs @@ -0,0 +1,1018 @@ +pub use prost_types::{Duration, Timestamp}; + +use mullvad_types::relay_constraints::Constraint; +use std::convert::TryFrom; + +tonic::include_proto!("mullvad_daemon.management_interface"); + +impl From<mullvad_types::location::GeoIpLocation> for GeoIpLocation { + fn from(geoip: mullvad_types::location::GeoIpLocation) -> GeoIpLocation { + GeoIpLocation { + ipv4: geoip.ipv4.map(|ip| ip.to_string()).unwrap_or_default(), + ipv6: geoip.ipv6.map(|ip| ip.to_string()).unwrap_or_default(), + country: geoip.country, + city: geoip.city.unwrap_or_default(), + latitude: geoip.latitude, + longitude: geoip.longitude, + mullvad_exit_ip: geoip.mullvad_exit_ip, + hostname: geoip.hostname.unwrap_or_default(), + bridge_hostname: geoip.bridge_hostname.unwrap_or_default(), + } + } +} + +impl From<talpid_types::net::TunnelEndpoint> for TunnelEndpoint { + fn from(endpoint: talpid_types::net::TunnelEndpoint) -> Self { + use talpid_types::net; + + TunnelEndpoint { + address: endpoint.endpoint.address.to_string(), + protocol: i32::from(TransportProtocol::from(endpoint.endpoint.protocol)), + tunnel_type: match endpoint.tunnel_type { + net::TunnelType::Wireguard => i32::from(TunnelType::Wireguard), + net::TunnelType::OpenVpn => i32::from(TunnelType::Openvpn), + }, + proxy: endpoint.proxy.map(|proxy_ep| ProxyEndpoint { + address: proxy_ep.endpoint.address.to_string(), + protocol: i32::from(TransportProtocol::from(proxy_ep.endpoint.protocol)), + proxy_type: match proxy_ep.proxy_type { + net::proxy::ProxyType::Shadowsocks => i32::from(ProxyType::Shadowsocks), + net::proxy::ProxyType::Custom => i32::from(ProxyType::Custom), + }, + }), + } + } +} + +impl From<mullvad_types::states::TunnelState> for TunnelState { + fn from(state: mullvad_types::states::TunnelState) -> Self { + use error_state::{ + firewall_policy_error::ErrorType as PolicyErrorType, Cause, FirewallPolicyError, + GenerationError, + }; + use mullvad_types::states::TunnelState as MullvadTunnelState; + + use talpid_types::tunnel as talpid_tunnel; + + let map_firewall_error = + |firewall_error: &talpid_tunnel::FirewallPolicyError| match firewall_error { + talpid_tunnel::FirewallPolicyError::Generic => FirewallPolicyError { + r#type: i32::from(PolicyErrorType::Generic), + ..Default::default() + }, + #[cfg(windows)] + talpid_tunnel::FirewallPolicyError::Locked(blocking_app) => { + let (lock_pid, lock_name) = match blocking_app { + Some(app) => (app.pid, app.name.clone()), + None => (0, "".to_string()), + }; + + FirewallPolicyError { + r#type: i32::from(PolicyErrorType::Locked), + lock_pid, + lock_name, + } + } + }; + + let state = match state { + MullvadTunnelState::Disconnected => { + tunnel_state::State::Disconnected(tunnel_state::Disconnected {}) + } + MullvadTunnelState::Connecting { endpoint, location } => { + tunnel_state::State::Connecting(tunnel_state::Connecting { + relay_info: Some(TunnelStateRelayInfo { + tunnel_endpoint: Some(TunnelEndpoint::from(endpoint)), + location: location.map(GeoIpLocation::from), + }), + }) + } + MullvadTunnelState::Connected { endpoint, location } => { + tunnel_state::State::Connected(tunnel_state::Connected { + relay_info: Some(TunnelStateRelayInfo { + tunnel_endpoint: Some(TunnelEndpoint::from(endpoint)), + location: location.map(GeoIpLocation::from), + }), + }) + } + MullvadTunnelState::Disconnecting(after_disconnect) => { + tunnel_state::State::Disconnecting(tunnel_state::Disconnecting { + after_disconnect: match after_disconnect { + talpid_tunnel::ActionAfterDisconnect::Nothing => { + i32::from(AfterDisconnect::Nothing) + } + talpid_tunnel::ActionAfterDisconnect::Block => { + i32::from(AfterDisconnect::Block) + } + talpid_tunnel::ActionAfterDisconnect::Reconnect => { + i32::from(AfterDisconnect::Reconnect) + } + }, + }) + } + MullvadTunnelState::Error(error_state) => { + tunnel_state::State::Error(tunnel_state::Error { + error_state: Some(ErrorState { + cause: match error_state.cause() { + talpid_tunnel::ErrorStateCause::AuthFailed(_) => { + i32::from(Cause::AuthFailed) + } + talpid_tunnel::ErrorStateCause::Ipv6Unavailable => { + i32::from(Cause::Ipv6Unavailable) + } + talpid_tunnel::ErrorStateCause::SetFirewallPolicyError(_) => { + i32::from(Cause::SetFirewallPolicyError) + } + talpid_tunnel::ErrorStateCause::SetDnsError => { + i32::from(Cause::SetDnsError) + } + talpid_tunnel::ErrorStateCause::StartTunnelError => { + i32::from(Cause::StartTunnelError) + } + talpid_tunnel::ErrorStateCause::TunnelParameterError(_) => { + i32::from(Cause::TunnelParameterError) + } + talpid_tunnel::ErrorStateCause::IsOffline => { + i32::from(Cause::IsOffline) + } + #[cfg(target_os = "android")] + talpid_tunnel::ErrorStateCause::VpnPermissionDenied => { + i32::from(Cause::VpnPermissionDenied) + } + }, + blocking_error: error_state.block_failure().map(map_firewall_error), + auth_fail_reason: if let talpid_tunnel::ErrorStateCause::AuthFailed( + reason, + ) = error_state.cause() + { + reason.clone().unwrap_or_default() + } else { + "".to_string() + }, + parameter_error: + if let talpid_tunnel::ErrorStateCause::TunnelParameterError(reason) = + error_state.cause() + { + match reason { + talpid_tunnel::ParameterGenerationError::NoMatchingRelay => { + i32::from(GenerationError::NoMatchingRelay) + } + talpid_tunnel::ParameterGenerationError::NoMatchingBridgeRelay => { + i32::from(GenerationError::NoMatchingBridgeRelay) + } + talpid_tunnel::ParameterGenerationError::NoWireguardKey => { + i32::from(GenerationError::NoWireguardKey) + } + talpid_tunnel::ParameterGenerationError::CustomTunnelHostResultionError => { + i32::from(GenerationError::CustomTunnelHostResolutionError) + } + } + } else { + 0 + }, + policy_error: + if let talpid_tunnel::ErrorStateCause::SetFirewallPolicyError(reason) = + error_state.cause() + { + Some(map_firewall_error(reason)) + } else { + None + }, + }), + }) + } + }; + + TunnelState { state: Some(state) } + } +} + +impl From<mullvad_types::wireguard::KeygenEvent> for KeygenEvent { + fn from(event: mullvad_types::wireguard::KeygenEvent) -> Self { + use keygen_event::KeygenEvent as Event; + use mullvad_types::wireguard::KeygenEvent as MullvadEvent; + + KeygenEvent { + event: match event { + MullvadEvent::NewKey(_) => i32::from(Event::NewKey), + MullvadEvent::TooManyKeys => i32::from(Event::TooManyKeys), + MullvadEvent::GenerationFailure => i32::from(Event::GenerationFailure), + }, + new_key: if let MullvadEvent::NewKey(key) = event { + Some(PublicKey::from(key)) + } else { + None + }, + } + } +} + +impl From<mullvad_types::wireguard::PublicKey> for PublicKey { + fn from(public_key: mullvad_types::wireguard::PublicKey) -> Self { + PublicKey { + key: public_key.key.as_bytes().to_vec(), + created: Some(Timestamp { + seconds: public_key.created.timestamp(), + nanos: 0, + }), + } + } +} + +impl From<mullvad_types::version::AppVersionInfo> for AppVersionInfo { + fn from(version_info: mullvad_types::version::AppVersionInfo) -> Self { + Self { + supported: version_info.supported, + latest_stable: version_info.latest_stable, + latest_beta: version_info.latest_beta, + suggested_upgrade: version_info.suggested_upgrade.unwrap_or_default(), + } + } +} + +impl From<mullvad_types::ConnectionConfig> for ConnectionConfig { + fn from(config: mullvad_types::ConnectionConfig) -> Self { + Self { + config: Some(match config { + mullvad_types::ConnectionConfig::OpenVpn(config) => { + connection_config::Config::Openvpn(connection_config::OpenvpnConfig { + address: config.endpoint.address.to_string(), + protocol: i32::from(TransportProtocol::from(config.endpoint.protocol)), + username: config.username, + password: config.password, + }) + } + mullvad_types::ConnectionConfig::Wireguard(config) => { + connection_config::Config::Wireguard(connection_config::WireguardConfig { + tunnel: Some(connection_config::wireguard_config::TunnelConfig { + private_key: config.tunnel.private_key.to_bytes().to_vec(), + addresses: config + .tunnel + .addresses + .iter() + .map(|address| address.to_string()) + .collect(), + }), + peer: Some(connection_config::wireguard_config::PeerConfig { + public_key: config.peer.public_key.as_bytes().to_vec(), + allowed_ips: config + .peer + .allowed_ips + .iter() + .map(|address| address.to_string()) + .collect(), + endpoint: config.peer.endpoint.to_string(), + protocol: i32::from(TransportProtocol::from(config.peer.protocol)), + }), + ipv4_gateway: config.ipv4_gateway.to_string(), + ipv6_gateway: config + .ipv6_gateway + .as_ref() + .map(|address| address.to_string()) + .unwrap_or_default(), + }) + } + }), + } + } +} + +impl From<talpid_types::net::TransportProtocol> for TransportProtocol { + fn from(protocol: talpid_types::net::TransportProtocol) -> Self { + match protocol { + talpid_types::net::TransportProtocol::Udp => TransportProtocol::Udp, + talpid_types::net::TransportProtocol::Tcp => TransportProtocol::Tcp, + } + } +} + +impl From<TransportProtocol> for TransportProtocolConstraint { + fn from(protocol: TransportProtocol) -> Self { + Self { + protocol: i32::from(protocol), + } + } +} + +impl From<talpid_types::net::IpVersion> for IpVersion { + fn from(version: talpid_types::net::IpVersion) -> Self { + match version { + talpid_types::net::IpVersion::V4 => Self::V4, + talpid_types::net::IpVersion::V6 => Self::V6, + } + } +} + +impl From<IpVersion> for IpVersionConstraint { + fn from(version: IpVersion) -> Self { + Self { + protocol: i32::from(version), + } + } +} + +impl From<mullvad_types::relay_constraints::LocationConstraint> for RelayLocation { + fn from(location: mullvad_types::relay_constraints::LocationConstraint) -> Self { + use mullvad_types::relay_constraints::LocationConstraint; + + match location { + LocationConstraint::Country(country) => Self { + country, + ..Default::default() + }, + LocationConstraint::City(country, city) => Self { + country, + city, + ..Default::default() + }, + LocationConstraint::Hostname(country, city, hostname) => Self { + country, + city, + hostname, + }, + } + } +} + +impl From<&mullvad_types::settings::Settings> for Settings { + fn from(settings: &mullvad_types::settings::Settings) -> Self { + Self { + account_token: settings.get_account_token().unwrap_or_default(), + relay_settings: Some(RelaySettings::from(settings.get_relay_settings())), + bridge_settings: Some(BridgeSettings::from(settings.bridge_settings.clone())), + bridge_state: Some(BridgeState::from(settings.get_bridge_state())), + allow_lan: settings.allow_lan, + block_when_disconnected: settings.block_when_disconnected, + auto_connect: settings.auto_connect, + tunnel_options: Some(TunnelOptions::from(&settings.tunnel_options)), + show_beta_releases: settings.show_beta_releases, + } + } +} + +impl From<mullvad_types::relay_constraints::BridgeState> for BridgeState { + fn from(state: mullvad_types::relay_constraints::BridgeState) -> Self { + use mullvad_types::relay_constraints::BridgeState; + Self { + state: i32::from(match state { + BridgeState::Auto => bridge_state::State::Auto, + BridgeState::On => bridge_state::State::On, + BridgeState::Off => bridge_state::State::Off, + }), + } + } +} + +impl From<mullvad_types::relay_constraints::BridgeSettings> for BridgeSettings { + fn from(settings: mullvad_types::relay_constraints::BridgeSettings) -> Self { + use mullvad_types::relay_constraints::BridgeSettings as MullvadBridgeSettings; + use talpid_types::net as talpid_net; + + let settings = match settings { + MullvadBridgeSettings::Normal(constraints) => { + bridge_settings::Type::Normal(bridge_settings::BridgeConstraints { + location: constraints + .location + .clone() + .option() + .map(RelayLocation::from), + providers: convert_providers_constraint(&constraints.providers), + }) + } + MullvadBridgeSettings::Custom(proxy_settings) => match proxy_settings { + talpid_net::openvpn::ProxySettings::Local(proxy_settings) => { + bridge_settings::Type::Local(bridge_settings::LocalProxySettings { + port: u32::from(proxy_settings.port), + peer: proxy_settings.peer.to_string(), + }) + } + talpid_net::openvpn::ProxySettings::Remote(proxy_settings) => { + bridge_settings::Type::Remote(bridge_settings::RemoteProxySettings { + address: proxy_settings.address.to_string(), + auth: proxy_settings.auth.as_ref().map(|auth| { + bridge_settings::RemoteProxyAuth { + username: auth.username.clone(), + password: auth.password.clone(), + } + }), + }) + } + talpid_net::openvpn::ProxySettings::Shadowsocks(proxy_settings) => { + bridge_settings::Type::Shadowsocks(bridge_settings::ShadowsocksProxySettings { + peer: proxy_settings.peer.to_string(), + password: proxy_settings.password.clone(), + cipher: proxy_settings.cipher.clone(), + }) + } + }, + }; + + BridgeSettings { + r#type: Some(settings), + } + } +} + +impl From<mullvad_types::relay_constraints::RelaySettings> for RelaySettings { + fn from(settings: mullvad_types::relay_constraints::RelaySettings) -> Self { + use mullvad_types::relay_constraints::RelaySettings as MullvadRelaySettings; + use talpid_types::net as talpid_net; + + let endpoint = match settings { + MullvadRelaySettings::CustomTunnelEndpoint(endpoint) => { + relay_settings::Endpoint::Custom(CustomRelaySettings { + host: endpoint.host, + config: Some(ConnectionConfig::from(endpoint.config)), + }) + } + MullvadRelaySettings::Normal(constraints) => { + relay_settings::Endpoint::Normal(NormalRelaySettings { + location: constraints.location.option().map(RelayLocation::from), + providers: convert_providers_constraint(&constraints.providers), + tunnel_type: match constraints.tunnel_protocol { + Constraint::Any => None, + Constraint::Only(talpid_net::TunnelType::Wireguard) => { + Some(TunnelType::Wireguard) + } + Constraint::Only(talpid_net::TunnelType::OpenVpn) => { + Some(TunnelType::Openvpn) + } + } + .map(|tunnel_type| TunnelTypeConstraint { + tunnel_type: i32::from(tunnel_type), + }), + + wireguard_constraints: Some(WireguardConstraints { + port: u32::from(constraints.wireguard_constraints.port.unwrap_or(0)), + ip_version: constraints + .wireguard_constraints + .ip_version + .option() + .map(IpVersion::from) + .map(IpVersionConstraint::from), + }), + + openvpn_constraints: Some(OpenvpnConstraints { + port: u32::from(constraints.openvpn_constraints.port.unwrap_or(0)), + protocol: constraints + .openvpn_constraints + .protocol + .as_ref() + .option() + .map(|protocol| TransportProtocol::from(*protocol)) + .map(TransportProtocolConstraint::from), + }), + }) + } + }; + + Self { + endpoint: Some(endpoint), + } + } +} + +impl From<&mullvad_types::settings::TunnelOptions> for TunnelOptions { + fn from(options: &mullvad_types::settings::TunnelOptions) -> Self { + Self { + openvpn: Some(tunnel_options::OpenvpnOptions { + mssfix: u32::from(options.openvpn.mssfix.unwrap_or_default()), + }), + wireguard: Some(tunnel_options::WireguardOptions { + mtu: u32::from(options.wireguard.options.mtu.unwrap_or_default()), + rotation_interval: options + .wireguard + .rotation_interval + .map(|ivl| Duration::from(std::time::Duration::from(ivl))), + }), + generic: Some(tunnel_options::GenericOptions { + enable_ipv6: options.generic.enable_ipv6, + }), + #[cfg(not(target_os = "android"))] + dns_options: Some(DnsOptions { + custom: options.dns_options.custom, + addresses: options + .dns_options + .addresses + .iter() + .map(|addr| addr.to_string()) + .collect(), + }), + #[cfg(target_os = "android")] + dns_options: None, + } + } +} + +impl From<mullvad_types::relay_list::RelayListCountry> for RelayListCountry { + fn from(country: mullvad_types::relay_list::RelayListCountry) -> Self { + let mut proto_country = RelayListCountry { + name: country.name, + code: country.code, + cities: Vec::with_capacity(country.cities.len()), + }; + + for city in country.cities.into_iter() { + proto_country.cities.push(RelayListCity { + name: city.name, + code: city.code, + latitude: city.latitude, + longitude: city.longitude, + relays: city.relays.into_iter().map(Relay::from).collect(), + }); + } + + proto_country + } +} + +impl From<mullvad_types::relay_list::Relay> for Relay { + fn from(relay: mullvad_types::relay_list::Relay) -> Self { + Self { + hostname: relay.hostname, + ipv4_addr_in: relay.ipv4_addr_in.to_string(), + ipv6_addr_in: relay + .ipv6_addr_in + .map(|addr| addr.to_string()) + .unwrap_or_default(), + include_in_country: relay.include_in_country, + active: relay.active, + 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(), + }), + location: relay.location.map(|location| Location { + 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 { + TransportProtocol::Udp => talpid_types::net::TransportProtocol::Udp, + TransportProtocol::Tcp => talpid_types::net::TransportProtocol::Tcp, + } + } +} + +pub enum FromProtobufTypeError { + InvalidArgument(&'static str), +} + +impl TryFrom<RelaySettingsUpdate> for mullvad_types::relay_constraints::RelaySettingsUpdate { + type Error = FromProtobufTypeError; + + fn try_from( + settings: RelaySettingsUpdate, + ) -> Result<mullvad_types::relay_constraints::RelaySettingsUpdate, Self::Error> { + use mullvad_types::CustomTunnelEndpoint; + use talpid_types::net::{self, openvpn, wireguard}; + + use mullvad_types::relay_constraints as mullvad_constraints; + + let update_value = + settings + .r#type + .clone() + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing relay settings", + ))?; + + match update_value { + relay_settings_update::Type::Custom(settings) => { + let config = settings + .config + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing relay settings", + ))?; + let config = config.config.ok_or(FromProtobufTypeError::InvalidArgument( + "missing relay settings", + ))?; + let config = match config { + connection_config::Config::Openvpn(config) => { + let address = match config.address.parse() { + Ok(address) => address, + Err(_) => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid address", + )) + } + }; + + mullvad_types::ConnectionConfig::OpenVpn(openvpn::ConnectionConfig { + endpoint: net::Endpoint { + address, + protocol: TransportProtocol::from_i32(config.protocol) + .ok_or(FromProtobufTypeError::InvalidArgument( + "invalid transport protocol", + ))? + .into(), + }, + username: config.username, + password: config.password, + }) + } + connection_config::Config::Wireguard(config) => { + let tunnel = config.tunnel.ok_or( + FromProtobufTypeError::InvalidArgument("missing tunnel config"), + )?; + + // Copy the private key to an array + if tunnel.private_key.len() != 32 { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid private key", + )); + } + + let mut private_key = [0; 32]; + let buffer = &tunnel.private_key[..private_key.len()]; + private_key.copy_from_slice(buffer); + + let peer = config.peer.ok_or(FromProtobufTypeError::InvalidArgument( + "missing peer config", + ))?; + + // Copy the public key to an array + if peer.public_key.len() != 32 { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid public key", + )); + } + + let mut public_key = [0; 32]; + let buffer = &peer.public_key[..public_key.len()]; + public_key.copy_from_slice(buffer); + + let ipv4_gateway = match config.ipv4_gateway.parse() { + Ok(address) => address, + Err(_) => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid IPv4 gateway", + )) + } + }; + let ipv6_gateway = if !config.ipv6_gateway.is_empty() { + let address = match config.ipv6_gateway.parse() { + Ok(address) => address, + Err(_) => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid IPv6 gateway", + )) + } + }; + Some(address) + } else { + None + }; + + let endpoint = match peer.endpoint.parse() { + Ok(address) => address, + Err(_) => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid peer address", + )) + } + }; + + let mut tunnel_addresses = Vec::new(); + for address in tunnel.addresses { + let address = address.parse().map_err(|_| { + FromProtobufTypeError::InvalidArgument("invalid address") + })?; + tunnel_addresses.push(address); + } + + let mut allowed_ips = Vec::new(); + for address in peer.allowed_ips { + let address = address.parse().map_err(|_| { + FromProtobufTypeError::InvalidArgument("invalid address") + })?; + allowed_ips.push(address); + } + + mullvad_types::ConnectionConfig::Wireguard(wireguard::ConnectionConfig { + tunnel: wireguard::TunnelConfig { + private_key: wireguard::PrivateKey::from(private_key), + addresses: tunnel_addresses, + }, + peer: wireguard::PeerConfig { + public_key: wireguard::PublicKey::from(public_key), + allowed_ips, + endpoint, + protocol: TransportProtocol::from_i32(peer.protocol) + .ok_or(FromProtobufTypeError::InvalidArgument( + "invalid transport protocol", + ))? + .into(), + }, + ipv4_gateway, + ipv6_gateway, + }) + } + }; + + Ok( + mullvad_constraints::RelaySettingsUpdate::CustomTunnelEndpoint( + CustomTunnelEndpoint { + host: settings.host, + config, + }, + ), + ) + } + + relay_settings_update::Type::Normal(settings) => { + // If `location` isn't provided, no changes are made. + // If `location` is provided, but is an empty vector, + // then the constraint is set to `Constraint::Any`. + let location = settings + .location + .map(Constraint::<mullvad_types::relay_constraints::LocationConstraint>::from); + + let tunnel_protocol = if let Some(update) = settings.tunnel_type { + match update.tunnel_type { + Some(constraint) => match TunnelType::from_i32(constraint.tunnel_type) { + Some(TunnelType::Openvpn) => { + Some(Constraint::Only(net::TunnelType::OpenVpn)) + } + Some(TunnelType::Wireguard) => { + Some(Constraint::Only(net::TunnelType::Wireguard)) + } + None => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid tunnel protocol", + )) + } + }, + None => Some(Constraint::Any), + } + } else { + None + }; + + let transport_protocol = if let Some(ref constraints) = settings.openvpn_constraints + { + match &constraints.protocol { + Some(constraint) => Some( + TransportProtocol::from_i32(constraint.protocol) + .ok_or(FromProtobufTypeError::InvalidArgument( + "invalid transport protocol", + ))? + .into(), + ), + None => None, + } + } else { + None + }; + + let providers = if let Some(ref provider_update) = settings.providers { + if !provider_update.providers.is_empty() { + Some(Constraint::Only( + mullvad_constraints::Providers::new( + provider_update.providers.clone().into_iter(), + ) + .map_err(|_| { + FromProtobufTypeError::InvalidArgument( + "must specify at least one provider", + ) + })?, + )) + } else { + Some(Constraint::Any) + } + } else { + None + }; + let ip_version = if let Some(ref constraints) = settings.wireguard_constraints { + match &constraints.ip_version { + Some(constraint) => match IpVersion::from_i32(constraint.protocol) { + Some(IpVersion::V4) => Some(net::IpVersion::V4), + Some(IpVersion::V6) => Some(net::IpVersion::V6), + None => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid ip protocol version", + )) + } + }, + None => None, + } + } else { + None + }; + + Ok(mullvad_constraints::RelaySettingsUpdate::Normal( + mullvad_constraints::RelayConstraintsUpdate { + location, + providers, + tunnel_protocol, + wireguard_constraints: settings.wireguard_constraints.map(|constraints| { + mullvad_constraints::WireguardConstraints { + port: if constraints.port != 0 { + Constraint::Only(constraints.port as u16) + } else { + Constraint::Any + }, + ip_version: Constraint::from(ip_version), + } + }), + openvpn_constraints: settings.openvpn_constraints.map(|constraints| { + mullvad_constraints::OpenVpnConstraints { + port: if constraints.port != 0 { + Constraint::Only(constraints.port as u16) + } else { + Constraint::Any + }, + protocol: Constraint::from(transport_protocol), + } + }), + }, + )) + } + } + } +} + +impl From<RelayLocation> for Constraint<mullvad_types::relay_constraints::LocationConstraint> { + fn from(location: RelayLocation) -> Self { + use mullvad_types::relay_constraints::LocationConstraint; + + if !location.hostname.is_empty() { + Constraint::Only(LocationConstraint::Hostname( + location.country, + location.city, + location.hostname, + )) + } else if !location.city.is_empty() { + Constraint::Only(LocationConstraint::City(location.country, location.city)) + } else if !location.country.is_empty() { + Constraint::Only(LocationConstraint::Country(location.country)) + } else { + Constraint::Any + } + } +} + +impl TryFrom<BridgeSettings> for mullvad_types::relay_constraints::BridgeSettings { + type Error = FromProtobufTypeError; + + fn try_from(settings: BridgeSettings) -> Result<Self, Self::Error> { + use mullvad_types::relay_constraints as mullvad_constraints; + use talpid_types::net as talpid_net; + + match settings + .r#type + .ok_or(FromProtobufTypeError::InvalidArgument( + "no settings provided", + ))? { + bridge_settings::Type::Normal(constraints) => { + let location = match constraints.location { + None => Constraint::Any, + Some(location) => { + Constraint::<mullvad_constraints::LocationConstraint>::from(location) + } + }; + let providers = if constraints.providers.is_empty() { + Constraint::Any + } else { + Constraint::Only( + mullvad_constraints::Providers::new( + constraints.providers.clone().into_iter(), + ) + .map_err(|_| { + FromProtobufTypeError::InvalidArgument( + "must specify at least one provider", + ) + })?, + ) + }; + + Ok(mullvad_constraints::BridgeSettings::Normal( + mullvad_constraints::BridgeConstraints { + location, + providers, + }, + )) + } + bridge_settings::Type::Local(proxy_settings) => { + let peer = proxy_settings.peer.parse().map_err(|_| { + FromProtobufTypeError::InvalidArgument("failed to parse peer address") + })?; + let proxy_settings = talpid_net::openvpn::ProxySettings::Local( + talpid_net::openvpn::LocalProxySettings { + port: proxy_settings.port as u16, + peer, + }, + ); + Ok(mullvad_constraints::BridgeSettings::Custom(proxy_settings)) + } + bridge_settings::Type::Remote(proxy_settings) => { + let address = proxy_settings.address.parse().map_err(|_| { + FromProtobufTypeError::InvalidArgument("failed to parse IP address") + })?; + let auth = proxy_settings + .auth + .map(|auth| talpid_net::openvpn::ProxyAuth { + username: auth.username, + password: auth.password, + }); + let proxy_settings = talpid_net::openvpn::ProxySettings::Remote( + talpid_net::openvpn::RemoteProxySettings { address, auth }, + ); + Ok(mullvad_constraints::BridgeSettings::Custom(proxy_settings)) + } + bridge_settings::Type::Shadowsocks(proxy_settings) => { + let peer = proxy_settings.peer.parse().map_err(|_| { + FromProtobufTypeError::InvalidArgument("failed to parse peer address") + })?; + let proxy_settings = talpid_net::openvpn::ProxySettings::Shadowsocks( + talpid_net::openvpn::ShadowsocksProxySettings { + peer, + password: proxy_settings.password, + cipher: proxy_settings.cipher, + }, + ); + Ok(mullvad_constraints::BridgeSettings::Custom(proxy_settings)) + } + } + } +} + +impl TryFrom<BridgeState> for mullvad_types::relay_constraints::BridgeState { + type Error = FromProtobufTypeError; + + fn try_from(state: BridgeState) -> Result<Self, Self::Error> { + match bridge_state::State::from_i32(state.state) { + Some(bridge_state::State::Auto) => { + Ok(mullvad_types::relay_constraints::BridgeState::Auto) + } + Some(bridge_state::State::On) => Ok(mullvad_types::relay_constraints::BridgeState::On), + Some(bridge_state::State::Off) => { + Ok(mullvad_types::relay_constraints::BridgeState::Off) + } + None => Err(FromProtobufTypeError::InvalidArgument( + "invalid bridge state", + )), + } + } +} + +fn convert_providers_constraint( + providers: &Constraint<mullvad_types::relay_constraints::Providers>, +) -> Vec<String> { + match providers.as_ref() { + Constraint::Any => vec![], + Constraint::Only(providers) => Vec::from(providers.clone()), + } +} diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs index 908b0d3215..d32608124d 100644 --- a/mullvad-types/src/relay_constraints.rs +++ b/mullvad-types/src/relay_constraints.rs @@ -498,7 +498,7 @@ impl fmt::Display for BridgeConstraints { } /// Setting indicating whether to connect to a bridge server, or to handle it automatically. -#[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] +#[derive(Debug, Clone, Copy, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] pub enum BridgeState { Auto, diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs index 9ea32fdb3b..1cd900edfc 100644 --- a/mullvad-types/src/settings/mod.rs +++ b/mullvad-types/src/settings/mod.rs @@ -143,8 +143,8 @@ impl Settings { } } - pub fn get_bridge_state(&self) -> &BridgeState { - &self.bridge_state + pub fn get_bridge_state(&self) -> BridgeState { + self.bridge_state } pub fn set_bridge_state(&mut self, bridge_state: BridgeState) -> bool { |
