summaryrefslogtreecommitdiffhomepage
diff options
context:
space:
mode:
authorMarkus Pettersson <markus.pettersson@mullvad.net>2023-09-11 15:52:15 +0200
committerDavid Lönnhager <david.l@mullvad.net>2023-10-09 14:40:03 +0200
commit2db6e2ecc64c0ddb9dba0b0947a31b0fbefe9f53 (patch)
tree943d1a981f74d9fbe792eb5932532e247bcd668c
parentc899c1b63858c12c9367318c19120fe899b31394 (diff)
downloadmullvadvpn-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.rs13
-rw-r--r--mullvad-api/src/proxy.rs23
-rw-r--r--mullvad-cli/src/cmds/proxy.rs84
-rw-r--r--mullvad-daemon/src/access_methods.rs50
-rw-r--r--mullvad-daemon/src/api.rs69
-rw-r--r--mullvad-daemon/src/lib.rs6
-rw-r--r--mullvad-management-interface/proto/management_interface.proto8
-rw-r--r--mullvad-management-interface/src/client.rs11
-rw-r--r--mullvad-management-interface/src/types/conversions/api_access_method.rs186
-rw-r--r--mullvad-management-interface/src/types/conversions/settings.rs4
-rw-r--r--mullvad-types/src/api_access_method.rs102
-rw-r--r--mullvad-types/src/settings/mod.rs10
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(),
}
}
}