diff options
| author | Markus Pettersson <markus.pettersson@mullvad.net> | 2023-08-24 16:18:06 +0200 |
|---|---|---|
| committer | David Lönnhager <david.l@mullvad.net> | 2023-10-09 14:40:02 +0200 |
| commit | 585a820373abea33e3338e97fb727028667968bb (patch) | |
| tree | 104cdb1c31ddd87dca5499c736b20d2e3ca3d160 | |
| parent | 44656b65922ee0b8c76db50a22f93e10158e8b59 (diff) | |
| download | mullvadvpn-585a820373abea33e3338e97fb727028667968bb.tar.xz mullvadvpn-585a820373abea33e3338e97fb727028667968bb.zip | |
Add `mullvad proxy add` command
Add daemon logic for storing custom access methods & allow a user to add
a custom socks5 or shadowsocks proxy.
Add all the necessary information for establishing Socks5
connections (both using a local Socks-proxy as well as the normal,
remote-proxy, use case) and Shadowsocks connections.
Add `api_access_settings` to `mullvad-daemon`
Naturally, the Protobuf types has to be mirrored on the Rust/daemon side
and lots of boilerplate code had to be written to convert between the two.
| -rw-r--r-- | mullvad-cli/src/cmds/proxy.rs | 126 | ||||
| -rw-r--r-- | mullvad-daemon/src/access_methods.rs | 37 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 24 | ||||
| -rw-r--r-- | mullvad-management-interface/proto/management_interface.proto | 39 | ||||
| -rw-r--r-- | mullvad-management-interface/src/client.rs | 22 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/api_access_method.rs | 171 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/mod.rs | 1 | ||||
| -rw-r--r-- | mullvad-management-interface/src/types/conversions/settings.rs | 12 | ||||
| -rw-r--r-- | mullvad-types/src/api_access_method.rs | 94 | ||||
| -rw-r--r-- | mullvad-types/src/lib.rs | 1 | ||||
| -rw-r--r-- | mullvad-types/src/settings/mod.rs | 13 |
11 files changed, 519 insertions, 21 deletions
diff --git a/mullvad-cli/src/cmds/proxy.rs b/mullvad-cli/src/cmds/proxy.rs index c12aee3683..fec4a6f966 100644 --- a/mullvad-cli/src/cmds/proxy.rs +++ b/mullvad-cli/src/cmds/proxy.rs @@ -1,5 +1,6 @@ use anyhow::Result; use mullvad_management_interface::MullvadProxyClient; +use mullvad_types::api_access_method::AccessMethod; use std::net::IpAddr; use clap::Subcommand; @@ -17,12 +18,13 @@ impl Proxy { match self { Proxy::Api(cmd) => match cmd { ApiCommands::List => { - println!("Listing the API access methods: .."); + //println!("Listing the API access methods: .."); Self::list().await?; } - ApiCommands::Add(cmd) => match cmd { - _ => println!("[NOT IMPEMENTLED YET] Adding custom proxy: {:?}", cmd), - }, + ApiCommands::Add(cmd) => { + //println!("Adding custom proxy"); + Self::add(cmd).await?; + } }, }; Ok(()) @@ -37,13 +39,20 @@ impl Proxy { } Ok(()) } + + /// Add a custom API access method. + async fn add(cmd: AddCustomCommands) -> Result<()> { + let mut rpc = MullvadProxyClient::new().await?; + let proxy = AccessMethod::try_from(cmd.clone())?; + rpc.add_access_method(proxy).await?; + Ok(()) + } } #[derive(Subcommand, Debug, Clone)] pub enum ApiCommands { /// List the configured API proxies List, - /// Add a custom API proxy #[clap(subcommand)] Add(AddCustomCommands), @@ -51,6 +60,28 @@ pub enum ApiCommands { #[derive(Subcommand, Debug, Clone)] pub enum AddCustomCommands { + /// Configure SOCKS5 proxy + #[clap(subcommand)] + Socks5(Socks5AddCommands), + + /// Configure bundled Shadowsocks proxy + Shadowsocks { + /// The IP of the remote Shadowsocks server + remote_ip: IpAddr, + /// The port of the remote Shadowsocks server + #[arg(default_value = "443")] + remote_port: u16, + /// Password for authentication + #[arg(default_value = "mullvad")] + password: String, + /// Cipher to use + #[arg(value_parser = SHADOWSOCKS_CIPHERS, default_value = "aes-256-gcm")] + cipher: String, + }, +} + +#[derive(Subcommand, Debug, Clone)] +pub enum Socks5AddCommands { /// Configure a local SOCKS5 proxy Local { /// The port that the server on localhost is listening on @@ -60,14 +91,12 @@ pub enum AddCustomCommands { /// The port of the remote peer remote_port: u16, }, - /// Configure a remote SOCKS5 proxy Remote { /// The IP of the remote proxy server remote_ip: IpAddr, /// The port of the remote proxy server remote_port: u16, - /// Username for authentication #[arg(requires = "password")] username: Option<String>, @@ -75,21 +104,76 @@ pub enum AddCustomCommands { #[arg(requires = "username")] password: Option<String>, }, +} - /// Configure bundled Shadowsocks proxy - Shadowsocks { - /// The IP of the remote Shadowsocks server - remote_ip: IpAddr, - /// The port of the remote Shadowsocks server - #[arg(default_value = "443")] - remote_port: u16, +/// Implement conversions from CLI types to Daemon types. +/// +/// Since these are not supposed to be used outside of the CLI, +/// we define them in a hidden-away module. +mod conversions { + use anyhow::{anyhow, Error}; + use mullvad_types::api_access_method as daemon_types; - /// Password for authentication - #[arg(default_value = "mullvad")] - password: String, + use super::{AddCustomCommands, Socks5AddCommands}; - /// Cipher to use - #[arg(value_parser = SHADOWSOCKS_CIPHERS, default_value = "aes-256-gcm")] - cipher: String, - }, + impl TryFrom<AddCustomCommands> for daemon_types::AccessMethod { + type Error = Error; + + fn try_from(value: AddCustomCommands) -> Result<Self, Self::Error> { + Ok(match value { + AddCustomCommands::Socks5(variant) => match variant { + Socks5AddCommands::Local { + local_port, + remote_ip, + remote_port, + } => { + println!("Adding LOCAL SOCKS5-proxy: localhost:{local_port} => {remote_ip}:{remote_port}"); + let socks_proxy = daemon_types::Socks5::Local( + daemon_types::Socks5Local::from_args( + remote_ip.to_string(), + remote_port, + local_port, + ) + .ok_or(anyhow!("Could not create a local Socks5 api proxy"))?, + ); + daemon_types::AccessMethod::Socks5(socks_proxy) + } + Socks5AddCommands::Remote { + remote_ip, + remote_port, + username, + password, + } => { + println!("Adding REMOTE SOCKS5-proxy: {username:?}+{password:?} @ {remote_ip}:{remote_port}"); + let socks_proxy = daemon_types::Socks5::Remote( + daemon_types::Socks5Remote::from_args( + remote_ip.to_string(), + remote_port, + ) + .ok_or(anyhow!("Could not create a remote Socks5 api proxy"))?, + ); + daemon_types::AccessMethod::Socks5(socks_proxy) + } + }, + AddCustomCommands::Shadowsocks { + remote_ip, + remote_port, + password, + cipher, + } => { + println!( + "Adding Shadowsocks-proxy: {password} @ {remote_ip}:{remote_port} using {cipher}" + ); + let shadowsocks_proxy = daemon_types::Shadowsocks::from_args( + remote_ip.to_string(), + remote_port, + cipher, + password, + ) + .ok_or(anyhow!("Could not create a Shadowsocks api proxy"))?; + daemon_types::AccessMethod::Shadowsocks(shadowsocks_proxy) + } + }) + } + } } diff --git a/mullvad-daemon/src/access_methods.rs b/mullvad-daemon/src/access_methods.rs new file mode 100644 index 0000000000..634914d00a --- /dev/null +++ b/mullvad-daemon/src/access_methods.rs @@ -0,0 +1,37 @@ +use crate::{new_selector_config, settings, Daemon, EventListener}; +use mullvad_types::api_access_method::AccessMethod; + +#[derive(err_derive::Error, Debug)] +pub enum Error { + /// Can not add access method + #[error(display = "Cannot add custom access method")] + Add, + /// Access methods settings error + #[error(display = "Settings error")] + Settings(#[error(source)] settings::Error), +} + +impl<L> Daemon<L> +where + L: EventListener + Clone + Send + 'static, +{ + pub async fn add_access_method(&mut self, access_method: AccessMethod) -> Result<(), Error> { + self.settings + .update(|settings| { + settings + .api_access_methods + .api_access_methods + .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_err(Error::Settings) + } +} diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 81ebe1050c..676e122a64 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -1,6 +1,7 @@ #![deny(rust_2018_idioms)] #![recursion_limit = "512"] +mod access_methods; pub mod account_history; mod api; #[cfg(not(target_os = "android"))] @@ -39,6 +40,7 @@ use mullvad_relay_selector::{ }; use mullvad_types::{ account::{AccountData, AccountToken, VoucherSubmission}, + api_access_method::AccessMethod, auth_failed::AuthFailed, custom_list::CustomList, device::{Device, DeviceEvent, DeviceEventCause, DeviceId, DeviceState, RemoveDeviceEvent}, @@ -170,6 +172,9 @@ pub enum Error { #[error(display = "A list with that name does not exist")] CustomListNotFound, + #[error(display = "Access method error")] + AccessMethodError(#[error(source)] access_methods::Error), + #[cfg(target_os = "macos")] #[error(display = "Failed to set exclusion group")] GroupIdError(#[error(source)] io::Error), @@ -255,6 +260,10 @@ pub enum DaemonCommand { DeleteCustomList(ResponseTx<(), Error>, mullvad_types::custom_list::Id), /// Update a custom list with a given id UpdateCustomList(ResponseTx<(), Error>, CustomList), + /// Get API access methods + GetApiAccessMethods(ResponseTx<Vec<AccessMethod>, Error>), + /// Add API access methods + AddApiAccessMethod(ResponseTx<(), Error>, AccessMethod), /// Get information about the currently running and latest app versions GetVersionInfo(oneshot::Sender<Option<AppVersionInfo>>), /// Return whether the daemon is performing post-upgrade tasks @@ -1030,6 +1039,8 @@ where DeleteCustomList(tx, id) => self.on_delete_custom_list(tx, id).await, UpdateCustomList(tx, update) => self.on_update_custom_list(tx, update).await, GetVersionInfo(tx) => self.on_get_version_info(tx), + GetApiAccessMethods(tx) => self.on_get_api_access_methods(tx), + AddApiAccessMethod(tx, method) => self.on_add_api_access_method(tx, method).await, IsPerformingPostUpgrade(tx) => self.on_is_performing_post_upgrade(tx), GetCurrentVersion(tx) => self.on_get_current_version(tx), #[cfg(not(target_os = "android"))] @@ -2204,6 +2215,19 @@ where Self::oneshot_send(tx, result, "update_custom_list response"); } + fn on_get_api_access_methods(&mut self, tx: ResponseTx<Vec<AccessMethod>, Error>) { + let result = Ok(self.settings.api_access_methods.api_access_methods.clone()); + Self::oneshot_send(tx, result, "get_api_access_methods response"); + } + + async fn on_add_api_access_method(&mut self, tx: ResponseTx<(), Error>, method: AccessMethod) { + let result = self + .add_access_method(method) + .await + .map_err(Error::AccessMethodError); + Self::oneshot_send(tx, result, "add_api_access_method response"); + } + fn on_get_settings(&self, tx: oneshot::Sender<Settings>) { Self::oneshot_send(tx, self.settings.to_settings(), "get_settings response"); } diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index f4b3eb961d..03c9a2c02c 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -73,6 +73,12 @@ service ManagementService { rpc DeleteCustomList(google.protobuf.StringValue) returns (google.protobuf.Empty) {} rpc UpdateCustomList(CustomList) returns (google.protobuf.Empty) {} + // API Access methods + rpc GetApiAccessMethods(google.protobuf.Empty) returns (ApiAccessMethods) {} + rpc AddApiAccessMethod(ApiAccessMethod) returns (google.protobuf.Empty) { + // Can I return something useful here instead of Empty? + } + // Split tunneling (Linux) rpc GetSplitTunnelProcesses(google.protobuf.Empty) returns (stream google.protobuf.Int32Value) {} rpc AddSplitTunnelProcess(google.protobuf.Int32Value) returns (google.protobuf.Empty) {} @@ -324,6 +330,38 @@ message CustomList { message CustomListSettings { repeated CustomList custom_lists = 1; } +message ApiAccessMethod { + message Socks5Local { + string ip = 1; + uint32 port = 2; + uint32 local_port = 3; + } + message Socks5Remote { + string ip = 1; + uint32 port = 2; + } + message Socks5 { + oneof Socks5type { + Socks5Local local = 1; + Socks5Remote remote = 2; + } + } + message Shadowsocks { + string ip = 1; + uint32 port = 2; + string password = 3; + string cipher = 4; + } + oneof access_method { + Socks5 socks5 = 1; + Shadowsocks shadowsocks = 2; + } +} + +message ApiAccessMethods { repeated ApiAccessMethod api_access_methods = 1; } + +message ApiAccessMethodSettings { repeated ApiAccessMethod api_access_methods = 1; } + message Settings { RelaySettings relay_settings = 1; BridgeSettings bridge_settings = 2; @@ -336,6 +374,7 @@ message Settings { SplitTunnelSettings split_tunnel = 9; ObfuscationSettings obfuscation_settings = 10; CustomListSettings custom_lists = 11; + ApiAccessMethodSettings api_access_methods = 12; } message SplitTunnelSettings { diff --git a/mullvad-management-interface/src/client.rs b/mullvad-management-interface/src/client.rs index a1ddc5e39a..e7c14800a5 100644 --- a/mullvad-management-interface/src/client.rs +++ b/mullvad-management-interface/src/client.rs @@ -4,6 +4,7 @@ use crate::types; use futures::{Stream, StreamExt}; use mullvad_types::{ account::{AccountData, AccountToken, VoucherSubmission}, + api_access_method::AccessMethod, custom_list::{CustomList, Id}, device::{Device, DeviceEvent, DeviceId, DeviceState, RemoveDeviceEvent}, location::GeoIpLocation, @@ -163,6 +164,19 @@ impl MullvadProxyClient { mullvad_types::relay_list::RelayList::try_from(list).map_err(Error::InvalidResponse) } + pub async fn get_api_access_methods(&mut self) -> Result<Vec<AccessMethod>> { + Ok(self + .0 + .get_api_access_methods(()) + .await + .map_err(Error::Rpc)? + .into_inner() + .api_access_methods + .iter() + .map(From::from) + .collect()) + } + pub async fn update_relay_locations(&mut self) -> Result<()> { self.0 .update_relay_locations(()) @@ -457,6 +471,14 @@ impl MullvadProxyClient { Ok(()) } + pub async fn add_access_method(&mut self, access_method: AccessMethod) -> Result<()> { + self.0 + .add_api_access_method(types::ApiAccessMethod::from(access_method)) + .await + .map_err(Error::Rpc) + .map(drop) + } + #[cfg(target_os = "linux")] pub async fn get_split_tunnel_processes(&mut self) -> Result<Vec<i32>> { use futures::TryStreamExt; diff --git a/mullvad-management-interface/src/types/conversions/api_access_method.rs b/mullvad-management-interface/src/types/conversions/api_access_method.rs new file mode 100644 index 0000000000..a91923e9e9 --- /dev/null +++ b/mullvad-management-interface/src/types/conversions/api_access_method.rs @@ -0,0 +1,171 @@ +/// Implements conversions for the auxilliary proto AccessMethod type to the internal AccessMethod data type. +mod settings { + use crate::types::proto; + use mullvad_types::api_access_method; + + impl From<&api_access_method::Settings> for proto::ApiAccessMethodSettings { + fn from(settings: &api_access_method::Settings) -> Self { + Self { + api_access_methods: settings + .api_access_methods + .iter() + .map(|method| method.clone().into()) + .collect(), + } + } + } + + impl From<api_access_method::Settings> for proto::ApiAccessMethodSettings { + fn from(settings: api_access_method::Settings) -> Self { + proto::ApiAccessMethodSettings::from(&settings) + } + } + + impl From<proto::ApiAccessMethodSettings> for api_access_method::Settings { + fn from(settings: proto::ApiAccessMethodSettings) -> Self { + Self { + api_access_methods: settings + .api_access_methods + .iter() + .map(api_access_method::AccessMethod::from) + .collect(), + } + } + } +} + +/// Implements conversions for the 'main' AccessMethod data type. +mod data { + use crate::types::proto::{self, api_access_method::socks5::Socks5type}; + use mullvad_types::api_access_method::{ + AccessMethod, Shadowsocks, Socks5, Socks5Local, Socks5Remote, + }; + + impl From<proto::ApiAccessMethods> for Vec<AccessMethod> { + fn from(api_access_methods: proto::ApiAccessMethods) -> Self { + api_access_methods + .api_access_methods + .iter() + .map(AccessMethod::from) + .collect() + } + } + + impl From<proto::ApiAccessMethod> for AccessMethod { + fn from(value: proto::ApiAccessMethod) -> Self { + // TODO: How to not unwrap? + match value.access_method.unwrap() { + 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::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)) + } + } + } + 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) + } + } + } + } + + 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::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() + } + AccessMethod::Socks5(Socks5::Remote(Socks5Remote { peer })) => { + proto::api_access_method::Socks5Remote { + ip: peer.ip().to_string(), + port: peer.port() as u32, + } + .into() + } + } + } + } + + impl From<&proto::ApiAccessMethod> for AccessMethod { + fn from(value: &proto::ApiAccessMethod) -> Self { + AccessMethod::from(value.clone()) + } + } + + impl From<Vec<AccessMethod>> for proto::ApiAccessMethods { + fn from(value: Vec<AccessMethod>) -> proto::ApiAccessMethods { + proto::ApiAccessMethods { + api_access_methods: value.iter().map(|method| method.clone().into()).collect(), + } + } + } + + impl From<proto::api_access_method::Shadowsocks> for proto::ApiAccessMethod { + fn from(value: proto::api_access_method::Shadowsocks) -> Self { + proto::api_access_method::AccessMethod::Shadowsocks(value).into() + } + } + + impl From<proto::api_access_method::Socks5> for proto::ApiAccessMethod { + fn from(value: proto::api_access_method::Socks5) -> Self { + proto::api_access_method::AccessMethod::Socks5(value).into() + } + } + + impl From<proto::api_access_method::socks5::Socks5type> for proto::ApiAccessMethod { + fn from(value: proto::api_access_method::socks5::Socks5type) -> Self { + proto::api_access_method::AccessMethod::Socks5(proto::api_access_method::Socks5 { + socks5type: Some(value), + }) + .into() + } + } + + impl From<proto::api_access_method::Socks5Local> for proto::ApiAccessMethod { + fn from(value: proto::api_access_method::Socks5Local) -> Self { + proto::api_access_method::socks5::Socks5type::Local(value).into() + } + } + + impl From<proto::api_access_method::Socks5Remote> for proto::ApiAccessMethod { + fn from(value: proto::api_access_method::Socks5Remote) -> Self { + proto::api_access_method::socks5::Socks5type::Remote(value).into() + } + } + + impl From<proto::api_access_method::AccessMethod> for proto::ApiAccessMethod { + fn from(value: proto::api_access_method::AccessMethod) -> Self { + proto::ApiAccessMethod { + access_method: Some(value), + } + } + } +} diff --git a/mullvad-management-interface/src/types/conversions/mod.rs b/mullvad-management-interface/src/types/conversions/mod.rs index d2e8b60265..ddfe3f4282 100644 --- a/mullvad-management-interface/src/types/conversions/mod.rs +++ b/mullvad-management-interface/src/types/conversions/mod.rs @@ -1,6 +1,7 @@ use std::str::FromStr; mod account; +mod api_access_method; mod custom_list; mod custom_tunnel; mod device; diff --git a/mullvad-management-interface/src/types/conversions/settings.rs b/mullvad-management-interface/src/types/conversions/settings.rs index f123b41755..d454af4cb0 100644 --- a/mullvad-management-interface/src/types/conversions/settings.rs +++ b/mullvad-management-interface/src/types/conversions/settings.rs @@ -42,6 +42,9 @@ impl From<&mullvad_types::settings::Settings> for proto::Settings { custom_lists: Some(proto::CustomListSettings::from( settings.custom_lists.clone(), )), + api_access_methods: Some(proto::ApiAccessMethodSettings::from( + &settings.api_access_methods, + )), } } } @@ -140,6 +143,12 @@ impl TryFrom<proto::Settings> for mullvad_types::settings::Settings { .ok_or(FromProtobufTypeError::InvalidArgument( "missing custom lists settings", ))?; + let api_access_methods_settings = + settings + .api_access_methods + .ok_or(FromProtobufTypeError::InvalidArgument( + "missing api access methods settings", + ))?; #[cfg(windows)] let split_tunnel = settings .split_tunnel @@ -171,6 +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_settings, + ), }) } } diff --git a/mullvad-types/src/api_access_method.rs b/mullvad-types/src/api_access_method.rs new file mode 100644 index 0000000000..5a4d32551f --- /dev/null +++ b/mullvad-types/src/api_access_method.rs @@ -0,0 +1,94 @@ +use std::str::FromStr; + +use serde::{Deserialize, Serialize}; +use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; + +/// Daemon settings for API access methods. +#[derive(Default, Debug, Clone, Serialize, Deserialize, PartialEq)] +pub struct Settings { + pub api_access_methods: Vec<AccessMethod>, +} + +/// API access method datastructure. +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum AccessMethod { + Shadowsocks(Shadowsocks), + Socks5(Socks5), +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct Shadowsocks { + pub peer: SocketAddr, + pub password: String, // TODO: Mask the password (using special type)? + pub cipher: String, // Gets validated at a later stage. Is assumed to be valid. +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub enum Socks5 { + Local(Socks5Local), + Remote(Socks5Remote), +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct Socks5Local { + pub peer: SocketAddr, + /// Port on localhost where the SOCKS5-proxy listens to. + pub port: u16, +} + +#[derive(Clone, Debug, Serialize, Deserialize, PartialEq)] +pub struct Socks5Remote { + pub peer: SocketAddr, +} + +impl Settings { + // TODO: Do I have to clone? + pub fn get_access_methods(&self) -> Vec<AccessMethod> { + self.api_access_methods.clone() + } +} + +impl Shadowsocks { + pub fn new(peer: SocketAddr, cipher: String, password: String) -> Self { + Shadowsocks { + peer, + password, + cipher, + } + } + + /// Like [new()], but tries to parse `ip` and `port` into a [`std::net::SocketAddr`] for you. + /// If `ip` or `port` are valid [`Some(Socks5Local)`] is returned, otherwise [`None`]. + pub fn from_args(ip: String, port: u16, cipher: String, password: String) -> Option<Self> { + let peer = SocketAddrV4::new(Ipv4Addr::from_str(&ip).ok()?, port).into(); + Some(Self::new(peer, password, cipher)) + } +} + +impl Socks5Local { + pub fn new(peer: SocketAddr, port: u16) -> Self { + Self { peer, port } + } + + /// Like [new()], but tries to parse `ip` and `port` into a [`std::net::SocketAddr`] for you. + /// If `ip` or `port` are valid [`Some(Socks5Local)`] is returned, otherwise [`None`]. + pub fn from_args(ip: String, port: u16, localport: u16) -> Option<Self> { + let peer_ip = IpAddr::V4(Ipv4Addr::from_str(&ip).ok()?); + let peer = SocketAddr::new(peer_ip, port); + Some(Self::new(peer, localport)) + } +} + +impl Socks5Remote { + pub fn new(peer: SocketAddr) -> Self { + Self { peer } + } + + /// Like [new()], but tries to parse `ip` and `port` into a [`std::net::SocketAddr`] for you. + /// If `ip` or `port` are valid [`Some(Socks5Remote)`] is returned, otherwise [`None`]. + pub fn from_args(ip: String, port: u16) -> Option<Self> { + let peer_ip = IpAddr::V4(Ipv4Addr::from_str(&ip).ok()?); + let peer = SocketAddr::new(peer_ip, port); + Some(Self::new(peer)) + } +} diff --git a/mullvad-types/src/lib.rs b/mullvad-types/src/lib.rs index bfac631f82..8b5a501663 100644 --- a/mullvad-types/src/lib.rs +++ b/mullvad-types/src/lib.rs @@ -1,6 +1,7 @@ #![deny(rust_2018_idioms)] pub mod account; +pub mod api_access_method; pub mod auth_failed; pub mod custom_list; pub mod device; diff --git a/mullvad-types/src/settings/mod.rs b/mullvad-types/src/settings/mod.rs index 3b3ca15014..fc66649b8a 100644 --- a/mullvad-types/src/settings/mod.rs +++ b/mullvad-types/src/settings/mod.rs @@ -1,4 +1,5 @@ use crate::{ + api_access_method, custom_list::CustomListsSettings, relay_constraints::{ BridgeConstraints, BridgeSettings, BridgeState, Constraint, GeographicLocationConstraint, @@ -76,6 +77,9 @@ pub struct Settings { /// All of the custom relay lists #[cfg_attr(target_os = "android", jnix(skip))] pub custom_lists: CustomListsSettings, + /// API access methods. + #[cfg_attr(target_os = "android", jnix(skip))] + pub api_access_methods: api_access_method::Settings, /// If the daemon should allow communication with private (LAN) networks. pub allow_lan: bool, /// Extra level of kill switch. When this setting is on, the disconnected state will block @@ -136,6 +140,15 @@ 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 + }, } } } |
