diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2023-09-11 15:52:15 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2023-10-09 14:40:03 +0200 |
| commit | 2db6e2ecc64c0ddb9dba0b0947a31b0fbefe9f53 (patch) | |
| tree | 943d1a981f74d9fbe792eb5932532e247bcd668c | |
| parent | c899c1b63858c12c9367318c19120fe899b31394 (diff) | |
| download | mullvadvpn-2db6e2ecc64c0ddb9dba0b0947a31b0fbefe9f53.tar.xz mullvadvpn-2db6e2ecc64c0ddb9dba0b0947a31b0fbefe9f53.zip | |
Code cleanup
- Add a new datastructures for distinguishing between built-in & custom
api access methods
- Implement `TryFrom` instead of `From` for fallible conversions
- Do not panic if a protobuf-message is ill-formatted
- Do not allow removal of built-in api access methods
- Refactor notification logic in `access_methods.rs`
- Rename `mullvad proxy api` to simply `mullvad proxy`
- Since there are no other kinds of proxies at the moment, the
subcommand `proxy api` does not make much sense.
- Remove left-over comments
| -rw-r--r-- | mullvad-api/src/https_client_with_sni.rs | 13 | ||||
| -rw-r--r-- | mullvad-api/src/proxy.rs | 23 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/proxy.rs | 84 | ||||
| -rw-r--r-- | mullvad-daemon/src/access_methods.rs | 50 | ||||
| -rw-r--r-- | mullvad-daemon/src/api.rs | 69 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 6 | ||||
| -rw-r--r-- | mullvad-management-interface/proto/management_interface.proto | 8 | ||||
| -rw-r--r-- | mullvad-management-interface/src/client.rs | 11 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/api_access_method.rs | 186 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/settings.rs | 4 | ||||
| -rw-r--r-- | mullvad-types/src/api_access_method.rs | 102 | ||||
| -rw-r--r-- | mullvad-types/src/settings/mod.rs | 10 |
12 files changed, 362 insertions, 204 deletions
diff --git a/mullvad-api/src/https_client_with_sni.rs b/mullvad-api/src/https_client_with_sni.rs index 940bb249d4..5bffa3d32b 100644 --- a/mullvad-api/src/https_client_with_sni.rs +++ b/mullvad-api/src/https_client_with_sni.rs @@ -146,8 +146,6 @@ impl InnerConnectionMode { } /// Set up a SOCKS5-socket connection. - /// - /// TODO: Handle case where the proxy-address is `localhost`. async fn handle_socks_connection( proxy_config: SocksConfig, addr: &SocketAddr, @@ -223,9 +221,14 @@ impl TryFrom<ApiConnectionMode> for InnerConnectionMode { proxy_context: SsContext::new_shared(ServerType::Local), }) } - ProxyConfig::Socks(config) => { - InnerConnectionMode::Socks5(SocksConfig { peer: config.peer }) - } + ProxyConfig::Socks(config) => match config { + mullvad_types::api_access_method::Socks5::Local(config) => { + InnerConnectionMode::Socks5(SocksConfig { peer: config.peer }) + } + mullvad_types::api_access_method::Socks5::Remote(config) => { + InnerConnectionMode::Socks5(SocksConfig { peer: config.peer }) + } + }, }, }) } diff --git a/mullvad-api/src/proxy.rs b/mullvad-api/src/proxy.rs index c5c2426933..112a747f04 100644 --- a/mullvad-api/src/proxy.rs +++ b/mullvad-api/src/proxy.rs @@ -1,5 +1,6 @@ use futures::Stream; use hyper::client::connect::Connected; +use mullvad_types::api_access_method; use serde::{Deserialize, Serialize}; use std::{ fmt, io, @@ -8,9 +9,7 @@ use std::{ pin::Pin, task::{self, Poll}, }; -use talpid_types::{ - net::openvpn::ShadowsocksProxySettings, net::openvpn::SocksProxySettings, ErrorExt, -}; +use talpid_types::ErrorExt; use tokio::{ fs, io::{AsyncRead, AsyncWrite, AsyncWriteExt, ReadBuf}, @@ -18,7 +17,7 @@ use tokio::{ const CURRENT_CONFIG_FILENAME: &str = "api-endpoint.json"; -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub enum ApiConnectionMode { /// Connect directly to the target. Direct, @@ -35,10 +34,10 @@ impl fmt::Display for ApiConnectionMode { } } -#[derive(Clone, Debug, PartialEq, Eq, Deserialize, Serialize)] +#[derive(Clone, Debug, PartialEq, Deserialize, Serialize)] pub enum ProxyConfig { - Shadowsocks(ShadowsocksProxySettings), - Socks(SocksProxySettings), + Shadowsocks(api_access_method::Shadowsocks), + Socks(api_access_method::Socks5), } impl fmt::Display for ProxyConfig { @@ -46,7 +45,10 @@ impl fmt::Display for ProxyConfig { match self { // TODO: Do not hardcode TCP ProxyConfig::Shadowsocks(ss) => write!(f, "Shadowsocks {}/TCP", ss.peer), - ProxyConfig::Socks(s) => write!(f, "Socks5 {}/TCP", s.peer), + ProxyConfig::Socks(socks) => match socks { + api_access_method::Socks5::Local(s) => write!(f, "Socks5 {}/TCP", s.peer), + api_access_method::Socks5::Remote(s) => write!(f, "Socks5 {}/TCP", s.peer), + }, } } } @@ -117,7 +119,10 @@ impl ApiConnectionMode { ApiConnectionMode::Direct => None, ApiConnectionMode::Proxied(proxy_config) => match proxy_config { ProxyConfig::Shadowsocks(ss) => Some(ss.peer), - ProxyConfig::Socks(s) => Some(s.peer), + ProxyConfig::Socks(socks) => match socks { + api_access_method::Socks5::Local(s) => Some(s.peer), + api_access_method::Socks5::Remote(s) => Some(s.peer), + }, }, } } diff --git a/mullvad-cli/src/cmds/proxy.rs b/mullvad-cli/src/cmds/proxy.rs index 5a4bf2fdd8..4826a571af 100644 --- a/mullvad-cli/src/cmds/proxy.rs +++ b/mullvad-cli/src/cmds/proxy.rs @@ -1,41 +1,47 @@ use anyhow::{anyhow, Result}; use mullvad_management_interface::MullvadProxyClient; -use mullvad_types::api_access_method::{AccessMethod, ApiAccessMethodReplace}; +use mullvad_types::api_access_method::{ + daemon::ApiAccessMethodReplace, AccessMethod, ObfuscationProtocol, +}; use std::net::IpAddr; use clap::{Args, Subcommand}; use talpid_types::net::openvpn::SHADOWSOCKS_CIPHERS; -#[derive(Subcommand, Debug)] +#[derive(Subcommand, Debug, Clone)] pub enum Proxy { - /// Get current api settings + /// List the configured API proxies + List, + /// Add a custom API proxy #[clap(subcommand)] - Api(ApiCommands), + Add(AddCustomCommands), + /// Edit an API proxy + Edit(EditCustomCommands), + /// Remove an API proxy + Remove(RemoveCustomCommands), } impl Proxy { pub async fn handle(self) -> Result<()> { match self { - Proxy::Api(cmd) => match cmd { - ApiCommands::List => { - //println!("Listing the API access methods: .."); - Self::list().await?; - } - ApiCommands::Add(cmd) => { - //println!("Adding custom proxy"); - Self::add(cmd).await?; - } - ApiCommands::Edit(cmd) => { - // Transform human-readable index to 0-based indexing. - let index = Self::zero_to_one_based_index(cmd.index)?; - Self::edit(EditCustomCommands { index, ..cmd }).await? - } - ApiCommands::Remove(cmd) => { - // Transform human-readable index to 0-based indexing. - let index = Self::zero_to_one_based_index(cmd.index)?; - Self::remove(RemoveCustomCommands { index }).await? - } - }, + Proxy::List => { + //println!("Listing the API access methods: .."); + Self::list().await?; + } + Proxy::Add(cmd) => { + //println!("Adding custom proxy"); + Self::add(cmd).await?; + } + Proxy::Edit(cmd) => { + // Transform human-readable index to 0-based indexing. + let index = Self::zero_to_one_based_index(cmd.index)?; + Self::edit(EditCustomCommands { index, ..cmd }).await? + } + Proxy::Remove(cmd) => { + // Transform human-readable index to 0-based indexing. + let index = Self::zero_to_one_based_index(cmd.index)?; + Self::remove(RemoveCustomCommands { index }).await? + } }; Ok(()) } @@ -88,8 +94,13 @@ impl Proxy { .clone(); // Create a new access method combining the new params with the previous values - let edited_access_method: AccessMethod = match access_method { - AccessMethod::Shadowsocks(shadowsocks) => { + let access_method = match access_method { + AccessMethod::BuiltIn(_) => Err(anyhow!("Can not edit built-in access method")), + AccessMethod::Custom(custom_access_method) => Ok(custom_access_method), + }?; + + let edited_access_method: AccessMethod = match access_method.access_method { + ObfuscationProtocol::Shadowsocks(shadowsocks) => { let ip = cmd.params.ip.unwrap_or(shadowsocks.peer.ip()).to_string(); let port = cmd.params.port.unwrap_or(shadowsocks.peer.port()); let password = cmd.params.password.unwrap_or(shadowsocks.password); @@ -97,7 +108,7 @@ impl Proxy { mullvad_types::api_access_method::Shadowsocks::from_args(ip, port, cipher, password) .map(|x| x.into()) } - AccessMethod::Socks5(socks) => match socks { + ObfuscationProtocol::Socks5(socks) => match socks { mullvad_types::api_access_method::Socks5::Local(local) => { let ip = cmd.params.ip.unwrap_or(local.peer.ip()).to_string(); let port = cmd.params.port.unwrap_or(local.peer.port()); @@ -135,19 +146,6 @@ impl Proxy { } #[derive(Subcommand, Debug, Clone)] -pub enum ApiCommands { - /// List the configured API proxies - List, - /// Add a custom API proxy - #[clap(subcommand)] - Add(AddCustomCommands), - /// Edit an API proxy - Edit(EditCustomCommands), - /// Remove an API proxy - Remove(RemoveCustomCommands), -} - -#[derive(Subcommand, Debug, Clone)] pub enum AddCustomCommands { /// Configure SOCKS5 proxy #[clap(subcommand)] @@ -262,7 +260,7 @@ mod conversions { ) .ok_or(anyhow!("Could not create a local Socks5 api proxy"))?, ); - daemon_types::AccessMethod::Socks5(socks_proxy) + socks_proxy.into() } Socks5AddCommands::Remote { remote_ip, @@ -278,7 +276,7 @@ mod conversions { ) .ok_or(anyhow!("Could not create a remote Socks5 api proxy"))?, ); - daemon_types::AccessMethod::Socks5(socks_proxy) + socks_proxy.into() } }, AddCustomCommands::Shadowsocks { @@ -297,7 +295,7 @@ mod conversions { password, ) .ok_or(anyhow!("Could not create a Shadowsocks api proxy"))?; - daemon_types::AccessMethod::Shadowsocks(shadowsocks_proxy) + shadowsocks_proxy.into() } }) } diff --git a/mullvad-daemon/src/access_methods.rs b/mullvad-daemon/src/access_methods.rs index e778cd2eeb..dbbf628f60 100644 --- a/mullvad-daemon/src/access_methods.rs +++ b/mullvad-daemon/src/access_methods.rs @@ -1,5 +1,10 @@ -use crate::{new_selector_config, settings, Daemon, EventListener}; -use mullvad_types::api_access_method::{AccessMethod, ApiAccessMethodReplace}; +use crate::{ + settings::{self, MadeChanges}, + Daemon, EventListener, +}; +use mullvad_types::api_access_method::{ + daemon::ApiAccessMethodReplace, AccessMethod, CustomAccessMethod, +}; #[derive(err_derive::Error, Debug)] pub enum Error { @@ -24,18 +29,15 @@ where .push(access_method); }) .await - .map(|changed| { - if changed { - self.event_listener - .notify_settings(self.settings.to_settings()); - self.relay_selector - .set_config(new_selector_config(&self.settings)); - }; - }) + .map(|did_change| self.notify_on_change(did_change)) .map_err(Error::Settings) } - pub async fn remove_access_method(&mut self, access_method: AccessMethod) -> Result<(), Error> { + pub async fn remove_access_method( + &mut self, + access_method: CustomAccessMethod, + ) -> Result<(), Error> { + let access_method = AccessMethod::from(access_method); self.settings .update(|settings| { settings @@ -44,14 +46,7 @@ where .retain(|x| *x != access_method); }) .await - .map(|changed| { - if changed { - self.event_listener - .notify_settings(self.settings.to_settings()); - self.relay_selector - .set_config(new_selector_config(&self.settings)); - }; - }) + .map(|did_change| self.notify_on_change(did_change)) .map_err(Error::Settings) } @@ -66,14 +61,15 @@ where access_methods.swap_remove(access_method_replace.index); }) .await - .map(|changed| { - if changed { - self.event_listener - .notify_settings(self.settings.to_settings()); - self.relay_selector - .set_config(new_selector_config(&self.settings)); - }; - }) + .map(|did_change| self.notify_on_change(did_change)) .map_err(Error::Settings) } + + /// If settings were changed due to an update, notify all listeners. + fn notify_on_change(&mut self, settings_changed: MadeChanges) { + if settings_changed { + self.event_listener + .notify_settings(self.settings.to_settings()); + }; + } } diff --git a/mullvad-daemon/src/api.rs b/mullvad-daemon/src/api.rs index 67f80ca235..18e77d5a28 100644 --- a/mullvad-daemon/src/api.rs +++ b/mullvad-daemon/src/api.rs @@ -10,6 +10,7 @@ use mullvad_api::{ ApiEndpointUpdateCallback, }; use mullvad_relay_selector::RelaySelector; +use mullvad_types::api_access_method; use std::{ net::SocketAddr, path::PathBuf, @@ -25,11 +26,12 @@ use talpid_types::{ ErrorExt, }; +/// TODO: Update this comment /// A stream that returns the next API connection mode to use for reaching the API. /// /// When `mullvad-api` fails to contact the API, it requests a new connection mode. /// The API can be connected to either directly (i.e., [`ApiConnectionMode::Direct`]) -/// or from a bridge ([`ApiConnectionMode::Proxied`]). +/// via a bridge ([`ApiConnectionMode::Proxied`]) or via any supported obfuscation protocol ([`ObfuscationProtocol`]). /// /// * Every 3rd attempt returns [`ApiConnectionMode::Direct`]. /// * Any other attempt returns a configuration for the bridge that is closest to the selected relay @@ -38,10 +40,9 @@ use talpid_types::{ /// bridge, [`ApiConnectionMode::Direct`] is returned. pub struct ApiConnectionModeProvider { cache_dir: PathBuf, - + /// Used for selecting a Relay when the `Bridges` access method is used. relay_selector: RelaySelector, retry_attempt: u32, - current_task: Option<Pin<Box<dyn Future<Output = ApiConnectionMode> + Send>>>, } @@ -63,35 +64,18 @@ impl Stream for ApiConnectionModeProvider { }; } - // Create a new task. - let config = if Self::should_use_bridge(self.retry_attempt) { - self.relay_selector - .get_bridge_forced() - .map(|settings| match settings { - ProxySettings::Shadowsocks(ss_settings) => { - ApiConnectionMode::Proxied(ProxyConfig::Shadowsocks(ss_settings)) - } - _ => { - log::error!("Received unexpected proxy settings type"); - ApiConnectionMode::Direct - } - }) - .unwrap_or(ApiConnectionMode::Direct) - } else { - ApiConnectionMode::Direct - }; - + let connection_mode = self.new_task(self.retry_attempt); self.retry_attempt = self.retry_attempt.wrapping_add(1); let cache_dir = self.cache_dir.clone(); self.current_task = Some(Box::pin(async move { - if let Err(error) = config.save(&cache_dir).await { + if let Err(error) = connection_mode.save(&cache_dir).await { log::debug!( "{}", error.display_chain_with_msg("Failed to save API endpoint") ); } - config + connection_mode })); self.poll_next(cx) @@ -113,6 +97,45 @@ impl ApiConnectionModeProvider { fn should_use_bridge(retry_attempt: u32) -> bool { retry_attempt % 3 > 0 } + + fn should_use_direct(retry_attempt: u32) -> bool { + // TODO: Change back before comitting! + false + // !Self::should_use_bridge(retry_attempt) + } + + /// Return a new connection mode to be used for the API connection. + /// + /// TODO: Figure out an appropriate algorithm for selecting between the available AccessMethods. + /// We need to be able to poll the daemon's settings to find out which access methods are available & active. + /// For now, we a + fn new_task(&self, retry_attempt: u32) -> ApiConnectionMode { + if Self::should_use_direct(retry_attempt) { + ApiConnectionMode::Direct + } else { + self.relay_selector + .get_bridge_forced() + .and_then(|settings| match settings { + ProxySettings::Shadowsocks(ss_settings) => { + let ss_settings: api_access_method::Shadowsocks = + api_access_method::Shadowsocks::new( + ss_settings.peer, + ss_settings.cipher, + ss_settings.password, + ); + println!("Using bridge mode to access the API! {:?}", ss_settings); + Some(ApiConnectionMode::Proxied(ProxyConfig::Shadowsocks( + ss_settings, + ))) + } + _ => { + log::error!("Received unexpected proxy settings type"); + None + } + }) + .unwrap_or(ApiConnectionMode::Direct) + } + } } /// Notifies the tunnel state machine that the API (real or proxied) endpoint has diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 9528aa1d5e..6e7b98d8ec 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -40,7 +40,7 @@ use mullvad_relay_selector::{ }; use mullvad_types::{ account::{AccountData, AccountToken, VoucherSubmission}, - api_access_method::{AccessMethod, ApiAccessMethodReplace}, + api_access_method::{daemon::ApiAccessMethodReplace, AccessMethod, CustomAccessMethod}, auth_failed::AuthFailed, custom_list::CustomList, device::{Device, DeviceEvent, DeviceEventCause, DeviceId, DeviceState, RemoveDeviceEvent}, @@ -265,7 +265,7 @@ pub enum DaemonCommand { /// Add API access methods AddApiAccessMethod(ResponseTx<(), Error>, AccessMethod), /// Remove an API access method - RemoveApiAccessMethod(ResponseTx<(), Error>, AccessMethod), + RemoveApiAccessMethod(ResponseTx<(), Error>, CustomAccessMethod), /// Edit an API access method ReplaceApiAccessMethod(ResponseTx<(), Error>, ApiAccessMethodReplace), /// Get information about the currently running and latest app versions @@ -2239,7 +2239,7 @@ where async fn on_remove_api_access_method( &mut self, tx: ResponseTx<(), Error>, - method: AccessMethod, + method: CustomAccessMethod, ) { let result = self .remove_access_method(method) diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index 2c1061a741..5e1aef6cea 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -337,6 +337,8 @@ message CustomList { message CustomListSettings { repeated CustomList custom_lists = 1; } message ApiAccessMethod { + message Direct {} + message Bridges {} message Socks5Local { string ip = 1; uint32 port = 2; @@ -359,8 +361,10 @@ message ApiAccessMethod { string cipher = 4; } oneof access_method { - Socks5 socks5 = 1; - Shadowsocks shadowsocks = 2; + Direct direct = 1; + Bridges bridges = 2; + Socks5 socks5 = 3; + Shadowsocks shadowsocks = 4; } } diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index 892f4ea5cd..15a5be1892 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -165,16 +165,17 @@ impl MullvadProxyClient { } pub async fn get_api_access_methods(&mut self) -> Result<Vec<AccessMethod>> { - Ok(self - .0 + self.0 .get_api_access_methods(()) .await .map_err(Error::Rpc)? .into_inner() .api_access_methods - .iter() - .map(From::from) - .collect()) + .into_iter() + .map(|access_method| { + AccessMethod::try_from(access_method).map_err(Error::InvalidResponse) + }) + .collect() } pub async fn update_relay_locations(&mut self) -> Result<()> { diff --git a/mullvad-management-interface/src/types/conversions/api_access_method.rs b/mullvad-management-interface/src/types/conversions/api_access_method.rs index b9217b88c9..d6d71e75a2 100644 --- a/mullvad-management-interface/src/types/conversions/api_access_method.rs +++ b/mullvad-management-interface/src/types/conversions/api_access_method.rs @@ -1,6 +1,6 @@ /// Implements conversions for the auxilliary proto AccessMethod type to the internal AccessMethod data type. mod settings { - use crate::types::proto; + use crate::types::{proto, FromProtobufTypeError}; use mullvad_types::api_access_method; impl From<&api_access_method::Settings> for proto::ApiAccessMethodSettings { @@ -21,20 +21,22 @@ mod settings { } } - impl From<proto::ApiAccessMethodSettings> for api_access_method::Settings { - fn from(settings: proto::ApiAccessMethodSettings) -> Self { - Self { + impl TryFrom<proto::ApiAccessMethodSettings> for api_access_method::Settings { + type Error = FromProtobufTypeError; + + fn try_from(settings: proto::ApiAccessMethodSettings) -> Result<Self, Self::Error> { + Ok(Self { api_access_methods: settings .api_access_methods .iter() - .map(api_access_method::AccessMethod::from) - .collect(), - } + .map(api_access_method::AccessMethod::try_from) + .collect::<Result<Vec<api_access_method::AccessMethod>, _>>()?, + }) } } - impl From<api_access_method::ApiAccessMethodReplace> for proto::ApiAccessMethodReplace { - fn from(value: api_access_method::ApiAccessMethodReplace) -> Self { + impl From<api_access_method::daemon::ApiAccessMethodReplace> for proto::ApiAccessMethodReplace { + fn from(value: api_access_method::daemon::ApiAccessMethodReplace) -> Self { proto::ApiAccessMethodReplace { index: value.index as u32, access_method: Some(value.access_method.into()), @@ -42,100 +44,142 @@ mod settings { } } - impl From<proto::ApiAccessMethodReplace> for api_access_method::ApiAccessMethodReplace { - // TODO: Implement `TryFrom` instead, and skip the `unwrap`. - fn from(value: proto::ApiAccessMethodReplace) -> Self { - api_access_method::ApiAccessMethodReplace { + impl TryFrom<proto::ApiAccessMethodReplace> for api_access_method::daemon::ApiAccessMethodReplace { + type Error = FromProtobufTypeError; + + fn try_from(value: proto::ApiAccessMethodReplace) -> Result<Self, Self::Error> { + Ok(api_access_method::daemon::ApiAccessMethodReplace { index: value.index as usize, - access_method: value.access_method.unwrap().into(), - } + access_method: value + .access_method + .ok_or(FromProtobufTypeError::InvalidArgument( + "Could not convert Access Method from protobuf", + )) + .and_then(TryInto::try_into)?, + }) } } } /// Implements conversions for the 'main' AccessMethod data type. mod data { - use crate::types::proto::{self, api_access_method::socks5::Socks5type}; + use crate::types::{ + proto::{self, api_access_method::socks5::Socks5type}, + FromProtobufTypeError, + }; use mullvad_types::api_access_method::{ - AccessMethod, Shadowsocks, Socks5, Socks5Local, Socks5Remote, + AccessMethod, BuiltInAccessMethod, ObfuscationProtocol, Shadowsocks, Socks5, Socks5Local, + Socks5Remote, }; - impl From<proto::ApiAccessMethods> for Vec<AccessMethod> { - fn from(api_access_methods: proto::ApiAccessMethods) -> Self { - api_access_methods + impl TryFrom<proto::ApiAccessMethods> for Vec<AccessMethod> { + type Error = FromProtobufTypeError; + + fn try_from(value: proto::ApiAccessMethods) -> Result<Self, Self::Error> { + value .api_access_methods .iter() - .map(AccessMethod::from) + .map(AccessMethod::try_from) .collect() } } - impl From<proto::ApiAccessMethod> for AccessMethod { - fn from(value: proto::ApiAccessMethod) -> Self { - // TODO: How to not unwrap? - match value.access_method.unwrap() { + impl TryFrom<proto::ApiAccessMethod> for AccessMethod { + type Error = FromProtobufTypeError; + + fn try_from(value: proto::ApiAccessMethod) -> Result<Self, Self::Error> { + let access_method = + value + .access_method + .ok_or(FromProtobufTypeError::InvalidArgument( + "Could not convert Access Method from protobuf", + ))?; + Ok(match access_method { proto::api_access_method::AccessMethod::Socks5(socks) => { match socks.socks5type.unwrap() { - Socks5type::Local(local) => { - let local_proxy = Socks5Local::from_args( - local.ip, - local.port as u16, - local.local_port as u16, - ) - .unwrap(); // This is dangerous territory .. - AccessMethod::Socks5(Socks5::Local(local_proxy)) - } + Socks5type::Local(local) => Socks5Local::from_args( + local.ip, + local.port as u16, + local.local_port as u16, + ) + .ok_or(FromProtobufTypeError::InvalidArgument( + "Could not parse Socks5 (local) message from protobuf", + ))? + .into(), Socks5type::Remote(remote) => { - let remote_proxy = - Socks5Remote::from_args(remote.ip, remote.port as u16).unwrap(); // This is dangerous territory .. - AccessMethod::Socks5(Socks5::Remote(remote_proxy)) + Socks5Remote::from_args(remote.ip, remote.port as u16) + .ok_or({ + FromProtobufTypeError::InvalidArgument( + "Could not parse Socks5 (remote) message from protobuf", + ) + })? + .into() } } } proto::api_access_method::AccessMethod::Shadowsocks(ss) => { - let shadow_sock = - Shadowsocks::from_args(ss.ip, ss.port as u16, ss.cipher, ss.password) - .unwrap(); - AccessMethod::Shadowsocks(shadow_sock) + Shadowsocks::from_args(ss.ip, ss.port as u16, ss.cipher, ss.password) + .ok_or(FromProtobufTypeError::InvalidArgument( + "Could not parse Shadowsocks message from protobuf", + ))? + .into() } - } + proto::api_access_method::AccessMethod::Direct(_) => { + BuiltInAccessMethod::Direct.into() + } + proto::api_access_method::AccessMethod::Bridges(_) => { + BuiltInAccessMethod::Bridge.into() + } + }) } } impl From<AccessMethod> for proto::ApiAccessMethod { fn from(value: AccessMethod) -> Self { match value { - AccessMethod::Shadowsocks(ss) => proto::api_access_method::Shadowsocks { - ip: ss.peer.ip().to_string(), - port: ss.peer.port() as u32, - password: ss.password, - cipher: ss.cipher, - } - .into(), + AccessMethod::Custom(value) => match value.access_method { + ObfuscationProtocol::Shadowsocks(ss) => proto::api_access_method::Shadowsocks { + ip: ss.peer.ip().to_string(), + port: ss.peer.port() as u32, + password: ss.password, + cipher: ss.cipher, + } + .into(), - AccessMethod::Socks5(Socks5::Local(Socks5Local { peer, port })) => { - proto::api_access_method::Socks5Local { - ip: peer.ip().to_string(), - port: peer.port() as u32, - local_port: port as u32, + ObfuscationProtocol::Socks5(Socks5::Local(Socks5Local { peer, port })) => { + proto::api_access_method::Socks5Local { + ip: peer.ip().to_string(), + port: peer.port() as u32, + local_port: port as u32, + } + .into() } - .into() - } - AccessMethod::Socks5(Socks5::Remote(Socks5Remote { peer })) => { - proto::api_access_method::Socks5Remote { - ip: peer.ip().to_string(), - port: peer.port() as u32, + ObfuscationProtocol::Socks5(Socks5::Remote(Socks5Remote { peer })) => { + proto::api_access_method::Socks5Remote { + ip: peer.ip().to_string(), + port: peer.port() as u32, + } + .into() } - .into() - } + }, + AccessMethod::BuiltIn(value) => match value { + mullvad_types::api_access_method::BuiltInAccessMethod::Direct => { + proto::api_access_method::Direct {}.into() + } + mullvad_types::api_access_method::BuiltInAccessMethod::Bridge => { + proto::api_access_method::Bridges {}.into() + } + }, } } } - impl From<&proto::ApiAccessMethod> for AccessMethod { - fn from(value: &proto::ApiAccessMethod) -> Self { - AccessMethod::from(value.clone()) + impl TryFrom<&proto::ApiAccessMethod> for AccessMethod { + type Error = FromProtobufTypeError; + + fn try_from(value: &proto::ApiAccessMethod) -> Result<Self, Self::Error> { + AccessMethod::try_from(value.clone()) } } @@ -180,6 +224,18 @@ mod data { } } + impl From<proto::api_access_method::Direct> for proto::ApiAccessMethod { + fn from(value: proto::api_access_method::Direct) -> Self { + proto::api_access_method::AccessMethod::Direct(value).into() + } + } + + impl From<proto::api_access_method::Bridges> for proto::ApiAccessMethod { + fn from(value: proto::api_access_method::Bridges) -> Self { + proto::api_access_method::AccessMethod::Bridges(value).into() + } + } + impl From<proto::api_access_method::AccessMethod> for proto::ApiAccessMethod { fn from(value: proto::api_access_method::AccessMethod) -> Self { proto::ApiAccessMethod { diff --git a/mullvad-management-interface/src/types/conversions/settings.rs b/mullvad-management-interface/src/types/conversions/settings.rs index d454af4cb0..57694d31f0 100644 --- a/mullvad-management-interface/src/types/conversions/settings.rs +++ b/mullvad-management-interface/src/types/conversions/settings.rs @@ -180,9 +180,9 @@ impl TryFrom<proto::Settings> for mullvad_types::settings::Settings { custom_lists: mullvad_types::custom_list::CustomListsSettings::try_from( custom_lists_settings, )?, - api_access_methods: mullvad_types::api_access_method::Settings::from( + api_access_methods: mullvad_types::api_access_method::Settings::try_from( api_access_methods_settings, - ), + )?, }) } } diff --git a/mullvad-types/src/api_access_method.rs b/mullvad-types/src/api_access_method.rs index 0cb417dda1..693d67d7dc 100644 --- a/mullvad-types/src/api_access_method.rs +++ b/mullvad-types/src/api_access_method.rs @@ -4,14 +4,45 @@ use serde::{Deserialize, Serialize}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; /// Daemon settings for API access methods. -#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] pub struct Settings { pub api_access_methods: Vec<AccessMethod>, } -/// API access method datastructure. +impl Default for Settings { + fn default() -> Self { + Self { + api_access_methods: vec![ + BuiltInAccessMethod::Direct.into(), + BuiltInAccessMethod::Bridge.into(), + ], + } + } +} + +/// Access method datastructure. #[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] pub enum AccessMethod { + BuiltIn(BuiltInAccessMethod), + Custom(CustomAccessMethod), +} + +/// Built-In access method datastructure. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum BuiltInAccessMethod { + Direct, + Bridge, +} + +/// Custom access method datastructure. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct CustomAccessMethod { + pub id: String, + pub access_method: ObfuscationProtocol, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum ObfuscationProtocol { Shadowsocks(Shadowsocks), Socks5(Socks5), } @@ -48,6 +79,23 @@ impl Settings { } } +impl AccessMethod { + pub fn is_custom(&self) -> bool { + matches!(self, AccessMethod::Custom(..)) + } + + pub fn is_builtin(&self) -> bool { + matches!(self, AccessMethod::BuiltIn(..)) + } + + pub fn as_custom(&self) -> Option<&CustomAccessMethod> { + match self { + AccessMethod::BuiltIn(_) => None, + AccessMethod::Custom(access_method) => Some(access_method), + } + } +} + impl Shadowsocks { pub fn new(peer: SocketAddr, cipher: String, password: String) -> Self { Shadowsocks { @@ -93,21 +141,49 @@ impl Socks5Remote { } } +impl From<BuiltInAccessMethod> for AccessMethod { + fn from(value: BuiltInAccessMethod) -> Self { + AccessMethod::BuiltIn(value) + } +} + +impl From<CustomAccessMethod> for AccessMethod { + fn from(value: CustomAccessMethod) -> Self { + AccessMethod::Custom(value) + } +} + +impl From<ObfuscationProtocol> for AccessMethod { + fn from(value: ObfuscationProtocol) -> Self { + CustomAccessMethod { + id: uuid::Uuid::new_v4().to_string(), + access_method: value, + } + .into() + } +} + impl From<Shadowsocks> for AccessMethod { fn from(value: Shadowsocks) -> Self { - AccessMethod::Shadowsocks(value) + ObfuscationProtocol::Shadowsocks(value).into() + } +} + +impl From<Socks5> for AccessMethod { + fn from(value: Socks5) -> Self { + ObfuscationProtocol::Socks5(value).into() } } impl From<Socks5Remote> for AccessMethod { fn from(value: Socks5Remote) -> Self { - AccessMethod::Socks5(value.into()) + Socks5::Remote(value).into() } } impl From<Socks5Local> for AccessMethod { fn from(value: Socks5Local) -> Self { - AccessMethod::Socks5(value.into()) + Socks5::Local(value).into() } } @@ -123,10 +199,14 @@ impl From<Socks5Local> for Socks5 { } } -/// TODO: Document why this is needed. -/// Hint: Argument to protobuf rpc `ApiAccessMethodReplace`. -#[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] -pub struct ApiAccessMethodReplace { - pub index: usize, - pub access_method: AccessMethod, +/// These are just extensions to the core [`AccessMethod`] datastructure which the mullvad daemon needs. +pub mod daemon { + use super::*; + /// TODO: Document why this is needed. + /// Hint: Argument to protobuf rpc `ApiAccessMethodReplace`. + #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] + pub struct ApiAccessMethodReplace { + pub index: usize, + pub access_method: AccessMethod, + } } diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs index fc66649b8a..311a154c28 100644 --- a/mullvad-types/src/settings/mod.rs +++ b/mullvad-types/src/settings/mod.rs @@ -140,15 +140,7 @@ impl Default for Settings { split_tunnel: SplitTunnelSettings::default(), settings_version: CURRENT_SETTINGS_VERSION, custom_lists: CustomListsSettings::default(), - //api_access_methods: api_access_method::Settings::default(), - // TODO: Remove this v in favor of this ^ when w can add or own access methods. - api_access_methods: { - use api_access_method::{AccessMethod, Shadowsocks}; - let mut xs = api_access_method::Settings::default(); - let mut ys = vec![]; - xs.api_access_methods.append(&mut ys); - xs - }, + api_access_methods: api_access_method::Settings::default(), } } } |
