diff options
| author | David Lönnhager <david.l@mullvad.net> | 2022-10-12 14:36:48 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2022-10-17 10:47:38 +0200 |
| commit | f8f4ee29ec02db7b6755158a2915b140d43f47ef (patch) | |
| tree | 92207ccae36925f655c2cdb9857892df2251111f | |
| parent | ca2e6e32c7d563597f7793259381231a82f4a80f (diff) | |
| download | mullvadvpn-f8f4ee29ec02db7b6755158a2915b140d43f47ef.tar.xz mullvadvpn-f8f4ee29ec02db7b6755158a2915b140d43f47ef.zip | |
Split gRPC conversions into submodules analogous to modules in the types crates
12 files changed, 2022 insertions, 1979 deletions
diff --git a/mullvad-cli/src/cmds/bridge.rs b/mullvad-cli/src/cmds/bridge.rs index a8a3e2de7c..9d1c5172fa 100644 --- a/mullvad-cli/src/cmds/bridge.rs +++ b/mullvad-cli/src/cmds/bridge.rs @@ -277,20 +277,25 @@ impl Bridge { } if let Some(new_providers) = providers { constraints.providers = - types::try_providers_constraint_from_proto(&new_providers).unwrap(); + types::relay_constraints::try_providers_constraint_from_proto( + &new_providers, + ) + .unwrap(); } if let Some(new_ownership) = ownership { - constraints.ownership = types::ownership_constraint_from_proto(new_ownership); + constraints.ownership = + types::relay_constraints::ownership_constraint_from_proto(new_ownership); } constraints } _ => { let location = Constraint::<LocationConstraint>::from(location.unwrap_or_default()); - let providers = - types::try_providers_constraint_from_proto(&providers.unwrap_or_default()) - .unwrap(); + let providers = types::relay_constraints::try_providers_constraint_from_proto( + &providers.unwrap_or_default(), + ) + .unwrap(); let ownership = ownership - .map(types::ownership_constraint_from_proto) + .map(types::relay_constraints::ownership_constraint_from_proto) .unwrap_or_default(); BridgeConstraints { diff --git a/mullvad-management-interface/src/types/conversions/custom_tunnel.rs b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs new file mode 100644 index 0000000000..950f8f920c --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/custom_tunnel.rs @@ -0,0 +1,152 @@ +use crate::types::{ + conversions::{bytes_to_privkey, bytes_to_pubkey, option_from_proto_string}, + proto, FromProtobufTypeError, +}; +use talpid_types::net::wireguard; + +impl TryFrom<proto::ConnectionConfig> for mullvad_types::ConnectionConfig { + type Error = FromProtobufTypeError; + + fn try_from( + config: proto::ConnectionConfig, + ) -> Result<mullvad_types::ConnectionConfig, Self::Error> { + use talpid_types::net::{self, openvpn}; + + let config = config.config.ok_or(FromProtobufTypeError::InvalidArgument( + "missing connection config", + ))?; + match config { + proto::connection_config::Config::Openvpn(config) => { + let address = match config.address.parse() { + Ok(address) => address, + Err(_) => { + return Err(FromProtobufTypeError::InvalidArgument("invalid address")) + } + }; + + Ok(mullvad_types::ConnectionConfig::OpenVpn( + openvpn::ConnectionConfig { + endpoint: net::Endpoint { + address, + protocol: super::net::try_transport_protocol_from_i32(config.protocol)?, + }, + username: config.username, + password: config.password, + }, + )) + } + proto::connection_config::Config::Wireguard(config) => { + let tunnel = config.tunnel.ok_or(FromProtobufTypeError::InvalidArgument( + "missing tunnel config", + ))?; + + let private_key = bytes_to_privkey(&tunnel.private_key)?; + + let peer = config.peer.ok_or(FromProtobufTypeError::InvalidArgument( + "missing peer config", + ))?; + + let public_key = bytes_to_pubkey(&peer.public_key)?; + + let ipv4_gateway = config.ipv4_gateway.parse().map_err(|_err| { + FromProtobufTypeError::InvalidArgument("invalid IPv4 gateway") + })?; + let ipv6_gateway = option_from_proto_string(config.ipv6_gateway) + .map(|addr| { + addr.parse().map_err(|_err| { + FromProtobufTypeError::InvalidArgument("invalid IPv6 gateway") + }) + }) + .transpose()?; + + let endpoint = peer.endpoint.parse().map_err(|_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); + } + + Ok(mullvad_types::ConnectionConfig::Wireguard( + wireguard::ConnectionConfig { + tunnel: wireguard::TunnelConfig { + private_key, + addresses: tunnel_addresses, + }, + peer: wireguard::PeerConfig { + public_key, + allowed_ips, + endpoint, + psk: None, + }, + exit_peer: None, + ipv4_gateway, + ipv6_gateway, + }, + )) + } + } + } +} + +impl From<mullvad_types::ConnectionConfig> for proto::ConnectionConfig { + fn from(config: mullvad_types::ConnectionConfig) -> Self { + use proto::connection_config; + + 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(proto::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(), + }), + ipv4_gateway: config.ipv4_gateway.to_string(), + ipv6_gateway: config + .ipv6_gateway + .as_ref() + .map(|address| address.to_string()) + .unwrap_or_default(), + }) + } + }), + } + } +} diff --git a/mullvad-management-interface/src/types/conversions/device.rs b/mullvad-management-interface/src/types/conversions/device.rs new file mode 100644 index 0000000000..625af6c9c1 --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/device.rs @@ -0,0 +1,139 @@ +use crate::types::{conversions::bytes_to_pubkey, proto, FromProtobufTypeError}; +use prost_types::Timestamp; + +impl TryFrom<proto::Device> for mullvad_types::device::Device { + type Error = FromProtobufTypeError; + + fn try_from(device: proto::Device) -> Result<Self, Self::Error> { + Ok(mullvad_types::device::Device { + id: device.id, + name: device.name, + pubkey: bytes_to_pubkey(&device.pubkey)?, + ports: device + .ports + .into_iter() + .map(mullvad_types::device::DevicePort::from) + .collect(), + hijack_dns: device.hijack_dns, + created: chrono::DateTime::from_utc( + chrono::NaiveDateTime::from_timestamp( + device + .created + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing 'created' field", + ))? + .seconds, + 0, + ), + chrono::Utc, + ), + }) + } +} + +impl From<mullvad_types::device::Device> for proto::Device { + fn from(device: mullvad_types::device::Device) -> Self { + proto::Device { + id: device.id, + name: device.name, + pubkey: device.pubkey.as_bytes().to_vec(), + ports: device + .ports + .into_iter() + .map(proto::DevicePort::from) + .collect(), + hijack_dns: device.hijack_dns, + created: Some(Timestamp { + seconds: device.created.timestamp(), + nanos: 0, + }), + } + } +} + +impl From<mullvad_types::device::DevicePort> for proto::DevicePort { + fn from(port: mullvad_types::device::DevicePort) -> Self { + proto::DevicePort { id: port.id } + } +} + +impl From<mullvad_types::device::DeviceState> for proto::DeviceState { + fn from(state: mullvad_types::device::DeviceState) -> Self { + proto::DeviceState { + state: proto::device_state::State::from(&state) as i32, + device: state.into_device().map(|device| proto::AccountAndDevice { + account_token: device.account_token, + device: Some(proto::Device::from(device.device)), + }), + } + } +} + +impl From<&mullvad_types::device::DeviceState> for proto::device_state::State { + fn from(state: &mullvad_types::device::DeviceState) -> Self { + use mullvad_types::device::DeviceState as MullvadState; + match state { + MullvadState::LoggedIn(_) => proto::device_state::State::LoggedIn, + MullvadState::LoggedOut => proto::device_state::State::LoggedOut, + MullvadState::Revoked => proto::device_state::State::Revoked, + } + } +} + +impl From<mullvad_types::device::DeviceEvent> for proto::DeviceEvent { + fn from(event: mullvad_types::device::DeviceEvent) -> Self { + proto::DeviceEvent { + cause: proto::device_event::Cause::from(event.cause) as i32, + new_state: Some(proto::DeviceState::from(event.new_state)), + } + } +} + +impl From<mullvad_types::device::DeviceEventCause> for proto::device_event::Cause { + fn from(cause: mullvad_types::device::DeviceEventCause) -> Self { + use mullvad_types::device::DeviceEventCause as MullvadEvent; + match cause { + MullvadEvent::LoggedIn => proto::device_event::Cause::LoggedIn, + MullvadEvent::LoggedOut => proto::device_event::Cause::LoggedOut, + MullvadEvent::Revoked => proto::device_event::Cause::Revoked, + MullvadEvent::Updated => proto::device_event::Cause::Updated, + MullvadEvent::RotatedKey => proto::device_event::Cause::RotatedKey, + } + } +} + +impl From<mullvad_types::device::RemoveDeviceEvent> for proto::RemoveDeviceEvent { + fn from(event: mullvad_types::device::RemoveDeviceEvent) -> Self { + proto::RemoveDeviceEvent { + account_token: event.account_token, + new_device_list: event + .new_devices + .into_iter() + .map(proto::Device::from) + .collect(), + } + } +} + +impl From<mullvad_types::device::AccountAndDevice> for proto::AccountAndDevice { + fn from(device: mullvad_types::device::AccountAndDevice) -> Self { + proto::AccountAndDevice { + account_token: device.account_token, + device: Some(proto::Device::from(device.device)), + } + } +} + +impl From<Vec<mullvad_types::device::Device>> for proto::DeviceList { + fn from(devices: Vec<mullvad_types::device::Device>) -> Self { + proto::DeviceList { + devices: devices.into_iter().map(proto::Device::from).collect(), + } + } +} + +impl From<proto::DevicePort> for mullvad_types::device::DevicePort { + fn from(port: proto::DevicePort) -> Self { + mullvad_types::device::DevicePort { id: port.id } + } +} diff --git a/mullvad-management-interface/src/types/conversions/location.rs b/mullvad-management-interface/src/types/conversions/location.rs new file mode 100644 index 0000000000..65fa825638 --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/location.rs @@ -0,0 +1,46 @@ +use crate::types::{ + conversions::{arg_from_str, option_from_proto_string}, + proto, FromProtobufTypeError, +}; + +impl From<mullvad_types::location::GeoIpLocation> for proto::GeoIpLocation { + fn from(geoip: mullvad_types::location::GeoIpLocation) -> proto::GeoIpLocation { + proto::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(), + entry_hostname: geoip.entry_hostname.unwrap_or_default(), + obfuscator_hostname: geoip.obfuscator_hostname.unwrap_or_default(), + } + } +} + +impl TryFrom<proto::GeoIpLocation> for mullvad_types::location::GeoIpLocation { + type Error = FromProtobufTypeError; + + fn try_from(geoip: proto::GeoIpLocation) -> Result<Self, Self::Error> { + Ok(mullvad_types::location::GeoIpLocation { + ipv4: option_from_proto_string(geoip.ipv4) + .map(|addr| arg_from_str(&addr, "invalid IPv4 address")) + .transpose()?, + ipv6: option_from_proto_string(geoip.ipv6) + .map(|addr| arg_from_str(&addr, "invalid IPv6 address")) + .transpose()?, + country: geoip.country, + city: option_from_proto_string(geoip.city), + latitude: geoip.latitude, + longitude: geoip.longitude, + mullvad_exit_ip: geoip.mullvad_exit_ip, + hostname: option_from_proto_string(geoip.hostname), + bridge_hostname: option_from_proto_string(geoip.bridge_hostname), + entry_hostname: option_from_proto_string(geoip.entry_hostname), + obfuscator_hostname: option_from_proto_string(geoip.obfuscator_hostname), + }) + } +} diff --git a/mullvad-management-interface/src/types/conversions/mod.rs b/mullvad-management-interface/src/types/conversions/mod.rs index b232569558..46639424fd 100644 --- a/mullvad-management-interface/src/types/conversions/mod.rs +++ b/mullvad-management-interface/src/types/conversions/mod.rs @@ -1,1890 +1,42 @@ -use super::proto; -use mullvad_types::relay_constraints::Constraint; -use prost_types::Timestamp; -use std::{convert::TryFrom, net::SocketAddr, str::FromStr}; -use talpid_types::{net::wireguard, ErrorExt}; +use std::str::FromStr; -impl From<mullvad_types::location::GeoIpLocation> for proto::GeoIpLocation { - fn from(geoip: mullvad_types::location::GeoIpLocation) -> proto::GeoIpLocation { - proto::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(), - entry_hostname: geoip.entry_hostname.unwrap_or_default(), - obfuscator_hostname: geoip.obfuscator_hostname.unwrap_or_default(), - } - } -} - -impl TryFrom<proto::GeoIpLocation> for mullvad_types::location::GeoIpLocation { - type Error = FromProtobufTypeError; - - fn try_from(geoip: proto::GeoIpLocation) -> Result<Self, Self::Error> { - Ok(mullvad_types::location::GeoIpLocation { - ipv4: option_from_proto_string(geoip.ipv4) - .map(|addr| arg_from_str(&addr, "invalid IPv4 address")) - .transpose()?, - ipv6: option_from_proto_string(geoip.ipv6) - .map(|addr| arg_from_str(&addr, "invalid IPv6 address")) - .transpose()?, - country: geoip.country, - city: option_from_proto_string(geoip.city), - latitude: geoip.latitude, - longitude: geoip.longitude, - mullvad_exit_ip: geoip.mullvad_exit_ip, - hostname: option_from_proto_string(geoip.hostname), - bridge_hostname: option_from_proto_string(geoip.bridge_hostname), - entry_hostname: option_from_proto_string(geoip.entry_hostname), - obfuscator_hostname: option_from_proto_string(geoip.obfuscator_hostname), - }) - } -} - -impl From<talpid_types::net::TunnelEndpoint> for proto::TunnelEndpoint { - fn from(endpoint: talpid_types::net::TunnelEndpoint) -> Self { - use talpid_types::net; - - proto::TunnelEndpoint { - address: endpoint.endpoint.address.to_string(), - protocol: i32::from(proto::TransportProtocol::from(endpoint.endpoint.protocol)), - tunnel_type: match endpoint.tunnel_type { - net::TunnelType::Wireguard => i32::from(proto::TunnelType::Wireguard), - net::TunnelType::OpenVpn => i32::from(proto::TunnelType::Openvpn), - }, - quantum_resistant: endpoint.quantum_resistant, - proxy: endpoint.proxy.map(|proxy_ep| proto::ProxyEndpoint { - address: proxy_ep.endpoint.address.to_string(), - protocol: i32::from(proto::TransportProtocol::from(proxy_ep.endpoint.protocol)), - proxy_type: match proxy_ep.proxy_type { - net::proxy::ProxyType::Shadowsocks => i32::from(proto::ProxyType::Shadowsocks), - net::proxy::ProxyType::Custom => i32::from(proto::ProxyType::Custom), - }, - }), - obfuscation: endpoint.obfuscation.map(|obfuscation_endpoint| { - proto::ObfuscationEndpoint { - address: obfuscation_endpoint.endpoint.address.ip().to_string(), - port: u32::from(obfuscation_endpoint.endpoint.address.port()), - protocol: i32::from(proto::TransportProtocol::from( - obfuscation_endpoint.endpoint.protocol, - )), - obfuscation_type: match obfuscation_endpoint.obfuscation_type { - net::ObfuscationType::Udp2Tcp => i32::from(proto::ObfuscationType::Udp2tcp), - }, - } - }), - entry_endpoint: endpoint.entry_endpoint.map(|entry| proto::Endpoint { - address: entry.address.to_string(), - protocol: i32::from(proto::TransportProtocol::from(entry.protocol)), - }), - } - } -} - -impl From<mullvad_types::states::TunnelState> for proto::TunnelState { - fn from(state: mullvad_types::states::TunnelState) -> Self { - use mullvad_types::states::TunnelState as MullvadTunnelState; - use proto::error_state::{ - firewall_policy_error::ErrorType as PolicyErrorType, Cause, FirewallPolicyError, - GenerationError, - }; - - 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 => { - proto::tunnel_state::State::Disconnected(proto::tunnel_state::Disconnected {}) - } - MullvadTunnelState::Connecting { endpoint, location } => { - proto::tunnel_state::State::Connecting(proto::tunnel_state::Connecting { - relay_info: Some(proto::TunnelStateRelayInfo { - tunnel_endpoint: Some(proto::TunnelEndpoint::from(endpoint)), - location: location.map(proto::GeoIpLocation::from), - }), - }) - } - MullvadTunnelState::Connected { endpoint, location } => { - proto::tunnel_state::State::Connected(proto::tunnel_state::Connected { - relay_info: Some(proto::TunnelStateRelayInfo { - tunnel_endpoint: Some(proto::TunnelEndpoint::from(endpoint)), - location: location.map(proto::GeoIpLocation::from), - }), - }) - } - MullvadTunnelState::Disconnecting(after_disconnect) => { - proto::tunnel_state::State::Disconnecting(proto::tunnel_state::Disconnecting { - after_disconnect: match after_disconnect { - talpid_tunnel::ActionAfterDisconnect::Nothing => { - i32::from(proto::AfterDisconnect::Nothing) - } - talpid_tunnel::ActionAfterDisconnect::Block => { - i32::from(proto::AfterDisconnect::Block) - } - talpid_tunnel::ActionAfterDisconnect::Reconnect => { - i32::from(proto::AfterDisconnect::Reconnect) - } - }, - }) - } - MullvadTunnelState::Error(error_state) => { - proto::tunnel_state::State::Error(proto::tunnel_state::Error { - error_state: Some(proto::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) - } - #[cfg(target_os = "windows")] - talpid_tunnel::ErrorStateCause::SplitTunnelError => { - i32::from(Cause::SplitTunnelError) - } - }, - 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 - }, - }), - }) - } - }; - - proto::TunnelState { state: Some(state) } - } -} - -impl TryFrom<proto::TunnelState> for mullvad_types::states::TunnelState { - type Error = FromProtobufTypeError; - - fn try_from(state: proto::TunnelState) -> Result<Self, FromProtobufTypeError> { - use mullvad_types::states::TunnelState as MullvadState; - use talpid_types::{net as talpid_net, tunnel as talpid_tunnel}; - - let state = match state.state { - Some(proto::tunnel_state::State::Disconnected(_)) => MullvadState::Disconnected, - Some(proto::tunnel_state::State::Connecting(proto::tunnel_state::Connecting { - relay_info: - Some(proto::TunnelStateRelayInfo { - tunnel_endpoint: Some(tunnel_endpoint), - location, - }), - })) => MullvadState::Connecting { - endpoint: talpid_net::TunnelEndpoint::try_from(tunnel_endpoint)?, - location: location - .map(mullvad_types::location::GeoIpLocation::try_from) - .transpose()?, - }, - Some(proto::tunnel_state::State::Connected(proto::tunnel_state::Connected { - relay_info: - Some(proto::TunnelStateRelayInfo { - tunnel_endpoint: Some(tunnel_endpoint), - location, - }), - })) => MullvadState::Connected { - endpoint: talpid_net::TunnelEndpoint::try_from(tunnel_endpoint)?, - location: location - .map(mullvad_types::location::GeoIpLocation::try_from) - .transpose()?, - }, - Some(proto::tunnel_state::State::Disconnecting( - proto::tunnel_state::Disconnecting { after_disconnect }, - )) => MullvadState::Disconnecting( - match proto::AfterDisconnect::from_i32(after_disconnect) { - Some(proto::AfterDisconnect::Nothing) => { - talpid_tunnel::ActionAfterDisconnect::Nothing - } - Some(proto::AfterDisconnect::Block) => { - talpid_tunnel::ActionAfterDisconnect::Block - } - Some(proto::AfterDisconnect::Reconnect) => { - talpid_tunnel::ActionAfterDisconnect::Reconnect - } - _ => { - return Err(FromProtobufTypeError::InvalidArgument( - "invalid \"after_disconnect\" action", - )) - } - }, - ), - Some(proto::tunnel_state::State::Error(proto::tunnel_state::Error { - error_state: - Some(proto::ErrorState { - cause, - blocking_error, - auth_fail_reason, - parameter_error, - policy_error, - }), - })) => { - let cause = match proto::error_state::Cause::from_i32(cause) { - Some(proto::error_state::Cause::AuthFailed) => { - talpid_tunnel::ErrorStateCause::AuthFailed(option_from_proto_string( - auth_fail_reason, - )) - } - Some(proto::error_state::Cause::Ipv6Unavailable) => { - talpid_tunnel::ErrorStateCause::Ipv6Unavailable - } - Some(proto::error_state::Cause::IsOffline) => { - talpid_tunnel::ErrorStateCause::IsOffline - } - Some(proto::error_state::Cause::SetDnsError) => { - talpid_tunnel::ErrorStateCause::SetDnsError - } - Some(proto::error_state::Cause::SetFirewallPolicyError) => { - let policy_error = policy_error.ok_or( - FromProtobufTypeError::InvalidArgument("missing firewall policy error"), - )?; - let policy_error = try_firewall_policy_error_from_i32( - policy_error.r#type, - policy_error.lock_pid, - policy_error.lock_name, - )?; - talpid_tunnel::ErrorStateCause::SetFirewallPolicyError(policy_error) - } - Some(proto::error_state::Cause::StartTunnelError) => { - talpid_tunnel::ErrorStateCause::StartTunnelError - } - Some(proto::error_state::Cause::TunnelParameterError) => { - let parameter_error = match proto::error_state::GenerationError::from_i32(parameter_error) { - Some(proto::error_state::GenerationError::CustomTunnelHostResolutionError) => talpid_tunnel::ParameterGenerationError::CustomTunnelHostResultionError, - Some(proto::error_state::GenerationError::NoMatchingBridgeRelay) => talpid_tunnel::ParameterGenerationError::NoMatchingBridgeRelay, - Some(proto::error_state::GenerationError::NoMatchingRelay) => talpid_tunnel::ParameterGenerationError::NoMatchingRelay, - Some(proto::error_state::GenerationError::NoWireguardKey) => talpid_tunnel::ParameterGenerationError::NoWireguardKey, - _ => return Err(FromProtobufTypeError::InvalidArgument( - "invalid parameter error", - )), - }; - talpid_tunnel::ErrorStateCause::TunnelParameterError(parameter_error) - } - #[cfg(target_os = "android")] - Some(proto::error_state::Cause::VpnPermissionDenied) => { - talpid_tunnel::ErrorStateCause::VpnPermissionDenied - } - #[cfg(target_os = "windows")] - Some(proto::error_state::Cause::SplitTunnelError) => { - talpid_tunnel::ErrorStateCause::SplitTunnelError - } - _ => { - return Err(FromProtobufTypeError::InvalidArgument( - "invalid error cause", - )) - } - }; - - let block_failure = blocking_error - .map(|blocking_error| { - try_firewall_policy_error_from_i32( - blocking_error.r#type, - blocking_error.lock_pid, - blocking_error.lock_name, - ) - }) - .transpose()?; - - MullvadState::Error(talpid_tunnel::ErrorState::new(cause, block_failure)) - } - _ => { - return Err(FromProtobufTypeError::InvalidArgument( - "invalid tunnel state", - )) - } - }; - - Ok(state) - } -} - -impl TryFrom<proto::TunnelEndpoint> for talpid_types::net::TunnelEndpoint { - type Error = FromProtobufTypeError; - - fn try_from(endpoint: proto::TunnelEndpoint) -> Result<Self, Self::Error> { - use talpid_types::net as talpid_net; - - Ok(talpid_net::TunnelEndpoint { - endpoint: talpid_net::Endpoint { - address: arg_from_str(&endpoint.address, "invalid endpoint address")?, - protocol: try_transport_protocol_from_i32(endpoint.protocol)?, - }, - tunnel_type: try_tunnel_type_from_i32(endpoint.tunnel_type)?, - quantum_resistant: endpoint.quantum_resistant, - proxy: endpoint - .proxy - .map(|proxy_ep| { - Ok(talpid_net::proxy::ProxyEndpoint { - endpoint: talpid_net::Endpoint { - address: arg_from_str( - &proxy_ep.address, - "invalid proxy endpoint address", - )?, - protocol: try_transport_protocol_from_i32(proxy_ep.protocol)?, - }, - proxy_type: match proto::ProxyType::from_i32(proxy_ep.proxy_type) { - Some(proto::ProxyType::Shadowsocks) => { - talpid_net::proxy::ProxyType::Shadowsocks - } - Some(proto::ProxyType::Custom) => talpid_net::proxy::ProxyType::Custom, - None => { - return Err(FromProtobufTypeError::InvalidArgument( - "unknown proxy type", - )) - } - }, - }) - }) - .transpose()?, - obfuscation: endpoint - .obfuscation - .map(|obfs_ep| { - Ok(talpid_net::ObfuscationEndpoint { - endpoint: talpid_net::Endpoint { - address: SocketAddr::new( - arg_from_str( - &obfs_ep.address, - "invalid obfuscation endpoint address", - )?, - obfs_ep.port as u16, - ), - protocol: try_transport_protocol_from_i32(obfs_ep.protocol)?, - }, - obfuscation_type: match proto::ObfuscationType::from_i32( - obfs_ep.obfuscation_type, - ) { - Some(proto::ObfuscationType::Udp2tcp) => { - talpid_net::ObfuscationType::Udp2Tcp - } - None => { - return Err(FromProtobufTypeError::InvalidArgument( - "unknown obfuscation type", - )) - } - }, - }) - }) - .transpose()?, - entry_endpoint: endpoint - .entry_endpoint - .map(|entry| { - Ok(talpid_net::Endpoint { - address: arg_from_str(&entry.address, "invalid entry endpoint address")?, - protocol: try_transport_protocol_from_i32(entry.protocol)?, - }) - }) - .transpose()?, - }) - } -} - -impl From<mullvad_types::device::Device> for proto::Device { - fn from(device: mullvad_types::device::Device) -> Self { - proto::Device { - id: device.id, - name: device.name, - pubkey: device.pubkey.as_bytes().to_vec(), - ports: device - .ports - .into_iter() - .map(proto::DevicePort::from) - .collect(), - hijack_dns: device.hijack_dns, - created: Some(Timestamp { - seconds: device.created.timestamp(), - nanos: 0, - }), - } - } -} - -impl From<mullvad_types::device::DevicePort> for proto::DevicePort { - fn from(port: mullvad_types::device::DevicePort) -> Self { - proto::DevicePort { id: port.id } - } -} - -impl From<mullvad_types::device::DeviceState> for proto::DeviceState { - fn from(state: mullvad_types::device::DeviceState) -> Self { - proto::DeviceState { - state: proto::device_state::State::from(&state) as i32, - device: state.into_device().map(|device| proto::AccountAndDevice { - account_token: device.account_token, - device: Some(proto::Device::from(device.device)), - }), - } - } -} - -impl From<&mullvad_types::device::DeviceState> for proto::device_state::State { - fn from(state: &mullvad_types::device::DeviceState) -> Self { - use mullvad_types::device::DeviceState as MullvadState; - match state { - MullvadState::LoggedIn(_) => proto::device_state::State::LoggedIn, - MullvadState::LoggedOut => proto::device_state::State::LoggedOut, - MullvadState::Revoked => proto::device_state::State::Revoked, - } - } -} - -impl From<mullvad_types::device::DeviceEvent> for proto::DeviceEvent { - fn from(event: mullvad_types::device::DeviceEvent) -> Self { - proto::DeviceEvent { - cause: proto::device_event::Cause::from(event.cause) as i32, - new_state: Some(proto::DeviceState::from(event.new_state)), - } - } -} - -impl From<mullvad_types::device::DeviceEventCause> for proto::device_event::Cause { - fn from(cause: mullvad_types::device::DeviceEventCause) -> Self { - use mullvad_types::device::DeviceEventCause as MullvadEvent; - match cause { - MullvadEvent::LoggedIn => proto::device_event::Cause::LoggedIn, - MullvadEvent::LoggedOut => proto::device_event::Cause::LoggedOut, - MullvadEvent::Revoked => proto::device_event::Cause::Revoked, - MullvadEvent::Updated => proto::device_event::Cause::Updated, - MullvadEvent::RotatedKey => proto::device_event::Cause::RotatedKey, - } - } -} - -impl From<mullvad_types::device::RemoveDeviceEvent> for proto::RemoveDeviceEvent { - fn from(event: mullvad_types::device::RemoveDeviceEvent) -> Self { - proto::RemoveDeviceEvent { - account_token: event.account_token, - new_device_list: event - .new_devices - .into_iter() - .map(proto::Device::from) - .collect(), - } - } -} - -impl From<mullvad_types::device::AccountAndDevice> for proto::AccountAndDevice { - fn from(device: mullvad_types::device::AccountAndDevice) -> Self { - proto::AccountAndDevice { - account_token: device.account_token, - device: Some(proto::Device::from(device.device)), - } - } -} - -impl From<Vec<mullvad_types::device::Device>> for proto::DeviceList { - fn from(devices: Vec<mullvad_types::device::Device>) -> Self { - proto::DeviceList { - devices: devices.into_iter().map(proto::Device::from).collect(), - } - } -} - -impl From<mullvad_types::wireguard::PublicKey> for proto::PublicKey { - fn from(public_key: mullvad_types::wireguard::PublicKey) -> Self { - proto::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 proto::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 proto::ConnectionConfig { - fn from(config: mullvad_types::ConnectionConfig) -> Self { - use proto::connection_config; - - 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(proto::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(), - }), - 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 proto::TransportProtocol { - fn from(protocol: talpid_types::net::TransportProtocol) -> Self { - match protocol { - talpid_types::net::TransportProtocol::Udp => proto::TransportProtocol::Udp, - talpid_types::net::TransportProtocol::Tcp => proto::TransportProtocol::Tcp, - } - } -} - -impl From<talpid_types::net::IpVersion> for proto::IpVersion { - fn from(version: talpid_types::net::IpVersion) -> Self { - match version { - talpid_types::net::IpVersion::V4 => proto::IpVersion::V4, - talpid_types::net::IpVersion::V6 => proto::IpVersion::V6, - } - } -} - -impl From<proto::IpVersion> for proto::IpVersionConstraint { - fn from(version: proto::IpVersion) -> Self { - Self { - protocol: i32::from(version), - } - } -} - -impl From<mullvad_types::relay_constraints::TransportPort> for proto::TransportPort { - fn from(port: mullvad_types::relay_constraints::TransportPort) -> Self { - proto::TransportPort { - protocol: proto::TransportProtocol::from(port.protocol) as i32, - port: port.port.map(u32::from).unwrap_or(0), - } - } -} - -impl - From< - mullvad_types::relay_constraints::Constraint< - mullvad_types::relay_constraints::LocationConstraint, - >, - > for proto::RelayLocation -{ - fn from( - location: mullvad_types::relay_constraints::Constraint< - mullvad_types::relay_constraints::LocationConstraint, - >, - ) -> Self { - location - .option() - .map(proto::RelayLocation::from) - .unwrap_or_default() - } -} - -impl From<mullvad_types::relay_constraints::LocationConstraint> for proto::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 proto::Settings { - fn from(settings: &mullvad_types::settings::Settings) -> Self { - #[cfg(windows)] - let split_tunnel = { - let mut converted_list = vec![]; - for path in settings.split_tunnel.apps.clone().iter() { - match path.as_path().as_os_str().to_str() { - Some(path) => converted_list.push(path.to_string()), - None => { - log::error!("failed to convert OS string: {:?}", path); - } - } - } - - Some(proto::SplitTunnelSettings { - enable_exclusions: settings.split_tunnel.enable_exclusions, - apps: converted_list, - }) - }; - #[cfg(not(windows))] - let split_tunnel = None; - - Self { - relay_settings: Some(proto::RelaySettings::from(settings.get_relay_settings())), - bridge_settings: Some(proto::BridgeSettings::from( - settings.bridge_settings.clone(), - )), - bridge_state: Some(proto::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(proto::TunnelOptions::from(&settings.tunnel_options)), - show_beta_releases: settings.show_beta_releases, - obfuscation_settings: Some(proto::ObfuscationSettings::from( - &settings.obfuscation_settings, - )), - split_tunnel, - } - } -} - -impl From<mullvad_types::relay_constraints::BridgeState> for proto::BridgeState { - fn from(state: mullvad_types::relay_constraints::BridgeState) -> Self { - use mullvad_types::relay_constraints::BridgeState; - Self { - state: i32::from(match state { - BridgeState::Auto => proto::bridge_state::State::Auto, - BridgeState::On => proto::bridge_state::State::On, - BridgeState::Off => proto::bridge_state::State::Off, - }), - } - } -} - -impl From<&mullvad_types::relay_constraints::ObfuscationSettings> for proto::ObfuscationSettings { - fn from(settings: &mullvad_types::relay_constraints::ObfuscationSettings) -> Self { - use mullvad_types::relay_constraints::SelectedObfuscation; - let selected_obfuscation = i32::from(match settings.selected_obfuscation { - SelectedObfuscation::Auto => proto::obfuscation_settings::SelectedObfuscation::Auto, - SelectedObfuscation::Off => proto::obfuscation_settings::SelectedObfuscation::Off, - SelectedObfuscation::Udp2Tcp => { - proto::obfuscation_settings::SelectedObfuscation::Udp2tcp - } - }); - Self { - selected_obfuscation, - udp2tcp: Some(proto::Udp2TcpObfuscationSettings::from(&settings.udp2tcp)), - } - } -} - -impl From<&mullvad_types::relay_constraints::Udp2TcpObfuscationSettings> - for proto::Udp2TcpObfuscationSettings -{ - fn from(settings: &mullvad_types::relay_constraints::Udp2TcpObfuscationSettings) -> Self { - Self { - port: u32::from(settings.port.unwrap_or(0)), - } - } -} - -impl From<mullvad_types::relay_constraints::BridgeSettings> for proto::BridgeSettings { - fn from(settings: mullvad_types::relay_constraints::BridgeSettings) -> Self { - use mullvad_types::relay_constraints::BridgeSettings as MullvadBridgeSettings; - use proto::bridge_settings; - 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(proto::RelayLocation::from), - providers: convert_providers_constraint(&constraints.providers), - ownership: convert_ownership_constraint(&constraints.ownership) as i32, - }) - } - 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, - }) - } - }, - }; - - proto::BridgeSettings { - r#type: Some(settings), - } - } -} - -impl From<mullvad_types::relay_constraints::RelaySettings> for proto::RelaySettings { - fn from(settings: mullvad_types::relay_constraints::RelaySettings) -> Self { - use mullvad_types::relay_constraints::RelaySettings as MullvadRelaySettings; - use proto::relay_settings; - use talpid_types::net as talpid_net; - - let endpoint = match settings { - MullvadRelaySettings::CustomTunnelEndpoint(endpoint) => { - relay_settings::Endpoint::Custom(proto::CustomRelaySettings { - host: endpoint.host, - config: Some(proto::ConnectionConfig::from(endpoint.config)), - }) - } - MullvadRelaySettings::Normal(constraints) => { - relay_settings::Endpoint::Normal(proto::NormalRelaySettings { - location: constraints - .location - .option() - .map(proto::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) => { - Some(proto::TunnelType::Wireguard) - } - Constraint::Only(talpid_net::TunnelType::OpenVpn) => { - Some(proto::TunnelType::Openvpn) - } - } - .map(|tunnel_type| proto::TunnelTypeConstraint { - tunnel_type: i32::from(tunnel_type), - }), - - wireguard_constraints: Some(proto::WireguardConstraints { - port: u32::from(constraints.wireguard_constraints.port.unwrap_or(0)), - ip_version: constraints - .wireguard_constraints - .ip_version - .option() - .map(proto::IpVersion::from) - .map(proto::IpVersionConstraint::from), - use_multihop: constraints.wireguard_constraints.use_multihop, - entry_location: constraints - .wireguard_constraints - .entry_location - .option() - .map(proto::RelayLocation::from), - }), - - openvpn_constraints: Some(proto::OpenvpnConstraints { - port: constraints - .openvpn_constraints - .port - .option() - .map(proto::TransportPort::from), - }), - }) - } - }; - - Self { - endpoint: Some(endpoint), - } - } -} - -impl From<&mullvad_types::settings::DnsOptions> for proto::DnsOptions { - fn from(options: &mullvad_types::settings::DnsOptions) -> Self { - use proto::dns_options; - - proto::DnsOptions { - state: match options.state { - mullvad_types::settings::DnsState::Default => dns_options::DnsState::Default as i32, - mullvad_types::settings::DnsState::Custom => dns_options::DnsState::Custom as i32, - }, - default_options: Some(proto::DefaultDnsOptions { - block_ads: options.default_options.block_ads, - block_trackers: options.default_options.block_trackers, - block_malware: options.default_options.block_malware, - block_adult_content: options.default_options.block_adult_content, - block_gambling: options.default_options.block_gambling, - }), - custom_options: Some(proto::CustomDnsOptions { - addresses: options - .custom_options - .addresses - .iter() - .map(|addr| addr.to_string()) - .collect(), - }), - } - } -} - -impl From<&mullvad_types::settings::TunnelOptions> for proto::TunnelOptions { - fn from(options: &mullvad_types::settings::TunnelOptions) -> Self { - Self { - openvpn: Some(proto::tunnel_options::OpenvpnOptions { - mssfix: u32::from(options.openvpn.mssfix.unwrap_or_default()), - }), - wireguard: Some(proto::tunnel_options::WireguardOptions { - mtu: u32::from(options.wireguard.options.mtu.unwrap_or_default()), - rotation_interval: options.wireguard.rotation_interval.map(|ivl| { - prost_types::Duration::try_from(std::time::Duration::from(ivl)) - .expect("Failed to convert std::time::Duration to prost_types::Duration for tunnel_options.wireguard.rotation_interval") - }), - #[cfg(windows)] - use_wireguard_nt: options.wireguard.options.use_wireguard_nt, - #[cfg(not(windows))] - use_wireguard_nt: false, - use_pq_safe_psk: options.wireguard.options.use_pq_safe_psk, - }), - generic: Some(proto::tunnel_options::GenericOptions { - enable_ipv6: options.generic.enable_ipv6, - }), - #[cfg(not(target_os = "android"))] - dns_options: Some(proto::DnsOptions::from(&options.dns_options)), - #[cfg(target_os = "android")] - dns_options: None, - } - } -} - -impl From<mullvad_types::relay_list::RelayList> for proto::RelayList { - fn from(relay_list: mullvad_types::relay_list::RelayList) -> Self { - let mut proto_list = proto::RelayList { - countries: vec![], - openvpn: Some(proto::OpenVpnEndpointData::from(relay_list.openvpn)), - bridge: Some(proto::BridgeEndpointData::from(relay_list.bridge)), - wireguard: Some(proto::WireguardEndpointData::from(relay_list.wireguard)), - }; - proto_list.countries = relay_list - .countries - .into_iter() - .map(proto::RelayListCountry::from) - .collect(); - proto_list - } -} - -impl From<mullvad_types::relay_list::OpenVpnEndpointData> for proto::OpenVpnEndpointData { - fn from(openvpn: mullvad_types::relay_list::OpenVpnEndpointData) -> Self { - proto::OpenVpnEndpointData { - endpoints: openvpn - .ports - .into_iter() - .map(|endpoint| proto::OpenVpnEndpoint { - port: u32::from(endpoint.port), - protocol: proto::TransportProtocol::from(endpoint.protocol) as i32, - }) - .collect(), - } - } -} - -impl From<mullvad_types::relay_list::BridgeEndpointData> for proto::BridgeEndpointData { - fn from(bridge: mullvad_types::relay_list::BridgeEndpointData) -> Self { - proto::BridgeEndpointData { - shadowsocks: bridge - .shadowsocks - .into_iter() - .map(|endpoint| proto::ShadowsocksEndpointData { - port: u32::from(endpoint.port), - cipher: endpoint.cipher, - password: endpoint.password, - protocol: proto::TransportProtocol::from(endpoint.protocol) as i32, - }) - .collect(), - } - } -} - -impl From<mullvad_types::relay_list::WireguardEndpointData> for proto::WireguardEndpointData { - fn from(wireguard: mullvad_types::relay_list::WireguardEndpointData) -> Self { - proto::WireguardEndpointData { - port_ranges: wireguard - .port_ranges - .into_iter() - .map(|(first, last)| proto::PortRange { - first: u32::from(first), - last: u32::from(last), - }) - .collect(), - ipv4_gateway: wireguard.ipv4_gateway.to_string(), - ipv6_gateway: wireguard.ipv6_gateway.to_string(), - udp2tcp_ports: wireguard.udp2tcp_ports.into_iter().map(u32::from).collect(), - } - } -} - -impl From<mullvad_types::relay_list::RelayListCountry> for proto::RelayListCountry { - fn from(country: mullvad_types::relay_list::RelayListCountry) -> Self { - let mut proto_country = proto::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(proto::RelayListCity { - name: city.name, - code: city.code, - latitude: city.latitude, - longitude: city.longitude, - relays: city.relays.into_iter().map(proto::Relay::from).collect(), - }); - } - - proto_country - } -} - -impl From<mullvad_types::relay_list::Relay> for proto::Relay { - fn from(relay: mullvad_types::relay_list::Relay) -> Self { - use mullvad_types::relay_list::RelayEndpointData as MullvadEndpointData; - - Self { - hostname: relay.hostname, - ipv4_addr_in: relay.ipv4_addr_in.to_string(), - 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, - endpoint_type: match &relay.endpoint_data { - MullvadEndpointData::Openvpn => proto::relay::RelayType::Openvpn as i32, - MullvadEndpointData::Bridge => proto::relay::RelayType::Bridge as i32, - MullvadEndpointData::Wireguard(_) => proto::relay::RelayType::Wireguard as i32, - }, - endpoint_data: match relay.endpoint_data { - MullvadEndpointData::Wireguard(data) => Some(to_proto_any( - "mullvad_daemon.management_interface/WireguardRelayEndpointData", - proto::WireguardRelayEndpointData { - public_key: data.public_key.as_bytes().to_vec(), - }, - )), - _ => None, - }, - location: relay.location.map(|location| proto::Location { - country: location.country, - country_code: location.country_code, - city: location.city, - city_code: location.city_code, - latitude: location.latitude, - longitude: location.longitude, - }), - } - } -} - -impl TryFrom<proto::Relay> for mullvad_types::relay_list::Relay { - type Error = FromProtobufTypeError; - - fn try_from(relay: proto::Relay) -> Result<Self, Self::Error> { - use mullvad_types::{ - location::Location as MullvadLocation, - relay_list::{Relay as MullvadRelay, RelayEndpointData as MullvadEndpointData}, - }; - - let endpoint_data = match relay.endpoint_type { - i if i == proto::relay::RelayType::Openvpn as i32 => MullvadEndpointData::Openvpn, - i if i == proto::relay::RelayType::Bridge as i32 => MullvadEndpointData::Bridge, - i if i == proto::relay::RelayType::Wireguard as i32 => { - let data = relay - .endpoint_data - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing endpoint wg data", - ))?; - let data: proto::WireguardRelayEndpointData = try_from_proto_any( - "mullvad_daemon.management_interface/WireguardRelayEndpointData", - data, - ) - .ok_or(FromProtobufTypeError::InvalidArgument( - "invalid endpoint wg data", - ))?; - MullvadEndpointData::Wireguard( - mullvad_types::relay_list::WireguardRelayEndpointData { - public_key: bytes_to_pubkey(&data.public_key)?, - }, - ) - } - _ => { - return Err(FromProtobufTypeError::InvalidArgument( - "invalid relay endpoint type", - )) - } - }; - - let ipv6_addr_in = option_from_proto_string(relay.ipv6_addr_in) - .map(|addr| { - addr.parse().map_err(|_err| { - FromProtobufTypeError::InvalidArgument("invalid relay IPv6 address") - }) - }) - .transpose()?; - - Ok(MullvadRelay { - hostname: relay.hostname, - ipv4_addr_in: relay.ipv4_addr_in.parse().map_err(|_err| { - FromProtobufTypeError::InvalidArgument("invalid relay IPv4 address") - })?, - ipv6_addr_in, - include_in_country: relay.include_in_country, - active: relay.active, - owned: relay.owned, - provider: relay.provider, - weight: relay.weight, - endpoint_data, - location: relay.location.map(|location| MullvadLocation { - country: location.country, - country_code: location.country_code, - city: location.city, - city_code: location.city_code, - latitude: location.latitude, - longitude: location.longitude, - }), - }) - } -} - -impl From<proto::TransportProtocol> for talpid_types::net::TransportProtocol { - fn from(protocol: proto::TransportProtocol) -> Self { - match protocol { - proto::TransportProtocol::Udp => talpid_types::net::TransportProtocol::Udp, - proto::TransportProtocol::Tcp => talpid_types::net::TransportProtocol::Tcp, - } - } -} +mod custom_tunnel; +mod device; +mod location; +mod net; +pub mod relay_constraints; +mod relay_list; +mod settings; +mod states; +mod version; +mod wireguard; #[derive(Debug)] pub enum FromProtobufTypeError { InvalidArgument(&'static str), } -impl TryFrom<proto::Device> for mullvad_types::device::Device { - type Error = FromProtobufTypeError; - - fn try_from(device: proto::Device) -> Result<Self, Self::Error> { - Ok(mullvad_types::device::Device { - id: device.id, - name: device.name, - pubkey: bytes_to_pubkey(&device.pubkey)?, - ports: device - .ports - .into_iter() - .map(mullvad_types::device::DevicePort::from) - .collect(), - hijack_dns: device.hijack_dns, - created: chrono::DateTime::from_utc( - chrono::NaiveDateTime::from_timestamp( - device - .created - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing 'created' field", - ))? - .seconds, - 0, - ), - chrono::Utc, - ), - }) - } -} - -impl From<proto::DevicePort> for mullvad_types::device::DevicePort { - fn from(port: proto::DevicePort) -> Self { - mullvad_types::device::DevicePort { id: port.id } - } -} - -impl TryFrom<&proto::WireguardConstraints> - for mullvad_types::relay_constraints::WireguardConstraints -{ - type Error = FromProtobufTypeError; - - fn try_from( - constraints: &proto::WireguardConstraints, - ) -> Result<mullvad_types::relay_constraints::WireguardConstraints, Self::Error> { - use mullvad_types::relay_constraints as mullvad_constraints; - use talpid_types::net; - - let ip_version = match &constraints.ip_version { - Some(constraint) => match proto::IpVersion::from_i32(constraint.protocol) { - Some(proto::IpVersion::V4) => Some(net::IpVersion::V4), - Some(proto::IpVersion::V6) => Some(net::IpVersion::V6), - None => { - return Err(FromProtobufTypeError::InvalidArgument( - "invalid ip protocol version", - )) - } - }, - None => None, - }; - - Ok(mullvad_constraints::WireguardConstraints { - port: if constraints.port == 0 { - Constraint::Any - } else { - Constraint::Only(constraints.port as u16) - }, - ip_version: Constraint::from(ip_version), - use_multihop: constraints.use_multihop, - entry_location: constraints - .entry_location - .clone() - .map(Constraint::<mullvad_types::relay_constraints::LocationConstraint>::from) - .unwrap_or(Constraint::Any), - }) - } -} - -impl TryFrom<&proto::OpenvpnConstraints> for mullvad_types::relay_constraints::OpenVpnConstraints { - type Error = FromProtobufTypeError; - - fn try_from( - constraints: &proto::OpenvpnConstraints, - ) -> Result<mullvad_types::relay_constraints::OpenVpnConstraints, Self::Error> { - use mullvad_types::relay_constraints as mullvad_constraints; - - Ok(mullvad_constraints::OpenVpnConstraints { - port: Constraint::from(match &constraints.port { - Some(port) => Some(mullvad_constraints::TransportPort::try_from(port.clone())?), - None => None, - }), - }) - } -} - -impl TryFrom<proto::RelaySettings> for mullvad_types::relay_constraints::RelaySettings { - type Error = FromProtobufTypeError; - - fn try_from( - settings: proto::RelaySettings, - ) -> Result<mullvad_types::relay_constraints::RelaySettings, Self::Error> { - use mullvad_types::{relay_constraints as mullvad_constraints, CustomTunnelEndpoint}; - use talpid_types::net; - - let update_value = settings - .endpoint - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing relay settings", - ))?; - - match update_value { - proto::relay_settings::Endpoint::Custom(settings) => { - let config = settings - .config - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing relay connection config", - ))?; - let config = mullvad_types::ConnectionConfig::try_from(config)?; - Ok(mullvad_constraints::RelaySettings::CustomTunnelEndpoint( - CustomTunnelEndpoint { - host: settings.host, - config, - }, - )) - } - - proto::relay_settings::Endpoint::Normal(settings) => { - let location = settings - .location - .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) - .transpose()? - .unwrap_or(Constraint::Any); - let openvpn_constraints = - mullvad_constraints::OpenVpnConstraints::try_from( - &settings.openvpn_constraints.ok_or( - FromProtobufTypeError::InvalidArgument("missing openvpn constraints"), - )?, - )?; - let wireguard_constraints = mullvad_constraints::WireguardConstraints::try_from( - &settings.wireguard_constraints.ok_or( - FromProtobufTypeError::InvalidArgument("missing wireguard constraints"), - )?, - )?; - - Ok(mullvad_constraints::RelaySettings::Normal( - mullvad_constraints::RelayConstraints { - location, - providers, - ownership, - tunnel_protocol, - wireguard_constraints, - openvpn_constraints, - }, - )) - } - } - } -} - -impl TryFrom<proto::RelaySettingsUpdate> for mullvad_types::relay_constraints::RelaySettingsUpdate { - type Error = FromProtobufTypeError; - - fn try_from( - settings: proto::RelaySettingsUpdate, - ) -> Result<mullvad_types::relay_constraints::RelaySettingsUpdate, Self::Error> { - use mullvad_types::{relay_constraints as mullvad_constraints, CustomTunnelEndpoint}; - use talpid_types::net; - - let update_value = settings - .r#type - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing relay settings", - ))?; - - match update_value { - proto::relay_settings_update::Type::Custom(settings) => { - let config = settings - .config - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing relay connection config", - ))?; - let config = mullvad_types::ConnectionConfig::try_from(config)?; - Ok( - mullvad_constraints::RelaySettingsUpdate::CustomTunnelEndpoint( - CustomTunnelEndpoint { - host: settings.host, - config, - }, - ), - ) - } - - proto::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 providers = if let Some(ref provider_update) = settings.providers { - Some(try_providers_constraint_from_proto( - &provider_update.providers, - )?) - } 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 - .tunnel_type - .map(Constraint::<net::TunnelType>::try_from) - .transpose()? - .unwrap_or(Constraint::Any), - ) - } else { - None - }; - let openvpn_constraints = - if let Some(ref constraints) = settings.openvpn_constraints { - Some(mullvad_constraints::OpenVpnConstraints::try_from( - constraints, - )?) - } else { - None - }; - let wireguard_constraints = - if let Some(ref constraints) = settings.wireguard_constraints { - Some(mullvad_constraints::WireguardConstraints::try_from( - constraints, - )?) - } else { - None - }; - Ok(mullvad_constraints::RelaySettingsUpdate::Normal( - mullvad_constraints::RelayConstraintsUpdate { - location, - providers, - ownership, - tunnel_protocol, - wireguard_constraints, - openvpn_constraints, - }, - )) - } - } - } -} - -impl TryFrom<proto::TunnelTypeConstraint> for Constraint<talpid_types::net::TunnelType> { - type Error = FromProtobufTypeError; - - fn try_from( - tunnel_type: proto::TunnelTypeConstraint, - ) -> Result<Constraint<talpid_types::net::TunnelType>, Self::Error> { - let tunnel_type = try_tunnel_type_from_i32(tunnel_type.tunnel_type)?; - Ok(Constraint::Only(tunnel_type)) - } -} - -impl TryFrom<proto::ConnectionConfig> for mullvad_types::ConnectionConfig { - type Error = FromProtobufTypeError; - - fn try_from( - config: proto::ConnectionConfig, - ) -> Result<mullvad_types::ConnectionConfig, Self::Error> { - use talpid_types::net::{self, openvpn}; - - let config = config.config.ok_or(FromProtobufTypeError::InvalidArgument( - "missing connection config", - ))?; - match config { - proto::connection_config::Config::Openvpn(config) => { - let address = match config.address.parse() { - Ok(address) => address, - Err(_) => { - return Err(FromProtobufTypeError::InvalidArgument("invalid address")) - } - }; - - Ok(mullvad_types::ConnectionConfig::OpenVpn( - openvpn::ConnectionConfig { - endpoint: net::Endpoint { - address, - protocol: try_transport_protocol_from_i32(config.protocol)?, - }, - username: config.username, - password: config.password, - }, - )) - } - proto::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", - ))?; - - let public_key = bytes_to_pubkey(&peer.public_key)?; - - let ipv4_gateway = config.ipv4_gateway.parse().map_err(|_err| { - FromProtobufTypeError::InvalidArgument("invalid IPv4 gateway") - })?; - let ipv6_gateway = option_from_proto_string(config.ipv6_gateway) - .map(|addr| { - addr.parse().map_err(|_err| { - FromProtobufTypeError::InvalidArgument("invalid IPv6 gateway") - }) - }) - .transpose()?; - - let endpoint = peer.endpoint.parse().map_err(|_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); - } - - Ok(mullvad_types::ConnectionConfig::Wireguard( - wireguard::ConnectionConfig { - tunnel: wireguard::TunnelConfig { - private_key: wireguard::PrivateKey::from(private_key), - addresses: tunnel_addresses, - }, - peer: wireguard::PeerConfig { - public_key, - allowed_ips, - endpoint, - psk: None, - }, - exit_peer: None, - ipv4_gateway, - ipv6_gateway, - }, - )) - } - } - } -} - -fn bytes_to_pubkey(bytes: &[u8]) -> Result<wireguard::PublicKey, FromProtobufTypeError> { - if bytes.len() != 32 { - return Err(FromProtobufTypeError::InvalidArgument("invalid public key")); - } - let mut public_key = [0; 32]; - public_key.copy_from_slice(&bytes[..32]); - Ok(wireguard::PublicKey::from(public_key)) -} - -impl From<proto::RelayLocation> - for Constraint<mullvad_types::relay_constraints::LocationConstraint> -{ - fn from(location: proto::RelayLocation) -> Self { - use mullvad_types::relay_constraints::LocationConstraint; - - if let Some(hostname) = option_from_proto_string(location.hostname) { - Constraint::Only(LocationConstraint::Hostname( - location.country, - location.city, - hostname, - )) - } else if let Some(city) = option_from_proto_string(location.city) { - Constraint::Only(LocationConstraint::City(location.country, city)) - } else if let Some(country) = option_from_proto_string(location.country) { - Constraint::Only(LocationConstraint::Country(country)) - } else { - Constraint::Any - } - } -} - -impl TryFrom<proto::BridgeSettings> for mullvad_types::relay_constraints::BridgeSettings { - type Error = FromProtobufTypeError; - - fn try_from(settings: proto::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", - ))? { - proto::bridge_settings::Type::Normal(constraints) => { - let location = match constraints.location { - None => Constraint::Any, - Some(location) => { - Constraint::<mullvad_constraints::LocationConstraint>::from(location) - } - }; - 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, - }, - )) - } - proto::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)) - } - proto::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)) - } - proto::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<proto::ObfuscationSettings> for mullvad_types::relay_constraints::ObfuscationSettings { - type Error = FromProtobufTypeError; - - fn try_from(settings: proto::ObfuscationSettings) -> Result<Self, Self::Error> { - use mullvad_types::relay_constraints::SelectedObfuscation; - use proto::obfuscation_settings::SelectedObfuscation as IpcSelectedObfuscation; - let selected_obfuscation = - match IpcSelectedObfuscation::from_i32(settings.selected_obfuscation) { - Some(IpcSelectedObfuscation::Auto) => SelectedObfuscation::Auto, - Some(IpcSelectedObfuscation::Off) => SelectedObfuscation::Off, - Some(IpcSelectedObfuscation::Udp2tcp) => SelectedObfuscation::Udp2Tcp, - None => { - return Err(FromProtobufTypeError::InvalidArgument( - "invalid selected obfuscator", - )); - } - }; - - let udp2tcp = match settings.udp2tcp { - Some(settings) => { - mullvad_types::relay_constraints::Udp2TcpObfuscationSettings::try_from(&settings)? - } - None => { - return Err(FromProtobufTypeError::InvalidArgument( - "invalid selected obfuscator", - )); - } - }; - - Ok(Self { - selected_obfuscation, - udp2tcp, - }) - } -} - -impl TryFrom<&proto::Udp2TcpObfuscationSettings> - for mullvad_types::relay_constraints::Udp2TcpObfuscationSettings -{ - type Error = FromProtobufTypeError; - - fn try_from(settings: &proto::Udp2TcpObfuscationSettings) -> Result<Self, Self::Error> { - Ok(Self { - port: if settings.port == 0 { - Constraint::Any - } else { - Constraint::Only(settings.port as u16) - }, - }) - } -} - -impl TryFrom<proto::BridgeState> for mullvad_types::relay_constraints::BridgeState { - type Error = FromProtobufTypeError; - - fn try_from(state: proto::BridgeState) -> Result<Self, Self::Error> { - match proto::bridge_state::State::from_i32(state.state) { - Some(proto::bridge_state::State::Auto) => { - Ok(mullvad_types::relay_constraints::BridgeState::Auto) - } - Some(proto::bridge_state::State::On) => { - Ok(mullvad_types::relay_constraints::BridgeState::On) - } - Some(proto::bridge_state::State::Off) => { - Ok(mullvad_types::relay_constraints::BridgeState::Off) - } - None => Err(FromProtobufTypeError::InvalidArgument( - "invalid bridge state", - )), - } - } -} - -impl TryFrom<proto::TunnelOptions> for mullvad_types::settings::TunnelOptions { - type Error = FromProtobufTypeError; - - fn try_from(options: proto::TunnelOptions) -> Result<Self, Self::Error> { - use talpid_types::net; - - let openvpn_options = options - .openvpn - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing openvpn tunnel options", - ))?; - let wireguard_options = options - .wireguard - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing openvpn tunnel options", - ))?; - let generic_options = options - .generic - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing generic tunnel options", - ))?; - let dns_options = options - .dns_options - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing tunnel DNS options", - ))?; - - Ok(Self { - openvpn: net::openvpn::TunnelOptions { - mssfix: if openvpn_options.mssfix != 0 { - Some(openvpn_options.mssfix as u16) - } else { - None - }, - }, - wireguard: mullvad_types::wireguard::TunnelOptions { - options: net::wireguard::TunnelOptions { - mtu: if wireguard_options.mtu != 0 { - Some(wireguard_options.mtu as u16) - } else { - None - }, - use_pq_safe_psk: wireguard_options.use_pq_safe_psk, - #[cfg(windows)] - use_wireguard_nt: wireguard_options.use_wireguard_nt, - }, - rotation_interval: wireguard_options - .rotation_interval - .map(std::time::Duration::try_from) - .transpose() - .map_err(|_| FromProtobufTypeError::InvalidArgument("invalid duration"))? - .map(mullvad_types::wireguard::RotationInterval::try_from) - .transpose() - .map_err(|error: mullvad_types::wireguard::RotationIntervalError| { - log::error!( - "{}", - error.display_chain_with_msg("Invalid rotation interval") - ); - FromProtobufTypeError::InvalidArgument("invalid rotation interval") - })?, - }, - generic: net::GenericTunnelOptions { - enable_ipv6: generic_options.enable_ipv6, - }, - #[cfg(not(target_os = "android"))] - dns_options: mullvad_types::settings::DnsOptions::try_from(dns_options)?, - }) - } +fn bytes_to_pubkey( + bytes: &[u8], +) -> Result<talpid_types::net::wireguard::PublicKey, FromProtobufTypeError> { + Ok(talpid_types::net::wireguard::PublicKey::from( + *bytes_to_wg_key(bytes, "invalid public key")?, + )) } -impl TryFrom<proto::DnsOptions> for mullvad_types::settings::DnsOptions { - type Error = FromProtobufTypeError; - - fn try_from(options: proto::DnsOptions) -> Result<Self, Self::Error> { - use mullvad_types::settings::{ - CustomDnsOptions as MullvadCustomDnsOptions, - DefaultDnsOptions as MullvadDefaultDnsOptions, DnsOptions as MullvadDnsOptions, - DnsState as MullvadDnsState, - }; - - let state = match proto::dns_options::DnsState::from_i32(options.state) { - Some(proto::dns_options::DnsState::Default) => MullvadDnsState::Default, - Some(proto::dns_options::DnsState::Custom) => MullvadDnsState::Custom, - None => { - return Err(FromProtobufTypeError::InvalidArgument( - "invalid DNS options state", - )) - } - }; - - let default_options = - options - .default_options - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing default DNS options", - ))?; - let custom_options = - options - .custom_options - .ok_or(FromProtobufTypeError::InvalidArgument( - "missing default DNS options", - ))?; - - Ok(MullvadDnsOptions { - state, - default_options: MullvadDefaultDnsOptions { - block_ads: default_options.block_ads, - block_trackers: default_options.block_trackers, - block_malware: default_options.block_malware, - block_adult_content: default_options.block_adult_content, - block_gambling: default_options.block_gambling, - }, - custom_options: MullvadCustomDnsOptions { - addresses: custom_options - .addresses - .into_iter() - .map(|addr| { - addr.parse().map_err(|_| { - FromProtobufTypeError::InvalidArgument("invalid IP address") - }) - }) - .collect::<Result<Vec<_>, _>>()?, - }, - }) - } +fn bytes_to_privkey( + bytes: &[u8], +) -> Result<talpid_types::net::wireguard::PrivateKey, FromProtobufTypeError> { + Ok(talpid_types::net::wireguard::PrivateKey::from( + *bytes_to_wg_key(bytes, "invalid private key")?, + )) } -impl TryFrom<proto::TransportPort> for mullvad_types::relay_constraints::TransportPort { - type Error = FromProtobufTypeError; - - fn try_from(port: proto::TransportPort) -> Result<Self, Self::Error> { - Ok(mullvad_types::relay_constraints::TransportPort { - protocol: try_transport_protocol_from_i32(port.protocol)?, - port: if port.port == 0 { - Constraint::Any - } else { - Constraint::Only(port.port as u16) - }, - }) - } -} - -fn try_tunnel_type_from_i32( - tunnel_type: i32, -) -> Result<talpid_types::net::TunnelType, FromProtobufTypeError> { - match proto::TunnelType::from_i32(tunnel_type) { - Some(proto::TunnelType::Openvpn) => Ok(talpid_types::net::TunnelType::OpenVpn), - Some(proto::TunnelType::Wireguard) => Ok(talpid_types::net::TunnelType::Wireguard), - None => Err(FromProtobufTypeError::InvalidArgument( - "invalid tunnel protocol", - )), - } +fn bytes_to_wg_key<'a>( + bytes: &'a [u8], + error_msg: &'static str, +) -> Result<&'a [u8; 32], FromProtobufTypeError> { + <&[u8; 32]>::try_from(bytes).map_err(|_| FromProtobufTypeError::InvalidArgument(error_msg)) } /// Returns `Option<String>`, where an empty string represents `None`. @@ -1902,103 +54,6 @@ fn arg_from_str<T: FromStr<Err = E>, E>( T::from_str(s).map_err(|_err| FromProtobufTypeError::InvalidArgument(invalid_arg_msg)) } -fn try_transport_protocol_from_i32( - protocol: i32, -) -> Result<talpid_types::net::TransportProtocol, FromProtobufTypeError> { - Ok(proto::TransportProtocol::from_i32(protocol) - .ok_or(FromProtobufTypeError::InvalidArgument( - "invalid transport protocol", - ))? - .into()) -} - -#[cfg_attr(not(target_os = "windows"), allow(unused_variables))] -fn try_firewall_policy_error_from_i32( - policy_error: i32, - lock_pid: u32, - lock_name: String, -) -> Result<talpid_types::tunnel::FirewallPolicyError, FromProtobufTypeError> { - match proto::error_state::firewall_policy_error::ErrorType::from_i32(policy_error) { - Some(proto::error_state::firewall_policy_error::ErrorType::Generic) => { - Ok(talpid_types::tunnel::FirewallPolicyError::Generic) - } - #[cfg(windows)] - Some(proto::error_state::firewall_policy_error::ErrorType::Locked) => { - let blocking_app = option_from_proto_string(lock_name).map(|name| { - talpid_types::tunnel::BlockingApplication { - pid: lock_pid, - name, - } - }); - Ok(talpid_types::tunnel::FirewallPolicyError::Locked( - blocking_app, - )) - } - _ => Err(FromProtobufTypeError::InvalidArgument( - "invalid firewall policy error", - )), - } -} - -pub fn try_providers_constraint_from_proto( - providers: &[String], -) -> Result<Constraint<mullvad_types::relay_constraints::Providers>, FromProtobufTypeError> { - if !providers.is_empty() { - Ok(Constraint::Only( - mullvad_types::relay_constraints::Providers::new(providers.iter().cloned()).map_err( - |_| FromProtobufTypeError::InvalidArgument("must specify at least one provider"), - )?, - )) - } else { - Ok(Constraint::Any) - } -} - -pub fn try_ownership_constraint_from_i32( - ownership: i32, -) -> Result<Constraint<mullvad_types::relay_constraints::Ownership>, FromProtobufTypeError> { - proto::Ownership::from_i32(ownership) - .map(ownership_constraint_from_proto) - .ok_or(FromProtobufTypeError::InvalidArgument( - "invalid ownership argument", - )) -} - -pub fn ownership_constraint_from_proto( - ownership: proto::Ownership, -) -> Constraint<mullvad_types::relay_constraints::Ownership> { - use mullvad_types::relay_constraints::Ownership as MullvadOwnership; - - match ownership { - proto::Ownership::Any => Constraint::Any, - proto::Ownership::MullvadOwned => Constraint::Only(MullvadOwnership::MullvadOwned), - proto::Ownership::Rented => Constraint::Only(MullvadOwnership::Rented), - } -} - -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()), - } -} - -fn convert_ownership_constraint( - ownership: &Constraint<mullvad_types::relay_constraints::Ownership>, -) -> proto::Ownership { - use mullvad_types::relay_constraints::Ownership as MullvadOwnership; - - match ownership.as_ref() { - Constraint::Any => proto::Ownership::Any, - Constraint::Only(ownership) => match ownership { - MullvadOwnership::MullvadOwned => proto::Ownership::MullvadOwned, - MullvadOwnership::Rented => proto::Ownership::Rented, - }, - } -} - impl From<FromProtobufTypeError> for crate::Status { fn from(err: FromProtobufTypeError) -> Self { match err { diff --git a/mullvad-management-interface/src/types/conversions/net.rs b/mullvad-management-interface/src/types/conversions/net.rs new file mode 100644 index 0000000000..96210c02e3 --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/net.rs @@ -0,0 +1,191 @@ +use crate::types::{conversions::arg_from_str, proto, FromProtobufTypeError}; +use mullvad_types::relay_constraints::Constraint; +use std::net::SocketAddr; + +impl From<talpid_types::net::TunnelEndpoint> for proto::TunnelEndpoint { + fn from(endpoint: talpid_types::net::TunnelEndpoint) -> Self { + use talpid_types::net; + + proto::TunnelEndpoint { + address: endpoint.endpoint.address.to_string(), + protocol: i32::from(proto::TransportProtocol::from(endpoint.endpoint.protocol)), + tunnel_type: match endpoint.tunnel_type { + net::TunnelType::Wireguard => i32::from(proto::TunnelType::Wireguard), + net::TunnelType::OpenVpn => i32::from(proto::TunnelType::Openvpn), + }, + quantum_resistant: endpoint.quantum_resistant, + proxy: endpoint.proxy.map(|proxy_ep| proto::ProxyEndpoint { + address: proxy_ep.endpoint.address.to_string(), + protocol: i32::from(proto::TransportProtocol::from(proxy_ep.endpoint.protocol)), + proxy_type: match proxy_ep.proxy_type { + net::proxy::ProxyType::Shadowsocks => i32::from(proto::ProxyType::Shadowsocks), + net::proxy::ProxyType::Custom => i32::from(proto::ProxyType::Custom), + }, + }), + obfuscation: endpoint.obfuscation.map(|obfuscation_endpoint| { + proto::ObfuscationEndpoint { + address: obfuscation_endpoint.endpoint.address.ip().to_string(), + port: u32::from(obfuscation_endpoint.endpoint.address.port()), + protocol: i32::from(proto::TransportProtocol::from( + obfuscation_endpoint.endpoint.protocol, + )), + obfuscation_type: match obfuscation_endpoint.obfuscation_type { + net::ObfuscationType::Udp2Tcp => i32::from(proto::ObfuscationType::Udp2tcp), + }, + } + }), + entry_endpoint: endpoint.entry_endpoint.map(|entry| proto::Endpoint { + address: entry.address.to_string(), + protocol: i32::from(proto::TransportProtocol::from(entry.protocol)), + }), + } + } +} + +impl TryFrom<proto::TunnelEndpoint> for talpid_types::net::TunnelEndpoint { + type Error = FromProtobufTypeError; + + fn try_from(endpoint: proto::TunnelEndpoint) -> Result<Self, Self::Error> { + use talpid_types::net as talpid_net; + + Ok(talpid_net::TunnelEndpoint { + endpoint: talpid_net::Endpoint { + address: arg_from_str(&endpoint.address, "invalid endpoint address")?, + protocol: try_transport_protocol_from_i32(endpoint.protocol)?, + }, + tunnel_type: try_tunnel_type_from_i32(endpoint.tunnel_type)?, + quantum_resistant: endpoint.quantum_resistant, + proxy: endpoint + .proxy + .map(|proxy_ep| { + Ok(talpid_net::proxy::ProxyEndpoint { + endpoint: talpid_net::Endpoint { + address: arg_from_str( + &proxy_ep.address, + "invalid proxy endpoint address", + )?, + protocol: try_transport_protocol_from_i32(proxy_ep.protocol)?, + }, + proxy_type: match proto::ProxyType::from_i32(proxy_ep.proxy_type) { + Some(proto::ProxyType::Shadowsocks) => { + talpid_net::proxy::ProxyType::Shadowsocks + } + Some(proto::ProxyType::Custom) => talpid_net::proxy::ProxyType::Custom, + None => { + return Err(FromProtobufTypeError::InvalidArgument( + "unknown proxy type", + )) + } + }, + }) + }) + .transpose()?, + obfuscation: endpoint + .obfuscation + .map(|obfs_ep| { + Ok(talpid_net::ObfuscationEndpoint { + endpoint: talpid_net::Endpoint { + address: SocketAddr::new( + arg_from_str( + &obfs_ep.address, + "invalid obfuscation endpoint address", + )?, + obfs_ep.port as u16, + ), + protocol: try_transport_protocol_from_i32(obfs_ep.protocol)?, + }, + obfuscation_type: match proto::ObfuscationType::from_i32( + obfs_ep.obfuscation_type, + ) { + Some(proto::ObfuscationType::Udp2tcp) => { + talpid_net::ObfuscationType::Udp2Tcp + } + None => { + return Err(FromProtobufTypeError::InvalidArgument( + "unknown obfuscation type", + )) + } + }, + }) + }) + .transpose()?, + entry_endpoint: endpoint + .entry_endpoint + .map(|entry| { + Ok(talpid_net::Endpoint { + address: arg_from_str(&entry.address, "invalid entry endpoint address")?, + protocol: try_transport_protocol_from_i32(entry.protocol)?, + }) + }) + .transpose()?, + }) + } +} + +impl From<talpid_types::net::TransportProtocol> for proto::TransportProtocol { + fn from(protocol: talpid_types::net::TransportProtocol) -> Self { + match protocol { + talpid_types::net::TransportProtocol::Udp => proto::TransportProtocol::Udp, + talpid_types::net::TransportProtocol::Tcp => proto::TransportProtocol::Tcp, + } + } +} + +impl From<talpid_types::net::IpVersion> for proto::IpVersion { + fn from(version: talpid_types::net::IpVersion) -> Self { + match version { + talpid_types::net::IpVersion::V4 => proto::IpVersion::V4, + talpid_types::net::IpVersion::V6 => proto::IpVersion::V6, + } + } +} + +impl From<proto::TransportProtocol> for talpid_types::net::TransportProtocol { + fn from(protocol: proto::TransportProtocol) -> Self { + match protocol { + proto::TransportProtocol::Udp => talpid_types::net::TransportProtocol::Udp, + proto::TransportProtocol::Tcp => talpid_types::net::TransportProtocol::Tcp, + } + } +} + +impl TryFrom<proto::TunnelTypeConstraint> for Constraint<talpid_types::net::TunnelType> { + type Error = FromProtobufTypeError; + + fn try_from( + tunnel_type: proto::TunnelTypeConstraint, + ) -> Result<Constraint<talpid_types::net::TunnelType>, Self::Error> { + let tunnel_type = try_tunnel_type_from_i32(tunnel_type.tunnel_type)?; + Ok(Constraint::Only(tunnel_type)) + } +} + +impl From<proto::IpVersion> for proto::IpVersionConstraint { + fn from(version: proto::IpVersion) -> Self { + Self { + protocol: i32::from(version), + } + } +} + +pub fn try_tunnel_type_from_i32( + tunnel_type: i32, +) -> Result<talpid_types::net::TunnelType, FromProtobufTypeError> { + match proto::TunnelType::from_i32(tunnel_type) { + Some(proto::TunnelType::Openvpn) => Ok(talpid_types::net::TunnelType::OpenVpn), + Some(proto::TunnelType::Wireguard) => Ok(talpid_types::net::TunnelType::Wireguard), + None => Err(FromProtobufTypeError::InvalidArgument( + "invalid tunnel protocol", + )), + } +} + +pub fn try_transport_protocol_from_i32( + protocol: i32, +) -> Result<talpid_types::net::TransportProtocol, FromProtobufTypeError> { + Ok(proto::TransportProtocol::from_i32(protocol) + .ok_or(FromProtobufTypeError::InvalidArgument( + "invalid transport protocol", + ))? + .into()) +} diff --git a/mullvad-management-interface/src/types/conversions/relay_constraints.rs b/mullvad-management-interface/src/types/conversions/relay_constraints.rs new file mode 100644 index 0000000000..d76006bf7f --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/relay_constraints.rs @@ -0,0 +1,679 @@ +use crate::types::{conversions::option_from_proto_string, proto, FromProtobufTypeError}; +use mullvad_types::relay_constraints::Constraint; + +impl TryFrom<&proto::WireguardConstraints> + for mullvad_types::relay_constraints::WireguardConstraints +{ + type Error = FromProtobufTypeError; + + fn try_from( + constraints: &proto::WireguardConstraints, + ) -> Result<mullvad_types::relay_constraints::WireguardConstraints, Self::Error> { + use mullvad_types::relay_constraints as mullvad_constraints; + use talpid_types::net; + + let ip_version = match &constraints.ip_version { + Some(constraint) => match proto::IpVersion::from_i32(constraint.protocol) { + Some(proto::IpVersion::V4) => Some(net::IpVersion::V4), + Some(proto::IpVersion::V6) => Some(net::IpVersion::V6), + None => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid ip protocol version", + )) + } + }, + None => None, + }; + + Ok(mullvad_constraints::WireguardConstraints { + port: if constraints.port == 0 { + Constraint::Any + } else { + Constraint::Only(constraints.port as u16) + }, + ip_version: Constraint::from(ip_version), + use_multihop: constraints.use_multihop, + entry_location: constraints + .entry_location + .clone() + .map(Constraint::<mullvad_types::relay_constraints::LocationConstraint>::from) + .unwrap_or(Constraint::Any), + }) + } +} + +impl TryFrom<&proto::OpenvpnConstraints> for mullvad_types::relay_constraints::OpenVpnConstraints { + type Error = FromProtobufTypeError; + + fn try_from( + constraints: &proto::OpenvpnConstraints, + ) -> Result<mullvad_types::relay_constraints::OpenVpnConstraints, Self::Error> { + use mullvad_types::relay_constraints as mullvad_constraints; + + Ok(mullvad_constraints::OpenVpnConstraints { + port: Constraint::from(match &constraints.port { + Some(port) => Some(mullvad_constraints::TransportPort::try_from(port.clone())?), + None => None, + }), + }) + } +} + +impl TryFrom<proto::RelaySettings> for mullvad_types::relay_constraints::RelaySettings { + type Error = FromProtobufTypeError; + + fn try_from( + settings: proto::RelaySettings, + ) -> Result<mullvad_types::relay_constraints::RelaySettings, Self::Error> { + use mullvad_types::{relay_constraints as mullvad_constraints, CustomTunnelEndpoint}; + use talpid_types::net; + + let update_value = settings + .endpoint + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing relay settings", + ))?; + + match update_value { + proto::relay_settings::Endpoint::Custom(settings) => { + let config = settings + .config + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing relay connection config", + ))?; + let config = mullvad_types::ConnectionConfig::try_from(config)?; + Ok(mullvad_constraints::RelaySettings::CustomTunnelEndpoint( + CustomTunnelEndpoint { + host: settings.host, + config, + }, + )) + } + + proto::relay_settings::Endpoint::Normal(settings) => { + let location = settings + .location + .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) + .transpose()? + .unwrap_or(Constraint::Any); + let openvpn_constraints = + mullvad_constraints::OpenVpnConstraints::try_from( + &settings.openvpn_constraints.ok_or( + FromProtobufTypeError::InvalidArgument("missing openvpn constraints"), + )?, + )?; + let wireguard_constraints = mullvad_constraints::WireguardConstraints::try_from( + &settings.wireguard_constraints.ok_or( + FromProtobufTypeError::InvalidArgument("missing wireguard constraints"), + )?, + )?; + + Ok(mullvad_constraints::RelaySettings::Normal( + mullvad_constraints::RelayConstraints { + location, + providers, + ownership, + tunnel_protocol, + wireguard_constraints, + openvpn_constraints, + }, + )) + } + } + } +} + +impl TryFrom<proto::RelaySettingsUpdate> for mullvad_types::relay_constraints::RelaySettingsUpdate { + type Error = FromProtobufTypeError; + + fn try_from( + settings: proto::RelaySettingsUpdate, + ) -> Result<mullvad_types::relay_constraints::RelaySettingsUpdate, Self::Error> { + use mullvad_types::{relay_constraints as mullvad_constraints, CustomTunnelEndpoint}; + use talpid_types::net; + + let update_value = settings + .r#type + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing relay settings", + ))?; + + match update_value { + proto::relay_settings_update::Type::Custom(settings) => { + let config = settings + .config + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing relay connection config", + ))?; + let config = mullvad_types::ConnectionConfig::try_from(config)?; + Ok( + mullvad_constraints::RelaySettingsUpdate::CustomTunnelEndpoint( + CustomTunnelEndpoint { + host: settings.host, + config, + }, + ), + ) + } + + proto::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 providers = if let Some(ref provider_update) = settings.providers { + Some(try_providers_constraint_from_proto( + &provider_update.providers, + )?) + } 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 + .tunnel_type + .map(Constraint::<net::TunnelType>::try_from) + .transpose()? + .unwrap_or(Constraint::Any), + ) + } else { + None + }; + let openvpn_constraints = + if let Some(ref constraints) = settings.openvpn_constraints { + Some(mullvad_constraints::OpenVpnConstraints::try_from( + constraints, + )?) + } else { + None + }; + let wireguard_constraints = + if let Some(ref constraints) = settings.wireguard_constraints { + Some(mullvad_constraints::WireguardConstraints::try_from( + constraints, + )?) + } else { + None + }; + Ok(mullvad_constraints::RelaySettingsUpdate::Normal( + mullvad_constraints::RelayConstraintsUpdate { + location, + providers, + ownership, + tunnel_protocol, + wireguard_constraints, + openvpn_constraints, + }, + )) + } + } + } +} + +impl From<mullvad_types::relay_constraints::BridgeState> for proto::BridgeState { + fn from(state: mullvad_types::relay_constraints::BridgeState) -> Self { + use mullvad_types::relay_constraints::BridgeState; + Self { + state: i32::from(match state { + BridgeState::Auto => proto::bridge_state::State::Auto, + BridgeState::On => proto::bridge_state::State::On, + BridgeState::Off => proto::bridge_state::State::Off, + }), + } + } +} + +impl From<&mullvad_types::relay_constraints::ObfuscationSettings> for proto::ObfuscationSettings { + fn from(settings: &mullvad_types::relay_constraints::ObfuscationSettings) -> Self { + use mullvad_types::relay_constraints::SelectedObfuscation; + let selected_obfuscation = i32::from(match settings.selected_obfuscation { + SelectedObfuscation::Auto => proto::obfuscation_settings::SelectedObfuscation::Auto, + SelectedObfuscation::Off => proto::obfuscation_settings::SelectedObfuscation::Off, + SelectedObfuscation::Udp2Tcp => { + proto::obfuscation_settings::SelectedObfuscation::Udp2tcp + } + }); + Self { + selected_obfuscation, + udp2tcp: Some(proto::Udp2TcpObfuscationSettings::from(&settings.udp2tcp)), + } + } +} + +impl From<&mullvad_types::relay_constraints::Udp2TcpObfuscationSettings> + for proto::Udp2TcpObfuscationSettings +{ + fn from(settings: &mullvad_types::relay_constraints::Udp2TcpObfuscationSettings) -> Self { + Self { + port: u32::from(settings.port.unwrap_or(0)), + } + } +} + +impl From<mullvad_types::relay_constraints::BridgeSettings> for proto::BridgeSettings { + fn from(settings: mullvad_types::relay_constraints::BridgeSettings) -> Self { + use mullvad_types::relay_constraints::BridgeSettings as MullvadBridgeSettings; + use proto::bridge_settings; + 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(proto::RelayLocation::from), + providers: convert_providers_constraint(&constraints.providers), + ownership: convert_ownership_constraint(&constraints.ownership) as i32, + }) + } + 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, + }) + } + }, + }; + + proto::BridgeSettings { + r#type: Some(settings), + } + } +} + +impl From<mullvad_types::relay_constraints::RelaySettings> for proto::RelaySettings { + fn from(settings: mullvad_types::relay_constraints::RelaySettings) -> Self { + use mullvad_types::relay_constraints::RelaySettings as MullvadRelaySettings; + use proto::relay_settings; + use talpid_types::net as talpid_net; + + let endpoint = match settings { + MullvadRelaySettings::CustomTunnelEndpoint(endpoint) => { + relay_settings::Endpoint::Custom(proto::CustomRelaySettings { + host: endpoint.host, + config: Some(proto::ConnectionConfig::from(endpoint.config)), + }) + } + MullvadRelaySettings::Normal(constraints) => { + relay_settings::Endpoint::Normal(proto::NormalRelaySettings { + location: constraints + .location + .option() + .map(proto::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) => { + Some(proto::TunnelType::Wireguard) + } + Constraint::Only(talpid_net::TunnelType::OpenVpn) => { + Some(proto::TunnelType::Openvpn) + } + } + .map(|tunnel_type| proto::TunnelTypeConstraint { + tunnel_type: i32::from(tunnel_type), + }), + + wireguard_constraints: Some(proto::WireguardConstraints { + port: u32::from(constraints.wireguard_constraints.port.unwrap_or(0)), + ip_version: constraints + .wireguard_constraints + .ip_version + .option() + .map(proto::IpVersion::from) + .map(proto::IpVersionConstraint::from), + use_multihop: constraints.wireguard_constraints.use_multihop, + entry_location: constraints + .wireguard_constraints + .entry_location + .option() + .map(proto::RelayLocation::from), + }), + + openvpn_constraints: Some(proto::OpenvpnConstraints { + port: constraints + .openvpn_constraints + .port + .option() + .map(proto::TransportPort::from), + }), + }) + } + }; + + Self { + endpoint: Some(endpoint), + } + } +} + +impl From<mullvad_types::relay_constraints::TransportPort> for proto::TransportPort { + fn from(port: mullvad_types::relay_constraints::TransportPort) -> Self { + proto::TransportPort { + protocol: proto::TransportProtocol::from(port.protocol) as i32, + port: port.port.map(u32::from).unwrap_or(0), + } + } +} + +impl + From< + mullvad_types::relay_constraints::Constraint< + mullvad_types::relay_constraints::LocationConstraint, + >, + > for proto::RelayLocation +{ + fn from( + location: mullvad_types::relay_constraints::Constraint< + mullvad_types::relay_constraints::LocationConstraint, + >, + ) -> Self { + location + .option() + .map(proto::RelayLocation::from) + .unwrap_or_default() + } +} + +impl From<mullvad_types::relay_constraints::LocationConstraint> for proto::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<proto::RelayLocation> + for Constraint<mullvad_types::relay_constraints::LocationConstraint> +{ + fn from(location: proto::RelayLocation) -> Self { + use mullvad_types::relay_constraints::LocationConstraint; + + if let Some(hostname) = option_from_proto_string(location.hostname) { + Constraint::Only(LocationConstraint::Hostname( + location.country, + location.city, + hostname, + )) + } else if let Some(city) = option_from_proto_string(location.city) { + Constraint::Only(LocationConstraint::City(location.country, city)) + } else if let Some(country) = option_from_proto_string(location.country) { + Constraint::Only(LocationConstraint::Country(country)) + } else { + Constraint::Any + } + } +} + +impl TryFrom<proto::BridgeSettings> for mullvad_types::relay_constraints::BridgeSettings { + type Error = FromProtobufTypeError; + + fn try_from(settings: proto::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", + ))? { + proto::bridge_settings::Type::Normal(constraints) => { + let location = match constraints.location { + None => Constraint::Any, + Some(location) => { + Constraint::<mullvad_constraints::LocationConstraint>::from(location) + } + }; + 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, + }, + )) + } + proto::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)) + } + proto::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)) + } + proto::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<proto::ObfuscationSettings> for mullvad_types::relay_constraints::ObfuscationSettings { + type Error = FromProtobufTypeError; + + fn try_from(settings: proto::ObfuscationSettings) -> Result<Self, Self::Error> { + use mullvad_types::relay_constraints::SelectedObfuscation; + use proto::obfuscation_settings::SelectedObfuscation as IpcSelectedObfuscation; + let selected_obfuscation = + match IpcSelectedObfuscation::from_i32(settings.selected_obfuscation) { + Some(IpcSelectedObfuscation::Auto) => SelectedObfuscation::Auto, + Some(IpcSelectedObfuscation::Off) => SelectedObfuscation::Off, + Some(IpcSelectedObfuscation::Udp2tcp) => SelectedObfuscation::Udp2Tcp, + None => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid selected obfuscator", + )); + } + }; + + let udp2tcp = match settings.udp2tcp { + Some(settings) => { + mullvad_types::relay_constraints::Udp2TcpObfuscationSettings::try_from(&settings)? + } + None => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid selected obfuscator", + )); + } + }; + + Ok(Self { + selected_obfuscation, + udp2tcp, + }) + } +} + +impl TryFrom<&proto::Udp2TcpObfuscationSettings> + for mullvad_types::relay_constraints::Udp2TcpObfuscationSettings +{ + type Error = FromProtobufTypeError; + + fn try_from(settings: &proto::Udp2TcpObfuscationSettings) -> Result<Self, Self::Error> { + Ok(Self { + port: if settings.port == 0 { + Constraint::Any + } else { + Constraint::Only(settings.port as u16) + }, + }) + } +} + +impl TryFrom<proto::BridgeState> for mullvad_types::relay_constraints::BridgeState { + type Error = FromProtobufTypeError; + + fn try_from(state: proto::BridgeState) -> Result<Self, Self::Error> { + match proto::bridge_state::State::from_i32(state.state) { + Some(proto::bridge_state::State::Auto) => { + Ok(mullvad_types::relay_constraints::BridgeState::Auto) + } + Some(proto::bridge_state::State::On) => { + Ok(mullvad_types::relay_constraints::BridgeState::On) + } + Some(proto::bridge_state::State::Off) => { + Ok(mullvad_types::relay_constraints::BridgeState::Off) + } + None => Err(FromProtobufTypeError::InvalidArgument( + "invalid bridge state", + )), + } + } +} + +impl TryFrom<proto::TransportPort> for mullvad_types::relay_constraints::TransportPort { + type Error = FromProtobufTypeError; + + fn try_from(port: proto::TransportPort) -> Result<Self, Self::Error> { + Ok(mullvad_types::relay_constraints::TransportPort { + protocol: super::net::try_transport_protocol_from_i32(port.protocol)?, + port: if port.port == 0 { + Constraint::Any + } else { + Constraint::Only(port.port as u16) + }, + }) + } +} + +pub fn try_providers_constraint_from_proto( + providers: &[String], +) -> Result<Constraint<mullvad_types::relay_constraints::Providers>, FromProtobufTypeError> { + if !providers.is_empty() { + Ok(Constraint::Only( + mullvad_types::relay_constraints::Providers::new(providers.iter().cloned()).map_err( + |_| FromProtobufTypeError::InvalidArgument("must specify at least one provider"), + )?, + )) + } else { + Ok(Constraint::Any) + } +} + +pub fn try_ownership_constraint_from_i32( + ownership: i32, +) -> Result<Constraint<mullvad_types::relay_constraints::Ownership>, FromProtobufTypeError> { + proto::Ownership::from_i32(ownership) + .map(ownership_constraint_from_proto) + .ok_or(FromProtobufTypeError::InvalidArgument( + "invalid ownership argument", + )) +} + +pub fn ownership_constraint_from_proto( + ownership: proto::Ownership, +) -> Constraint<mullvad_types::relay_constraints::Ownership> { + use mullvad_types::relay_constraints::Ownership as MullvadOwnership; + + match ownership { + proto::Ownership::Any => Constraint::Any, + proto::Ownership::MullvadOwned => Constraint::Only(MullvadOwnership::MullvadOwned), + proto::Ownership::Rented => Constraint::Only(MullvadOwnership::Rented), + } +} + +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()), + } +} + +fn convert_ownership_constraint( + ownership: &Constraint<mullvad_types::relay_constraints::Ownership>, +) -> proto::Ownership { + use mullvad_types::relay_constraints::Ownership as MullvadOwnership; + + match ownership.as_ref() { + Constraint::Any => proto::Ownership::Any, + Constraint::Only(ownership) => match ownership { + MullvadOwnership::MullvadOwned => proto::Ownership::MullvadOwned, + MullvadOwnership::Rented => proto::Ownership::Rented, + }, + } +} diff --git a/mullvad-management-interface/src/types/conversions/relay_list.rs b/mullvad-management-interface/src/types/conversions/relay_list.rs new file mode 100644 index 0000000000..23f628ad9b --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/relay_list.rs @@ -0,0 +1,205 @@ +use crate::types::{ + conversions::{bytes_to_pubkey, option_from_proto_string, to_proto_any, try_from_proto_any}, + proto, FromProtobufTypeError, +}; + +impl From<mullvad_types::relay_list::RelayList> for proto::RelayList { + fn from(relay_list: mullvad_types::relay_list::RelayList) -> Self { + let mut proto_list = proto::RelayList { + countries: vec![], + openvpn: Some(proto::OpenVpnEndpointData::from(relay_list.openvpn)), + bridge: Some(proto::BridgeEndpointData::from(relay_list.bridge)), + wireguard: Some(proto::WireguardEndpointData::from(relay_list.wireguard)), + }; + proto_list.countries = relay_list + .countries + .into_iter() + .map(proto::RelayListCountry::from) + .collect(); + proto_list + } +} + +impl From<mullvad_types::relay_list::OpenVpnEndpointData> for proto::OpenVpnEndpointData { + fn from(openvpn: mullvad_types::relay_list::OpenVpnEndpointData) -> Self { + proto::OpenVpnEndpointData { + endpoints: openvpn + .ports + .into_iter() + .map(|endpoint| proto::OpenVpnEndpoint { + port: u32::from(endpoint.port), + protocol: proto::TransportProtocol::from(endpoint.protocol) as i32, + }) + .collect(), + } + } +} + +impl From<mullvad_types::relay_list::BridgeEndpointData> for proto::BridgeEndpointData { + fn from(bridge: mullvad_types::relay_list::BridgeEndpointData) -> Self { + proto::BridgeEndpointData { + shadowsocks: bridge + .shadowsocks + .into_iter() + .map(|endpoint| proto::ShadowsocksEndpointData { + port: u32::from(endpoint.port), + cipher: endpoint.cipher, + password: endpoint.password, + protocol: proto::TransportProtocol::from(endpoint.protocol) as i32, + }) + .collect(), + } + } +} + +impl From<mullvad_types::relay_list::WireguardEndpointData> for proto::WireguardEndpointData { + fn from(wireguard: mullvad_types::relay_list::WireguardEndpointData) -> Self { + proto::WireguardEndpointData { + port_ranges: wireguard + .port_ranges + .into_iter() + .map(|(first, last)| proto::PortRange { + first: u32::from(first), + last: u32::from(last), + }) + .collect(), + ipv4_gateway: wireguard.ipv4_gateway.to_string(), + ipv6_gateway: wireguard.ipv6_gateway.to_string(), + udp2tcp_ports: wireguard.udp2tcp_ports.into_iter().map(u32::from).collect(), + } + } +} + +impl From<mullvad_types::relay_list::RelayListCountry> for proto::RelayListCountry { + fn from(country: mullvad_types::relay_list::RelayListCountry) -> Self { + let mut proto_country = proto::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(proto::RelayListCity { + name: city.name, + code: city.code, + latitude: city.latitude, + longitude: city.longitude, + relays: city.relays.into_iter().map(proto::Relay::from).collect(), + }); + } + + proto_country + } +} + +impl From<mullvad_types::relay_list::Relay> for proto::Relay { + fn from(relay: mullvad_types::relay_list::Relay) -> Self { + use mullvad_types::relay_list::RelayEndpointData as MullvadEndpointData; + + Self { + hostname: relay.hostname, + ipv4_addr_in: relay.ipv4_addr_in.to_string(), + 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, + endpoint_type: match &relay.endpoint_data { + MullvadEndpointData::Openvpn => proto::relay::RelayType::Openvpn as i32, + MullvadEndpointData::Bridge => proto::relay::RelayType::Bridge as i32, + MullvadEndpointData::Wireguard(_) => proto::relay::RelayType::Wireguard as i32, + }, + endpoint_data: match relay.endpoint_data { + MullvadEndpointData::Wireguard(data) => Some(to_proto_any( + "mullvad_daemon.management_interface/WireguardRelayEndpointData", + proto::WireguardRelayEndpointData { + public_key: data.public_key.as_bytes().to_vec(), + }, + )), + _ => None, + }, + location: relay.location.map(|location| proto::Location { + country: location.country, + country_code: location.country_code, + city: location.city, + city_code: location.city_code, + latitude: location.latitude, + longitude: location.longitude, + }), + } + } +} + +impl TryFrom<proto::Relay> for mullvad_types::relay_list::Relay { + type Error = FromProtobufTypeError; + + fn try_from(relay: proto::Relay) -> Result<Self, Self::Error> { + use mullvad_types::{ + location::Location as MullvadLocation, + relay_list::{Relay as MullvadRelay, RelayEndpointData as MullvadEndpointData}, + }; + + let endpoint_data = match relay.endpoint_type { + i if i == proto::relay::RelayType::Openvpn as i32 => MullvadEndpointData::Openvpn, + i if i == proto::relay::RelayType::Bridge as i32 => MullvadEndpointData::Bridge, + i if i == proto::relay::RelayType::Wireguard as i32 => { + let data = relay + .endpoint_data + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing endpoint wg data", + ))?; + let data: proto::WireguardRelayEndpointData = try_from_proto_any( + "mullvad_daemon.management_interface/WireguardRelayEndpointData", + data, + ) + .ok_or(FromProtobufTypeError::InvalidArgument( + "invalid endpoint wg data", + ))?; + MullvadEndpointData::Wireguard( + mullvad_types::relay_list::WireguardRelayEndpointData { + public_key: bytes_to_pubkey(&data.public_key)?, + }, + ) + } + _ => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid relay endpoint type", + )) + } + }; + + let ipv6_addr_in = option_from_proto_string(relay.ipv6_addr_in) + .map(|addr| { + addr.parse().map_err(|_err| { + FromProtobufTypeError::InvalidArgument("invalid relay IPv6 address") + }) + }) + .transpose()?; + + Ok(MullvadRelay { + hostname: relay.hostname, + ipv4_addr_in: relay.ipv4_addr_in.parse().map_err(|_err| { + FromProtobufTypeError::InvalidArgument("invalid relay IPv4 address") + })?, + ipv6_addr_in, + include_in_country: relay.include_in_country, + active: relay.active, + owned: relay.owned, + provider: relay.provider, + weight: relay.weight, + endpoint_data, + location: relay.location.map(|location| MullvadLocation { + country: location.country, + country_code: location.country_code, + city: location.city, + city_code: location.city_code, + latitude: location.latitude, + longitude: location.longitude, + }), + }) + } +} diff --git a/mullvad-management-interface/src/types/conversions/settings.rs b/mullvad-management-interface/src/types/conversions/settings.rs new file mode 100644 index 0000000000..60805d5004 --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/settings.rs @@ -0,0 +1,227 @@ +use crate::types::{proto, FromProtobufTypeError}; +use talpid_types::ErrorExt; + +impl From<&mullvad_types::settings::Settings> for proto::Settings { + fn from(settings: &mullvad_types::settings::Settings) -> Self { + #[cfg(windows)] + let split_tunnel = { + let mut converted_list = vec![]; + for path in settings.split_tunnel.apps.clone().iter() { + match path.as_path().as_os_str().to_str() { + Some(path) => converted_list.push(path.to_string()), + None => { + log::error!("failed to convert OS string: {:?}", path); + } + } + } + + Some(proto::SplitTunnelSettings { + enable_exclusions: settings.split_tunnel.enable_exclusions, + apps: converted_list, + }) + }; + #[cfg(not(windows))] + let split_tunnel = None; + + Self { + relay_settings: Some(proto::RelaySettings::from(settings.get_relay_settings())), + bridge_settings: Some(proto::BridgeSettings::from( + settings.bridge_settings.clone(), + )), + bridge_state: Some(proto::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(proto::TunnelOptions::from(&settings.tunnel_options)), + show_beta_releases: settings.show_beta_releases, + obfuscation_settings: Some(proto::ObfuscationSettings::from( + &settings.obfuscation_settings, + )), + split_tunnel, + } + } +} + +impl From<&mullvad_types::settings::DnsOptions> for proto::DnsOptions { + fn from(options: &mullvad_types::settings::DnsOptions) -> Self { + use proto::dns_options; + + proto::DnsOptions { + state: match options.state { + mullvad_types::settings::DnsState::Default => dns_options::DnsState::Default as i32, + mullvad_types::settings::DnsState::Custom => dns_options::DnsState::Custom as i32, + }, + default_options: Some(proto::DefaultDnsOptions { + block_ads: options.default_options.block_ads, + block_trackers: options.default_options.block_trackers, + block_malware: options.default_options.block_malware, + block_adult_content: options.default_options.block_adult_content, + block_gambling: options.default_options.block_gambling, + }), + custom_options: Some(proto::CustomDnsOptions { + addresses: options + .custom_options + .addresses + .iter() + .map(|addr| addr.to_string()) + .collect(), + }), + } + } +} + +impl From<&mullvad_types::settings::TunnelOptions> for proto::TunnelOptions { + fn from(options: &mullvad_types::settings::TunnelOptions) -> Self { + Self { + openvpn: Some(proto::tunnel_options::OpenvpnOptions { + mssfix: u32::from(options.openvpn.mssfix.unwrap_or_default()), + }), + wireguard: Some(proto::tunnel_options::WireguardOptions { + mtu: u32::from(options.wireguard.options.mtu.unwrap_or_default()), + rotation_interval: options.wireguard.rotation_interval.map(|ivl| { + prost_types::Duration::try_from(std::time::Duration::from(ivl)) + .expect("Failed to convert std::time::Duration to prost_types::Duration for tunnel_options.wireguard.rotation_interval") + }), + #[cfg(windows)] + use_wireguard_nt: options.wireguard.options.use_wireguard_nt, + #[cfg(not(windows))] + use_wireguard_nt: false, + use_pq_safe_psk: options.wireguard.options.use_pq_safe_psk, + }), + generic: Some(proto::tunnel_options::GenericOptions { + enable_ipv6: options.generic.enable_ipv6, + }), + #[cfg(not(target_os = "android"))] + dns_options: Some(proto::DnsOptions::from(&options.dns_options)), + #[cfg(target_os = "android")] + dns_options: None, + } + } +} + +impl TryFrom<proto::TunnelOptions> for mullvad_types::settings::TunnelOptions { + type Error = FromProtobufTypeError; + + fn try_from(options: proto::TunnelOptions) -> Result<Self, Self::Error> { + use talpid_types::net; + + let openvpn_options = options + .openvpn + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing openvpn tunnel options", + ))?; + let wireguard_options = options + .wireguard + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing openvpn tunnel options", + ))?; + let generic_options = options + .generic + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing generic tunnel options", + ))?; + let dns_options = options + .dns_options + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing tunnel DNS options", + ))?; + + Ok(Self { + openvpn: net::openvpn::TunnelOptions { + mssfix: if openvpn_options.mssfix != 0 { + Some(openvpn_options.mssfix as u16) + } else { + None + }, + }, + wireguard: mullvad_types::wireguard::TunnelOptions { + options: net::wireguard::TunnelOptions { + mtu: if wireguard_options.mtu != 0 { + Some(wireguard_options.mtu as u16) + } else { + None + }, + use_pq_safe_psk: wireguard_options.use_pq_safe_psk, + #[cfg(windows)] + use_wireguard_nt: wireguard_options.use_wireguard_nt, + }, + rotation_interval: wireguard_options + .rotation_interval + .map(std::time::Duration::try_from) + .transpose() + .map_err(|_| FromProtobufTypeError::InvalidArgument("invalid duration"))? + .map(mullvad_types::wireguard::RotationInterval::try_from) + .transpose() + .map_err(|error: mullvad_types::wireguard::RotationIntervalError| { + log::error!( + "{}", + error.display_chain_with_msg("Invalid rotation interval") + ); + FromProtobufTypeError::InvalidArgument("invalid rotation interval") + })?, + }, + generic: net::GenericTunnelOptions { + enable_ipv6: generic_options.enable_ipv6, + }, + #[cfg(not(target_os = "android"))] + dns_options: mullvad_types::settings::DnsOptions::try_from(dns_options)?, + }) + } +} + +impl TryFrom<proto::DnsOptions> for mullvad_types::settings::DnsOptions { + type Error = FromProtobufTypeError; + + fn try_from(options: proto::DnsOptions) -> Result<Self, Self::Error> { + use mullvad_types::settings::{ + CustomDnsOptions as MullvadCustomDnsOptions, + DefaultDnsOptions as MullvadDefaultDnsOptions, DnsOptions as MullvadDnsOptions, + DnsState as MullvadDnsState, + }; + + let state = match proto::dns_options::DnsState::from_i32(options.state) { + Some(proto::dns_options::DnsState::Default) => MullvadDnsState::Default, + Some(proto::dns_options::DnsState::Custom) => MullvadDnsState::Custom, + None => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid DNS options state", + )) + } + }; + + let default_options = + options + .default_options + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing default DNS options", + ))?; + let custom_options = + options + .custom_options + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing default DNS options", + ))?; + + Ok(MullvadDnsOptions { + state, + default_options: MullvadDefaultDnsOptions { + block_ads: default_options.block_ads, + block_trackers: default_options.block_trackers, + block_malware: default_options.block_malware, + block_adult_content: default_options.block_adult_content, + block_gambling: default_options.block_gambling, + }, + custom_options: MullvadCustomDnsOptions { + addresses: custom_options + .addresses + .into_iter() + .map(|addr| { + addr.parse().map_err(|_| { + FromProtobufTypeError::InvalidArgument("invalid IP address") + }) + }) + .collect::<Result<Vec<_>, _>>()?, + }, + }) + } +} diff --git a/mullvad-management-interface/src/types/conversions/states.rs b/mullvad-management-interface/src/types/conversions/states.rs new file mode 100644 index 0000000000..c462e77aa6 --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/states.rs @@ -0,0 +1,318 @@ +use crate::types::{conversions::option_from_proto_string, proto, FromProtobufTypeError}; + +impl From<mullvad_types::states::TunnelState> for proto::TunnelState { + fn from(state: mullvad_types::states::TunnelState) -> Self { + use mullvad_types::states::TunnelState as MullvadTunnelState; + use proto::error_state::{ + firewall_policy_error::ErrorType as PolicyErrorType, Cause, FirewallPolicyError, + GenerationError, + }; + + 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 => { + proto::tunnel_state::State::Disconnected(proto::tunnel_state::Disconnected {}) + } + MullvadTunnelState::Connecting { endpoint, location } => { + proto::tunnel_state::State::Connecting(proto::tunnel_state::Connecting { + relay_info: Some(proto::TunnelStateRelayInfo { + tunnel_endpoint: Some(proto::TunnelEndpoint::from(endpoint)), + location: location.map(proto::GeoIpLocation::from), + }), + }) + } + MullvadTunnelState::Connected { endpoint, location } => { + proto::tunnel_state::State::Connected(proto::tunnel_state::Connected { + relay_info: Some(proto::TunnelStateRelayInfo { + tunnel_endpoint: Some(proto::TunnelEndpoint::from(endpoint)), + location: location.map(proto::GeoIpLocation::from), + }), + }) + } + MullvadTunnelState::Disconnecting(after_disconnect) => { + proto::tunnel_state::State::Disconnecting(proto::tunnel_state::Disconnecting { + after_disconnect: match after_disconnect { + talpid_tunnel::ActionAfterDisconnect::Nothing => { + i32::from(proto::AfterDisconnect::Nothing) + } + talpid_tunnel::ActionAfterDisconnect::Block => { + i32::from(proto::AfterDisconnect::Block) + } + talpid_tunnel::ActionAfterDisconnect::Reconnect => { + i32::from(proto::AfterDisconnect::Reconnect) + } + }, + }) + } + MullvadTunnelState::Error(error_state) => { + proto::tunnel_state::State::Error(proto::tunnel_state::Error { + error_state: Some(proto::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) + } + #[cfg(target_os = "windows")] + talpid_tunnel::ErrorStateCause::SplitTunnelError => { + i32::from(Cause::SplitTunnelError) + } + }, + 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 + }, + }), + }) + } + }; + + proto::TunnelState { state: Some(state) } + } +} + +impl TryFrom<proto::TunnelState> for mullvad_types::states::TunnelState { + type Error = FromProtobufTypeError; + + fn try_from(state: proto::TunnelState) -> Result<Self, FromProtobufTypeError> { + use mullvad_types::states::TunnelState as MullvadState; + use talpid_types::{net as talpid_net, tunnel as talpid_tunnel}; + + let state = match state.state { + Some(proto::tunnel_state::State::Disconnected(_)) => MullvadState::Disconnected, + Some(proto::tunnel_state::State::Connecting(proto::tunnel_state::Connecting { + relay_info: + Some(proto::TunnelStateRelayInfo { + tunnel_endpoint: Some(tunnel_endpoint), + location, + }), + })) => MullvadState::Connecting { + endpoint: talpid_net::TunnelEndpoint::try_from(tunnel_endpoint)?, + location: location + .map(mullvad_types::location::GeoIpLocation::try_from) + .transpose()?, + }, + Some(proto::tunnel_state::State::Connected(proto::tunnel_state::Connected { + relay_info: + Some(proto::TunnelStateRelayInfo { + tunnel_endpoint: Some(tunnel_endpoint), + location, + }), + })) => MullvadState::Connected { + endpoint: talpid_net::TunnelEndpoint::try_from(tunnel_endpoint)?, + location: location + .map(mullvad_types::location::GeoIpLocation::try_from) + .transpose()?, + }, + Some(proto::tunnel_state::State::Disconnecting( + proto::tunnel_state::Disconnecting { after_disconnect }, + )) => MullvadState::Disconnecting( + match proto::AfterDisconnect::from_i32(after_disconnect) { + Some(proto::AfterDisconnect::Nothing) => { + talpid_tunnel::ActionAfterDisconnect::Nothing + } + Some(proto::AfterDisconnect::Block) => { + talpid_tunnel::ActionAfterDisconnect::Block + } + Some(proto::AfterDisconnect::Reconnect) => { + talpid_tunnel::ActionAfterDisconnect::Reconnect + } + _ => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid \"after_disconnect\" action", + )) + } + }, + ), + Some(proto::tunnel_state::State::Error(proto::tunnel_state::Error { + error_state: + Some(proto::ErrorState { + cause, + blocking_error, + auth_fail_reason, + parameter_error, + policy_error, + }), + })) => { + let cause = match proto::error_state::Cause::from_i32(cause) { + Some(proto::error_state::Cause::AuthFailed) => { + talpid_tunnel::ErrorStateCause::AuthFailed(option_from_proto_string( + auth_fail_reason, + )) + } + Some(proto::error_state::Cause::Ipv6Unavailable) => { + talpid_tunnel::ErrorStateCause::Ipv6Unavailable + } + Some(proto::error_state::Cause::IsOffline) => { + talpid_tunnel::ErrorStateCause::IsOffline + } + Some(proto::error_state::Cause::SetDnsError) => { + talpid_tunnel::ErrorStateCause::SetDnsError + } + Some(proto::error_state::Cause::SetFirewallPolicyError) => { + let policy_error = policy_error.ok_or( + FromProtobufTypeError::InvalidArgument("missing firewall policy error"), + )?; + let policy_error = try_firewall_policy_error_from_i32( + policy_error.r#type, + policy_error.lock_pid, + policy_error.lock_name, + )?; + talpid_tunnel::ErrorStateCause::SetFirewallPolicyError(policy_error) + } + Some(proto::error_state::Cause::StartTunnelError) => { + talpid_tunnel::ErrorStateCause::StartTunnelError + } + Some(proto::error_state::Cause::TunnelParameterError) => { + let parameter_error = match proto::error_state::GenerationError::from_i32(parameter_error) { + Some(proto::error_state::GenerationError::CustomTunnelHostResolutionError) => talpid_tunnel::ParameterGenerationError::CustomTunnelHostResultionError, + Some(proto::error_state::GenerationError::NoMatchingBridgeRelay) => talpid_tunnel::ParameterGenerationError::NoMatchingBridgeRelay, + Some(proto::error_state::GenerationError::NoMatchingRelay) => talpid_tunnel::ParameterGenerationError::NoMatchingRelay, + Some(proto::error_state::GenerationError::NoWireguardKey) => talpid_tunnel::ParameterGenerationError::NoWireguardKey, + _ => return Err(FromProtobufTypeError::InvalidArgument( + "invalid parameter error", + )), + }; + talpid_tunnel::ErrorStateCause::TunnelParameterError(parameter_error) + } + #[cfg(target_os = "android")] + Some(proto::error_state::Cause::VpnPermissionDenied) => { + talpid_tunnel::ErrorStateCause::VpnPermissionDenied + } + #[cfg(target_os = "windows")] + Some(proto::error_state::Cause::SplitTunnelError) => { + talpid_tunnel::ErrorStateCause::SplitTunnelError + } + _ => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid error cause", + )) + } + }; + + let block_failure = blocking_error + .map(|blocking_error| { + try_firewall_policy_error_from_i32( + blocking_error.r#type, + blocking_error.lock_pid, + blocking_error.lock_name, + ) + }) + .transpose()?; + + MullvadState::Error(talpid_tunnel::ErrorState::new(cause, block_failure)) + } + _ => { + return Err(FromProtobufTypeError::InvalidArgument( + "invalid tunnel state", + )) + } + }; + + Ok(state) + } +} + +#[cfg_attr(not(target_os = "windows"), allow(unused_variables))] +fn try_firewall_policy_error_from_i32( + policy_error: i32, + lock_pid: u32, + lock_name: String, +) -> Result<talpid_types::tunnel::FirewallPolicyError, FromProtobufTypeError> { + match proto::error_state::firewall_policy_error::ErrorType::from_i32(policy_error) { + Some(proto::error_state::firewall_policy_error::ErrorType::Generic) => { + Ok(talpid_types::tunnel::FirewallPolicyError::Generic) + } + #[cfg(windows)] + Some(proto::error_state::firewall_policy_error::ErrorType::Locked) => { + let blocking_app = option_from_proto_string(lock_name).map(|name| { + talpid_types::tunnel::BlockingApplication { + pid: lock_pid, + name, + } + }); + Ok(talpid_types::tunnel::FirewallPolicyError::Locked( + blocking_app, + )) + } + _ => Err(FromProtobufTypeError::InvalidArgument( + "invalid firewall policy error", + )), + } +} diff --git a/mullvad-management-interface/src/types/conversions/version.rs b/mullvad-management-interface/src/types/conversions/version.rs new file mode 100644 index 0000000000..0a21667695 --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/version.rs @@ -0,0 +1,12 @@ +use crate::types::proto; + +impl From<mullvad_types::version::AppVersionInfo> for proto::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(), + } + } +} diff --git a/mullvad-management-interface/src/types/conversions/wireguard.rs b/mullvad-management-interface/src/types/conversions/wireguard.rs new file mode 100644 index 0000000000..e90e728c2f --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/wireguard.rs @@ -0,0 +1,14 @@ +use crate::types::proto; +use prost_types::Timestamp; + +impl From<mullvad_types::wireguard::PublicKey> for proto::PublicKey { + fn from(public_key: mullvad_types::wireguard::PublicKey) -> Self { + proto::PublicKey { + key: public_key.key.as_bytes().to_vec(), + created: Some(Timestamp { + seconds: public_key.created.timestamp(), + nanos: 0, + }), + } + } +} |
