diff options
| author | Emīls Piņķis <emils@mullvad.net> | 2019-05-28 17:33:55 +0100 |
|---|---|---|
| committer | Emīls Piņķis <emils@mullvad.net> | 2019-05-28 17:33:55 +0100 |
| commit | ec6674bd044b866f388d6e68f8380e669c067193 (patch) | |
| tree | 589c784398b81ef09bb98d69fbf12750b55c9921 | |
| parent | 5da250dc85c96f3f83b6b1c1d490e6afb5f2db53 (diff) | |
| parent | 52e91fba03b8f1acbc07f73864445b8e85089875 (diff) | |
| download | mullvadvpn-ec6674bd044b866f388d6e68f8380e669c067193.tar.xz mullvadvpn-ec6674bd044b866f388d6e68f8380e669c067193.zip | |
Merge branch 'shadowsocks-cli'
| -rw-r--r-- | mullvad-cli/src/cmds/bridge.rs | 354 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/mod.rs | 4 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/relay.rs | 82 | ||||
| -rw-r--r-- | mullvad-cli/src/cmds/tunnel.rs | 240 | ||||
| -rw-r--r-- | mullvad-cli/src/location.rs | 69 | ||||
| -rw-r--r-- | mullvad-cli/src/main.rs | 1 | ||||
| -rw-r--r-- | mullvad-daemon/src/lib.rs | 79 | ||||
| -rw-r--r-- | mullvad-daemon/src/management_interface.rs | 46 | ||||
| -rw-r--r-- | mullvad-daemon/src/relays.rs | 6 | ||||
| -rw-r--r-- | mullvad-ipc-client/src/lib.rs | 15 | ||||
| -rw-r--r-- | mullvad-types/src/custom_tunnel.rs | 2 | ||||
| -rw-r--r-- | mullvad-types/src/relay_constraints.rs | 23 | ||||
| -rw-r--r-- | mullvad-types/src/settings.rs | 13 | ||||
| -rw-r--r-- | talpid-core/src/process/openvpn.rs | 4 | ||||
| -rw-r--r-- | talpid-core/src/tunnel/openvpn.rs | 6 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connected_state.rs | 2 | ||||
| -rw-r--r-- | talpid-core/src/tunnel_state_machine/connecting_state.rs | 2 | ||||
| -rw-r--r-- | talpid-types/src/net/openvpn.rs | 3 |
18 files changed, 568 insertions, 383 deletions
diff --git a/mullvad-cli/src/cmds/bridge.rs b/mullvad-cli/src/cmds/bridge.rs new file mode 100644 index 0000000000..3c686154eb --- /dev/null +++ b/mullvad-cli/src/cmds/bridge.rs @@ -0,0 +1,354 @@ +use crate::{location, new_rpc_client, Command, Result}; +use clap::value_t; + +use mullvad_types::relay_constraints::{BridgeConstraints, BridgeSettings, BridgeState}; +use talpid_types::net::openvpn::{self, SHADOWSOCKS_CIPHERS}; + +use std::net::{IpAddr, SocketAddr}; + +pub struct Bridge; + +impl Command for Bridge { + fn name(&self) -> &'static str { + "bridge" + } + + fn clap_subcommand(&self) -> clap::App<'static, 'static> { + clap::SubCommand::with_name(self.name()) + .about("Manage use of bridges") + .setting(clap::AppSettings::SubcommandRequiredElseHelp) + .subcommand(create_bridge_set_subcommand()) + .subcommand( + clap::SubCommand::with_name("get").about("Get current bridge settings and state"), + ) + .subcommand(clap::SubCommand::with_name("list").about("List brigde relays")) + } + + fn run(&self, matches: &clap::ArgMatches<'_>) -> Result<()> { + match matches.subcommand() { + ("set", Some(set_matches)) => Self::handle_set(set_matches), + ("get", _) => Self::handle_get(), + ("list", _) => Self::list_bridge_relays(), + _ => unreachable!("unhandled command"), + } + } +} + +fn create_bridge_set_subcommand() -> clap::App<'static, 'static> { + clap::SubCommand::with_name("set") + .about("Set bridge state and settings") + .setting(clap::AppSettings::SubcommandRequiredElseHelp) + .subcommand(create_set_state_subcommand()) + .subcommand(create_set_custom_settings_subcommand()) + .subcommand(location::get_subcommand().about( + "Set country or city to select bridge relays from. Use the 'list' \ + command to show available alternatives.", + )) +} + + +fn create_set_custom_settings_subcommand() -> clap::App<'static, 'static> { + clap::SubCommand::with_name("custom") + .about("Configure a SOCKS5 proxy") + .setting(clap::AppSettings::SubcommandRequiredElseHelp) + .subcommand( + clap::SubCommand::with_name("local") + .about("Registers a local SOCKS5 proxy") + .arg( + clap::Arg::with_name("local-port") + .help("Specifies the port the local proxy server is listening on") + .required(true) + .index(1), + ) + .arg( + clap::Arg::with_name("remote-ip") + .help("Specifies the IP of the proxy server peer") + .required(true) + .index(2), + ) + .arg( + clap::Arg::with_name("remote-port") + .help("Specifies the port of the proxy server peer") + .required(true) + .index(3), + ), + ) + .subcommand( + clap::SubCommand::with_name("remote") + .about("Registers a remote SOCKS5 proxy") + .arg( + clap::Arg::with_name("remote-ip") + .help("Specifies the IP of the remote proxy server") + .required(true) + .index(1), + ) + .arg( + clap::Arg::with_name("remote-port") + .help("Specifies the port the remote proxy server is listening on") + .required(true) + .index(2), + ) + .arg( + clap::Arg::with_name("username") + .help("Specifies the username for remote authentication") + .required(true) + .index(3), + ) + .arg( + clap::Arg::with_name("password") + .help("Specifies the password for remote authentication") + .required(true) + .index(4), + ), + ) + .subcommand( + clap::SubCommand::with_name("shadowsocks") + .about("Configure bundled Shadowsocks proxy") + .arg( + clap::Arg::with_name("remote-ip") + .help("Specifies the IP of the remote Shadowsocks server") + .required(true) + .index(1), + ) + .arg( + clap::Arg::with_name("remote-port") + .help("Specifies the port of the remote Shadowsocks server") + .default_value("443") + .index(2), + ) + .arg( + clap::Arg::with_name("password") + .help("Specifies the password on the remote Shadowsocks server") + .default_value("23#dfsbbb") + .index(3), + ) + .arg( + clap::Arg::with_name("cipher") + .help("Specifies the cipher to use") + .default_value("chacha20") + .possible_values(SHADOWSOCKS_CIPHERS) + .index(4), + ), + ) +} + +fn create_set_state_subcommand() -> clap::App<'static, 'static> { + clap::SubCommand::with_name("state") + .about("Set bridge state") + .arg( + clap::Arg::with_name("state") + .help("Specifies whether a bridge should be used") + .index(1) + .possible_values(&["auto", "on", "off"]), + ) +} + +impl Bridge { + fn handle_set(matches: &clap::ArgMatches<'_>) -> Result<()> { + match matches.subcommand() { + ("location", Some(location_matches)) => { + Self::handle_set_bridge_location(location_matches) + } + ("custom", Some(custom_matches)) => { + Self::handle_bridge_set_custom_settings(custom_matches) + } + ("state", Some(set_matches)) => Self::handle_set_bridge_state(set_matches), + _ => unreachable!("unhandled command"), + } + } + + fn handle_get() -> Result<()> { + let mut rpc = new_rpc_client()?; + let settings = rpc.get_settings()?; + println!("Bridge state - {}", settings.get_bridge_state()); + match settings.get_bridge_settings() { + BridgeSettings::Custom(proxy) => { + match proxy { + openvpn::ProxySettings::Local(local_proxy) => { + Self::print_local_proxy(&local_proxy) + } + openvpn::ProxySettings::Remote(remote_proxy) => { + Self::print_remote_proxy(&remote_proxy) + } + openvpn::ProxySettings::Shadowsocks(shadowsocks_proxy) => { + Self::print_shadowsocks_proxy(&shadowsocks_proxy) + } + }; + } + BridgeSettings::Normal(constraints) => { + println!("Bridge constraints: {}", constraints); + } + }; + Ok(()) + } + + fn handle_set_bridge_location(matches: &clap::ArgMatches<'_>) -> Result<()> { + let location = location::get_constraint(matches); + let mut rpc = new_rpc_client()?; + rpc.set_bridge_settings(BridgeSettings::Normal(BridgeConstraints { location }))?; + Ok(()) + } + + fn handle_set_bridge_state(matches: &clap::ArgMatches<'_>) -> Result<()> { + let state = match matches.value_of("state").unwrap() { + "auto" => BridgeState::Auto, + "on" => BridgeState::On, + "off" => BridgeState::Off, + _ => unreachable!(), + }; + let mut rpc = new_rpc_client()?; + rpc.set_bridge_state(state)?; + Ok(()) + } + + fn handle_bridge_set_custom_settings(matches: &clap::ArgMatches<'_>) -> Result<()> { + if let Some(args) = matches.subcommand_matches("local") { + let local_port = + value_t!(args.value_of("local-port"), u16).unwrap_or_else(|e| e.exit()); + let remote_ip = + value_t!(args.value_of("remote-ip"), IpAddr).unwrap_or_else(|e| e.exit()); + let remote_port = + value_t!(args.value_of("remote-port"), u16).unwrap_or_else(|e| e.exit()); + + let proxy = openvpn::LocalProxySettings { + port: local_port, + peer: SocketAddr::new(remote_ip, remote_port), + }; + + let packed_proxy = openvpn::ProxySettings::Local(proxy); + + if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) { + panic!(error); + } + + let mut rpc = new_rpc_client()?; + rpc.set_bridge_settings(BridgeSettings::Custom(packed_proxy))?; + } else if let Some(args) = matches.subcommand_matches("remote") { + let remote_ip = + value_t!(args.value_of("remote-ip"), IpAddr).unwrap_or_else(|e| e.exit()); + let remote_port = + value_t!(args.value_of("remote-port"), u16).unwrap_or_else(|e| e.exit()); + let username = args.value_of("username"); + let password = args.value_of("password"); + + let auth = match (username, password) { + (Some(username), Some(password)) => Some(openvpn::ProxyAuth { + username: username.to_string(), + password: password.to_string(), + }), + _ => None, + }; + + let proxy = openvpn::RemoteProxySettings { + address: SocketAddr::new(remote_ip, remote_port), + auth, + }; + + let packed_proxy = openvpn::ProxySettings::Remote(proxy); + + if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) { + panic!(error); + } + + let mut rpc = new_rpc_client()?; + rpc.set_bridge_settings(BridgeSettings::Custom(packed_proxy))?; + } else if let Some(args) = matches.subcommand_matches("shadowsocks") { + let remote_ip = + value_t!(args.value_of("remote-ip"), IpAddr).unwrap_or_else(|e| e.exit()); + let remote_port = + value_t!(args.value_of("remote-port"), u16).unwrap_or_else(|e| e.exit()); + let password = args.value_of("password").unwrap().to_string(); + let cipher = args.value_of("cipher").unwrap().to_string(); + + let proxy = openvpn::ShadowsocksProxySettings { + peer: SocketAddr::new(remote_ip, remote_port), + password, + cipher, + }; + + let packed_proxy = openvpn::ProxySettings::Shadowsocks(proxy); + + if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) { + panic!(error); + } + + let mut rpc = new_rpc_client()?; + rpc.set_bridge_settings(BridgeSettings::Custom(packed_proxy))?; + } else { + unreachable!("unhandled proxy type"); + } + + println!("proxy details have been updated"); + Ok(()) + } + + fn print_local_proxy(proxy: &openvpn::LocalProxySettings) { + println!("proxy: local"); + println!(" local port: {}", proxy.port); + println!(" peer IP: {}", proxy.peer.ip()); + println!(" peer port: {}", proxy.peer.port()); + } + + fn print_remote_proxy(proxy: &openvpn::RemoteProxySettings) { + println!("proxy: remote"); + println!(" server IP: {}", proxy.address.ip()); + println!(" server port: {}", proxy.address.port()); + + if let Some(ref auth) = proxy.auth { + println!(" auth username: {}", auth.username); + println!(" auth password: {}", auth.password); + } else { + println!(" auth: none"); + } + } + + fn print_shadowsocks_proxy(proxy: &openvpn::ShadowsocksProxySettings) { + println!("proxy: Shadowsocks"); + println!(" peer IP: {}", proxy.peer.ip()); + println!(" peer port: {}", proxy.peer.port()); + println!(" password: {}", proxy.password); + println!(" cipher: {}", proxy.cipher); + } + + fn list_bridge_relays() -> Result<()> { + let mut rpc = new_rpc_client()?; + let mut locations = rpc.get_relay_locations()?; + + locations.countries = locations + .countries + .into_iter() + .filter_map(|mut country| { + country.cities = country + .cities + .into_iter() + .filter_map(|mut city| { + city.relays.retain(|relay| !relay.bridges.is_empty()); + if !city.relays.is_empty() { + Some(city) + } else { + None + } + }) + .collect(); + if !country.cities.is_empty() { + Some(country) + } else { + None + } + }) + .collect(); + + for mut country in locations.countries { + country.cities.sort_by(|c1, c2| c1.name.cmp(&c2.name)); + println!("{} ({})", country.name, country.code); + for city in &country.cities { + println!( + "\t{} ({}) @ {:.5}°N, {:.5}°W", + city.name, city.code, city.latitude, city.longitude + ); + } + println!(); + } + Ok(()) + } +} diff --git a/mullvad-cli/src/cmds/mod.rs b/mullvad-cli/src/cmds/mod.rs index 43e1968065..83bfd47883 100644 --- a/mullvad-cli/src/cmds/mod.rs +++ b/mullvad-cli/src/cmds/mod.rs @@ -7,6 +7,9 @@ pub use self::account::Account; mod auto_connect; pub use self::auto_connect::AutoConnect; +mod bridge; +pub use self::bridge::Bridge; + mod status; pub use self::status::Status; @@ -37,6 +40,7 @@ pub fn get_commands() -> HashMap<&'static str, Box<dyn Command>> { Box::new(Account), Box::new(AutoConnect), Box::new(BlockWhenDisconnected), + Box::new(Bridge), Box::new(Connect), Box::new(Disconnect), Box::new(Lan), diff --git a/mullvad-cli/src/cmds/relay.rs b/mullvad-cli/src/cmds/relay.rs index 5cc8f2815b..965107057d 100644 --- a/mullvad-cli/src/cmds/relay.rs +++ b/mullvad-cli/src/cmds/relay.rs @@ -1,4 +1,4 @@ -use crate::{new_rpc_client, Command, Error, Result}; +use crate::{location, new_rpc_client, Command, Error, Result}; use clap::{value_t, values_t}; use std::{ io::{self, BufRead}, @@ -8,8 +8,8 @@ use std::{ use mullvad_types::{ relay_constraints::{ - Constraint, LocationConstraint, OpenVpnConstraints, RelayConstraintsUpdate, - RelaySettingsUpdate, TunnelConstraints, WireguardConstraints, + Constraint, OpenVpnConstraints, RelayConstraintsUpdate, RelaySettingsUpdate, + TunnelConstraints, WireguardConstraints, }, ConnectionConfig, CustomTunnelEndpoint, }; @@ -109,31 +109,9 @@ impl Command for Relay { ) ) .subcommand( - clap::SubCommand::with_name("location") - .about( - "Set country or city to select relays from. Use the 'list' \ - command to show available alternatives.", - ) - .arg( - clap::Arg::with_name("country") - .help( - "The two letter country code, or 'any' for no preference.", - ) - .required(true) - .index(1) - .validator(country_code_validator), - ) - .arg( - clap::Arg::with_name("city") - .help("The three letter city code") - .index(2) - .validator(city_code_validator), - ) - .arg( - clap::Arg::with_name("hostname") - .help("The relay hostname") - .index(3), - ), + location::get_subcommand() + .about("Set country or city to select relays from. Use the 'list' \ + command to show available alternatives.") ) .subcommand( clap::SubCommand::with_name("tunnel") @@ -289,37 +267,7 @@ impl Relay { } fn set_location(&self, matches: &clap::ArgMatches<'_>) -> Result<()> { - let country = matches.value_of("country").unwrap(); - let city = matches.value_of("city"); - let hostname = matches.value_of("hostname"); - - let location_constraint = match (country, city, hostname) { - ("any", None, None) => Constraint::Any, - ("any", ..) => clap::Error::with_description( - "City can't be given when selecting 'any' country", - clap::ErrorKind::InvalidValue, - ) - .exit(), - (country, None, None) => { - Constraint::Only(LocationConstraint::Country(country.to_owned())) - } - (country, Some(city), None) => Constraint::Only(LocationConstraint::City( - country.to_owned(), - city.to_owned(), - )), - (country, Some(city), Some(hostname)) => { - Constraint::Only(LocationConstraint::Hostname( - country.to_owned(), - city.to_owned(), - hostname.to_owned(), - )) - } - (..) => clap::Error::with_description( - "Invalid country, city and hostname combination given", - clap::ErrorKind::InvalidValue, - ) - .exit(), - }; + let location_constraint = location::get_constraint(matches); self.update_constraints(RelaySettingsUpdate::Normal(RelayConstraintsUpdate { location: Some(location_constraint), @@ -409,19 +357,3 @@ fn parse_protocol_constraint(raw_protocol: &str) -> Constraint<TransportProtocol _ => unreachable!(), } } - -fn country_code_validator(code: String) -> ::std::result::Result<(), String> { - if code.len() == 2 || code == "any" { - Ok(()) - } else { - Err(String::from("Country codes must be two letters, or 'any'.")) - } -} - -fn city_code_validator(code: String) -> ::std::result::Result<(), String> { - if code.len() == 3 { - Ok(()) - } else { - Err(String::from("City codes must be three letters")) - } -} diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs index c02a88d03a..136290d5bb 100644 --- a/mullvad-cli/src/cmds/tunnel.rs +++ b/mullvad-cli/src/cmds/tunnel.rs @@ -2,9 +2,6 @@ use crate::{new_rpc_client, Command, Result}; use clap::value_t; use mullvad_types::settings::TunnelOptions; -use talpid_types::net::openvpn::{self, SHADOWSOCKS_CIPHERS}; - -use std::net::{IpAddr, SocketAddr}; pub struct Tunnel; @@ -67,7 +64,6 @@ fn create_openvpn_subcommand() -> clap::App<'static, 'static> { .about("Manage options for OpenVPN tunnels") .setting(clap::AppSettings::SubcommandRequiredElseHelp) .subcommand(create_openvpn_mssfix_subcommand()) - .subcommand(create_openvpn_proxy_subcommand()) } fn create_openvpn_mssfix_subcommand() -> clap::App<'static, 'static> { @@ -81,97 +77,6 @@ fn create_openvpn_mssfix_subcommand() -> clap::App<'static, 'static> { ) } -fn create_openvpn_proxy_subcommand() -> clap::App<'static, 'static> { - clap::SubCommand::with_name("proxy") - .about("Configure a SOCKS5 proxy") - .setting(clap::AppSettings::SubcommandRequiredElseHelp) - .subcommand(clap::SubCommand::with_name("get")) - .subcommand(clap::SubCommand::with_name("unset")) - .subcommand( - clap::SubCommand::with_name("set") - .setting(clap::AppSettings::SubcommandRequiredElseHelp) - .subcommand( - clap::SubCommand::with_name("local") - .about("Registers a local SOCKS5 proxy") - .arg( - clap::Arg::with_name("local-port") - .help("Specifies the port the local proxy server is listening on") - .required(true) - .index(1), - ) - .arg( - clap::Arg::with_name("remote-ip") - .help("Specifies the IP of the proxy server peer") - .required(true) - .index(2), - ) - .arg( - clap::Arg::with_name("remote-port") - .help("Specifies the port of the proxy server peer") - .required(true) - .index(3), - ), - ) - .subcommand( - clap::SubCommand::with_name("remote") - .about("Registers a remote SOCKS5 proxy") - .arg( - clap::Arg::with_name("remote-ip") - .help("Specifies the IP of the remote proxy server") - .required(true) - .index(1), - ) - .arg( - clap::Arg::with_name("remote-port") - .help("Specifies the port the remote proxy server is listening on") - .required(true) - .index(2), - ) - .arg( - clap::Arg::with_name("username") - .help("Specifies the username for remote authentication") - .required(true) - .index(3), - ) - .arg( - clap::Arg::with_name("password") - .help("Specifies the password for remote authentication") - .required(true) - .index(4), - ), - ) - .subcommand( - clap::SubCommand::with_name("shadowsocks") - .about("Configure bundled Shadowsocks proxy") - .arg( - clap::Arg::with_name("remote-ip") - .help("Specifies the IP of the remote Shadowsocks server") - .required(true) - .index(1), - ) - .arg( - clap::Arg::with_name("remote-port") - .help("Specifies the port of the remote Shadowsocks server") - .default_value("443") - .index(2), - ) - .arg( - clap::Arg::with_name("password") - .help("Specifies the password on the remote Shadowsocks server") - .default_value("23#dfsbbb") - .index(3), - ) - .arg( - clap::Arg::with_name("cipher") - .help("Specifies the cipher to use") - .default_value("chacha20") - .possible_values(SHADOWSOCKS_CIPHERS) - .index(4), - ), - ), - ) -} - fn create_ipv6_subcommand() -> clap::App<'static, 'static> { clap::SubCommand::with_name("ipv6") .setting(clap::AppSettings::SubcommandRequiredElseHelp) @@ -190,7 +95,6 @@ impl Tunnel { fn handle_openvpn_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> { match matches.subcommand() { ("mssfix", Some(mssfix_matches)) => Self::handle_openvpn_mssfix_cmd(mssfix_matches), - ("proxy", Some(proxy_matches)) => Self::handle_openvpn_proxy_cmd(proxy_matches), _ => unreachable!("unhandled command"), } } @@ -204,15 +108,6 @@ impl Tunnel { } } - fn handle_openvpn_proxy_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> { - match matches.subcommand() { - ("get", Some(_)) => Self::process_openvpn_proxy_get(), - ("unset", Some(_)) => Self::process_openvpn_proxy_unset(), - ("set", Some(set_matches)) => Self::process_openvpn_proxy_set(set_matches), - _ => unreachable!("unhandled command"), - } - } - fn handle_wireguard_cmd(matches: &clap::ArgMatches<'_>) -> Result<()> { match matches.subcommand() { ("mtu", Some(matches)) => match matches.subcommand() { @@ -323,141 +218,6 @@ impl Tunnel { Ok(()) } - fn process_openvpn_proxy_get() -> Result<()> { - let tunnel_options = Self::get_tunnel_options()?; - if let Some(proxy) = tunnel_options.openvpn.proxy { - if let openvpn::ProxySettings::Local(local_proxy) = proxy { - Self::print_local_proxy(&local_proxy) - } else if let openvpn::ProxySettings::Remote(remote_proxy) = proxy { - Self::print_remote_proxy(&remote_proxy) - } else if let openvpn::ProxySettings::Shadowsocks(shadowsocks_proxy) = proxy { - Self::print_shadowsocks_proxy(&shadowsocks_proxy) - } else { - unreachable!("unhandled proxy type"); - } - } else { - println!("proxy: unset"); - } - Ok(()) - } - - fn print_local_proxy(proxy: &openvpn::LocalProxySettings) { - println!("proxy: local"); - println!(" local port: {}", proxy.port); - println!(" peer IP: {}", proxy.peer.ip()); - println!(" peer port: {}", proxy.peer.port()); - } - - fn print_remote_proxy(proxy: &openvpn::RemoteProxySettings) { - println!("proxy: remote"); - println!(" server IP: {}", proxy.address.ip()); - println!(" server port: {}", proxy.address.port()); - - if let Some(ref auth) = proxy.auth { - println!(" auth username: {}", auth.username); - println!(" auth password: {}", auth.password); - } else { - println!(" auth: none"); - } - } - - fn print_shadowsocks_proxy(proxy: &openvpn::ShadowsocksProxySettings) { - println!("proxy: Shadowsocks"); - println!(" peer IP: {}", proxy.peer.ip()); - println!(" peer port: {}", proxy.peer.port()); - println!(" password: {}", proxy.password); - println!(" cipher: {}", proxy.cipher); - } - - fn process_openvpn_proxy_unset() -> Result<()> { - let mut rpc = new_rpc_client()?; - rpc.set_openvpn_proxy(None)?; - println!("proxy details have been unset"); - Ok(()) - } - - fn process_openvpn_proxy_set(matches: &clap::ArgMatches<'_>) -> Result<()> { - if let Some(args) = matches.subcommand_matches("local") { - let local_port = - value_t!(args.value_of("local-port"), u16).unwrap_or_else(|e| e.exit()); - let remote_ip = - value_t!(args.value_of("remote-ip"), IpAddr).unwrap_or_else(|e| e.exit()); - let remote_port = - value_t!(args.value_of("remote-port"), u16).unwrap_or_else(|e| e.exit()); - - let proxy = openvpn::LocalProxySettings { - port: local_port, - peer: SocketAddr::new(remote_ip, remote_port), - }; - - let packed_proxy = openvpn::ProxySettings::Local(proxy); - - if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) { - panic!(error); - } - - let mut rpc = new_rpc_client()?; - rpc.set_openvpn_proxy(Some(packed_proxy))?; - } else if let Some(args) = matches.subcommand_matches("remote") { - let remote_ip = - value_t!(args.value_of("remote-ip"), IpAddr).unwrap_or_else(|e| e.exit()); - let remote_port = - value_t!(args.value_of("remote-port"), u16).unwrap_or_else(|e| e.exit()); - let username = args.value_of("username"); - let password = args.value_of("password"); - - let auth = match (username, password) { - (Some(username), Some(password)) => Some(openvpn::ProxyAuth { - username: username.to_string(), - password: password.to_string(), - }), - _ => None, - }; - - let proxy = openvpn::RemoteProxySettings { - address: SocketAddr::new(remote_ip, remote_port), - auth, - }; - - let packed_proxy = openvpn::ProxySettings::Remote(proxy); - - if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) { - panic!(error); - } - - let mut rpc = new_rpc_client()?; - rpc.set_openvpn_proxy(Some(packed_proxy))?; - } else if let Some(args) = matches.subcommand_matches("shadowsocks") { - let remote_ip = - value_t!(args.value_of("remote-ip"), IpAddr).unwrap_or_else(|e| e.exit()); - let remote_port = - value_t!(args.value_of("remote-port"), u16).unwrap_or_else(|e| e.exit()); - let password = args.value_of("password").unwrap().to_string(); - let cipher = args.value_of("cipher").unwrap().to_string(); - - let proxy = openvpn::ShadowsocksProxySettings { - peer: SocketAddr::new(remote_ip, remote_port), - password, - cipher, - }; - - let packed_proxy = openvpn::ProxySettings::Shadowsocks(proxy); - - if let Err(error) = openvpn::validate_proxy_settings(&packed_proxy) { - panic!(error); - } - - let mut rpc = new_rpc_client()?; - rpc.set_openvpn_proxy(Some(packed_proxy))?; - } else { - unreachable!("unhandled proxy type"); - } - - println!("proxy details have been updated"); - println!("note: The OpenVPN tunnel constraints have been updated to use TCP"); - Ok(()) - } - fn process_ipv6_get() -> Result<()> { let tunnel_options = Self::get_tunnel_options()?; println!( diff --git a/mullvad-cli/src/location.rs b/mullvad-cli/src/location.rs new file mode 100644 index 0000000000..10b27b61d8 --- /dev/null +++ b/mullvad-cli/src/location.rs @@ -0,0 +1,69 @@ +use mullvad_types::relay_constraints::{Constraint, LocationConstraint}; + +pub fn get_subcommand() -> clap::App<'static, 'static> { + clap::SubCommand::with_name("location") + .arg( + clap::Arg::with_name("country") + .help("The two letter country code, or 'any' for no preference.") + .required(true) + .index(1) + .validator(country_code_validator), + ) + .arg( + clap::Arg::with_name("city") + .help("The three letter city code") + .index(2) + .validator(city_code_validator), + ) + .arg( + clap::Arg::with_name("hostname") + .help("The hostname") + .index(3), + ) +} + +pub fn get_constraint(matches: &clap::ArgMatches<'_>) -> Constraint<LocationConstraint> { + let country = matches.value_of("country").unwrap(); + let city = matches.value_of("city"); + let hostname = matches.value_of("hostname"); + + match (country, city, hostname) { + ("any", None, None) => Constraint::Any, + ("any", ..) => clap::Error::with_description( + "City can't be given when selecting 'any' country", + clap::ErrorKind::InvalidValue, + ) + .exit(), + (country, None, None) => Constraint::Only(LocationConstraint::Country(country.to_owned())), + (country, Some(city), None) => Constraint::Only(LocationConstraint::City( + country.to_owned(), + city.to_owned(), + )), + (country, Some(city), Some(hostname)) => Constraint::Only(LocationConstraint::Hostname( + country.to_owned(), + city.to_owned(), + hostname.to_owned(), + )), + (..) => clap::Error::with_description( + "Invalid country, city and hostname combination given", + clap::ErrorKind::InvalidValue, + ) + .exit(), + } +} + +fn country_code_validator(code: String) -> ::std::result::Result<(), String> { + if code.len() == 2 || code == "any" { + Ok(()) + } else { + Err(String::from("Country codes must be two letters, or 'any'.")) + } +} + +fn city_code_validator(code: String) -> ::std::result::Result<(), String> { + if code.len() == 3 { + Ok(()) + } else { + Err(String::from("City codes must be three letters")) + } +} diff --git a/mullvad-cli/src/main.rs b/mullvad-cli/src/main.rs index 3dc1879fb8..4abe16224c 100644 --- a/mullvad-cli/src/main.rs +++ b/mullvad-cli/src/main.rs @@ -14,6 +14,7 @@ use std::io; use talpid_types::ErrorExt; mod cmds; +mod location; pub const PRODUCT_VERSION: &str = include_str!(concat!(env!("OUT_DIR"), "/product-version.txt")); diff --git a/mullvad-daemon/src/lib.rs b/mullvad-daemon/src/lib.rs index 50b8dae23f..1d5851d94f 100644 --- a/mullvad-daemon/src/lib.rs +++ b/mullvad-daemon/src/lib.rs @@ -446,7 +446,8 @@ where RelaySettings::CustomTunnelEndpoint(custom_relay) => { self.last_generated_relay = None; custom_relay - .to_tunnel_parameters(self.settings.get_tunnel_options().clone()) + // TODO(emilsp): generate proxy settings for custom tunnels + .to_tunnel_parameters(self.settings.get_tunnel_options().clone(), None) .map_err(|e| { e.display_chain_with_msg("Custom tunnel endpoint could not be resolved") }) @@ -491,7 +492,7 @@ where account_token: String, retry_attempt: u32, ) -> Result<TunnelParameters> { - let mut tunnel_options = self.settings.get_tunnel_options().clone(); + let tunnel_options = self.settings.get_tunnel_options().clone(); let location = relay.location.as_ref().expect("Relay has no location set"); match endpoint { MullvadEndpoint::OpenVpn(endpoint) => { @@ -529,7 +530,6 @@ where } } }; - tunnel_options.openvpn.proxy = proxy_settings; Ok(openvpn::TunnelParameters { config: openvpn::ConnectionConfig::new( @@ -539,6 +539,7 @@ where ), options: tunnel_options.openvpn, generic_options: tunnel_options.generic, + proxy: proxy_settings, } .into()) } @@ -620,7 +621,10 @@ where } SetAutoConnect(tx, auto_connect) => self.on_set_auto_connect(tx, auto_connect), SetOpenVpnMssfix(tx, mssfix_arg) => self.on_set_openvpn_mssfix(tx, mssfix_arg), - SetOpenVpnProxy(tx, proxy) => self.on_set_openvpn_proxy(tx, proxy), + SetBridgeSettings(tx, bridge_settings) => { + self.on_set_bridge_settings(tx, bridge_settings) + } + SetBridgeState(tx, bridge_state) => self.on_set_bridge_state(tx, bridge_state), SetEnableIpv6(tx, enable_ipv6) => self.on_set_enable_ipv6(tx, enable_ipv6), SetWireguardMtu(tx, mtu) => self.on_set_wireguard_mtu(tx, mtu), GetSettings(tx) => self.on_get_settings(tx), @@ -876,35 +880,62 @@ where } } - fn on_set_openvpn_proxy( + fn on_set_bridge_settings( &mut self, tx: oneshot::Sender<::std::result::Result<(), settings::Error>>, - proxy: Option<openvpn::ProxySettings>, + new_settings: BridgeSettings, ) { - let constraints_result = match proxy { - Some(_) => self.apply_proxy_constraints(), - _ => Ok(false), - }; - let proxy_result = self.settings.set_openvpn_proxy(proxy); + match self.settings.set_bridge_settings(new_settings) { + Ok(settings_changes) => { + if settings_changes { + self.event_listener.notify_settings(self.settings.clone()); + self.reconnect_tunnel(); + }; + Self::oneshot_send(tx, Ok(()), "set_bridge_settings"); + } + + Err(e) => { + log::error!( + "{}", + e.display_chain_with_msg("Failed to set new bridge settings") + ); + Self::oneshot_send(tx, Err(e), "set_bridge_settings"); + } + } + } + + fn on_set_bridge_state( + &mut self, + tx: oneshot::Sender<::std::result::Result<(), settings::Error>>, + bridge_state: BridgeState, + ) { + let result = match self.settings.set_bridge_state(bridge_state.clone()) { + Ok(settings_changed) => { + if settings_changed { + if bridge_state == BridgeState::On { + if let Err(e) = self.apply_proxy_constraints() { + log::error!( + "{}", + e.display_chain_with_msg("Failed to apply proxy constraints") + ); + } + } - match (proxy_result, constraints_result) { - (Ok(proxy_changed), Ok(constraints_changed)) => { - Self::oneshot_send(tx, Ok(()), "set_openvpn_proxy response"); - if proxy_changed || constraints_changed { self.event_listener.notify_settings(self.settings.clone()); - info!("Initiating tunnel restart because the OpenVPN proxy setting changed"); + log::info!("Initiating tunnel restart because bridge state changed"); self.reconnect_tunnel(); } + Ok(()) } - (Ok(_), Err(error)) | (Err(error), Ok(_)) => { - error!("{}", error.display_chain()); - Self::oneshot_send(tx, Err(error), "set_openvpn_proxy response"); - } - (Err(error), Err(_)) => { - error!("{}", error.display_chain()); - Self::oneshot_send(tx, Err(error), "set_openvpn_proxy response"); + Err(error) => { + log::error!( + "{}", + error.display_chain_with_msg("Failed to set new bridge state") + ); + Err(error) } - } + }; + Self::oneshot_send(tx, result, "on_set_bridge_state response"); } // Set the OpenVPN tunnel to use TCP. diff --git a/mullvad-daemon/src/management_interface.rs b/mullvad-daemon/src/management_interface.rs index 9d212abef8..7c195d74e2 100644 --- a/mullvad-daemon/src/management_interface.rs +++ b/mullvad-daemon/src/management_interface.rs @@ -15,7 +15,7 @@ use mullvad_rpc; use mullvad_types::{ account::{AccountData, AccountToken}, location::GeoIpLocation, - relay_constraints::RelaySettingsUpdate, + relay_constraints::{BridgeSettings, BridgeState, RelaySettingsUpdate}, relay_list::RelayList, settings::{self, Settings}, states::TargetState, @@ -27,11 +27,7 @@ use std::{ }; use talpid_core::mpsc::IntoSender; use talpid_ipc; -use talpid_types::{ - net::{openvpn, wireguard}, - tunnel::TunnelStateTransition, - ErrorExt, -}; +use talpid_types::{net::wireguard, tunnel::TunnelStateTransition, ErrorExt}; use uuid; /// FIXME(linus): This is here just because the futures crate has deprecated it and jsonrpc_core @@ -115,8 +111,12 @@ build_rpc_trait! { fn set_openvpn_mssfix(&self, Self::Metadata, Option<u16>) -> BoxFuture<(), Error>; /// Sets proxy details for OpenVPN - #[rpc(meta, name = "set_openvpn_proxy")] - fn set_openvpn_proxy(&self, Self::Metadata, Option<openvpn::ProxySettings>) -> BoxFuture<(), Error>; + #[rpc(meta, name = "set_bridge_settings")] + fn set_bridge_settings(&self, Self::Metadata, BridgeSettings) -> BoxFuture<(), Error>; + + /// Sets bridge state + #[rpc(meta, name = "set_bridge_state")] + fn set_bridge_state(&self, Self::Metadata, BridgeState) -> BoxFuture<(), Error>; /// Set if IPv6 is enabled in the tunnel #[rpc(meta, name = "set_enable_ipv6")] @@ -202,10 +202,9 @@ pub enum ManagementCommand { /// Set the mssfix argument for OpenVPN SetOpenVpnMssfix(OneshotSender<()>, Option<u16>), /// Set proxy details for OpenVPN - SetOpenVpnProxy( - OneshotSender<Result<(), settings::Error>>, - Option<openvpn::ProxySettings>, - ), + SetBridgeSettings(OneshotSender<Result<(), settings::Error>>, BridgeSettings), + /// Set proxy state + SetBridgeState(OneshotSender<Result<(), settings::Error>>, BridgeState), /// Set if IPv6 should be enabled in the tunnel SetEnableIpv6(OneshotSender<()>, bool), /// Set MTU for wireguard tunnels @@ -546,15 +545,15 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } - fn set_openvpn_proxy( + fn set_bridge_settings( &self, _: Self::Metadata, - proxy: Option<openvpn::ProxySettings>, + bridge_settings: BridgeSettings, ) -> BoxFuture<(), Error> { - log::debug!("set_openvpn_proxy({:?})", proxy); + log::debug!("set_bridge_settings({:?})", bridge_settings); let (tx, rx) = sync::oneshot::channel(); let future = self - .send_command_to_daemon(ManagementCommand::SetOpenVpnProxy(tx, proxy)) + .send_command_to_daemon(ManagementCommand::SetBridgeSettings(tx, bridge_settings)) .and_then(|_| rx.map_err(|_| Error::internal_error())) .and_then(|settings_result| { settings_result.map_err(|error| match error { @@ -568,6 +567,21 @@ impl<T: From<ManagementCommand> + 'static + Send> ManagementInterfaceApi Box::new(future) } + fn set_bridge_state( + &self, + _: Self::Metadata, + bridge_state: BridgeState, + ) -> BoxFuture<(), Error> { + log::debug!("set_bridge_state({:?})", bridge_state); + let (tx, rx) = sync::oneshot::channel(); + let future = self + .send_command_to_daemon(ManagementCommand::SetBridgeState(tx, bridge_state)) + .and_then(|_| rx.map_err(|_| Error::internal_error())) + .and_then(|settings_result| settings_result.map_err(|_| Error::internal_error())); + + Box::new(future) + } + fn set_enable_ipv6(&self, _: Self::Metadata, enable_ipv6: bool) -> BoxFuture<(), Error> { log::debug!("set_enable_ipv6({})", enable_ipv6); let (tx, rx) = sync::oneshot::channel(); diff --git a/mullvad-daemon/src/relays.rs b/mullvad-daemon/src/relays.rs index 955d577994..b2a0b14387 100644 --- a/mullvad-daemon/src/relays.rs +++ b/mullvad-daemon/src/relays.rs @@ -284,6 +284,12 @@ impl RelaySelector { if !self.should_use_bridge(retry_attempt) { return None; } + + // For now, only TCP tunnels are supported. + if let &Constraint::Only(TransportProtocol::Udp) = &bridge_constraints.transport_protocol { + return None; + } + self.get_proxy_settings(bridge_constraints, location) } diff --git a/mullvad-ipc-client/src/lib.rs b/mullvad-ipc-client/src/lib.rs index 1c7433a6f9..29f8985287 100644 --- a/mullvad-ipc-client/src/lib.rs +++ b/mullvad-ipc-client/src/lib.rs @@ -6,7 +6,7 @@ use jsonrpc_client_ipc::IpcTransport; use mullvad_types::{ account::{AccountData, AccountToken}, location::GeoIpLocation, - relay_constraints::{RelaySettings, RelaySettingsUpdate}, + relay_constraints::{BridgeSettings, BridgeState, RelaySettings, RelaySettingsUpdate}, relay_list::RelayList, settings::{Settings, TunnelOptions}, version::AppVersionInfo, @@ -14,10 +14,7 @@ use mullvad_types::{ }; use serde::{Deserialize, Serialize}; use std::{io, path::Path, thread}; -use talpid_types::{ - net::{openvpn, wireguard}, - tunnel::TunnelStateTransition, -}; +use talpid_types::{net::wireguard, tunnel::TunnelStateTransition}; static NO_ARGS: [u8; 0] = []; @@ -194,8 +191,12 @@ impl DaemonRpcClient { self.call("set_openvpn_mssfix", &[mssfix]) } - pub fn set_openvpn_proxy(&mut self, proxy: Option<openvpn::ProxySettings>) -> Result<()> { - self.call("set_openvpn_proxy", &[proxy]) + pub fn set_bridge_settings(&mut self, settings: BridgeSettings) -> Result<()> { + self.call("set_bridge_settings", &[settings]) + } + + pub fn set_bridge_state(&mut self, state: BridgeState) -> Result<()> { + self.call("set_bridge_state", &[state]) } pub fn shutdown(&mut self) -> Result<()> { diff --git a/mullvad-types/src/custom_tunnel.rs b/mullvad-types/src/custom_tunnel.rs index ac46b609a3..9559711314 100644 --- a/mullvad-types/src/custom_tunnel.rs +++ b/mullvad-types/src/custom_tunnel.rs @@ -31,6 +31,7 @@ impl CustomTunnelEndpoint { pub fn to_tunnel_parameters( &self, tunnel_options: TunnelOptions, + proxy: Option<openvpn::ProxySettings>, ) -> Result<TunnelParameters, Error> { let ip = resolve_to_ip(&self.host)?; let mut config = self.config.clone(); @@ -41,6 +42,7 @@ impl CustomTunnelEndpoint { config, options: tunnel_options.openvpn.clone(), generic_options: tunnel_options.generic.clone(), + proxy, } .into(), ConnectionConfig::Wireguard(connection) => wireguard::TunnelParameters { diff --git a/mullvad-types/src/relay_constraints.rs b/mullvad-types/src/relay_constraints.rs index 47170adfef..cd901c6bbc 100644 --- a/mullvad-types/src/relay_constraints.rs +++ b/mullvad-types/src/relay_constraints.rs @@ -248,6 +248,15 @@ pub struct BridgeConstraints { pub location: Constraint<LocationConstraint>, } +impl fmt::Display for BridgeConstraints { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> Result<(), fmt::Error> { + match self.location { + Constraint::Any => write!(f, "any location"), + Constraint::Only(ref location_constraint) => location_constraint.fmt(f), + } + } +} + #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] #[serde(rename_all = "snake_case")] pub enum BridgeState { @@ -256,6 +265,20 @@ pub enum BridgeState { Off, } +impl fmt::Display for BridgeState { + fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result { + write!( + f, + "{}", + match self { + BridgeState::Auto => "auto", + BridgeState::On => "on", + BridgeState::Off => "off", + } + ) + } +} + #[derive(Debug, Clone, Eq, PartialEq, Deserialize, Serialize)] pub struct InternalBridgeConstraints { pub location: Constraint<LocationConstraint>, diff --git a/mullvad-types/src/settings.rs b/mullvad-types/src/settings.rs index 32047d31be..10e5b03298 100644 --- a/mullvad-types/src/settings.rs +++ b/mullvad-types/src/settings.rs @@ -207,19 +207,6 @@ impl Settings { } } - pub fn set_openvpn_proxy(&mut self, proxy: Option<openvpn::ProxySettings>) -> Result<bool> { - if let Some(ref settings) = proxy { - openvpn::validate_proxy_settings(settings).map_err(Error::InvalidProxyData)?; - } - - if self.tunnel_options.openvpn.proxy != proxy { - self.tunnel_options.openvpn.proxy = proxy; - self.save().map(|_| true) - } else { - Ok(false) - } - } - pub fn set_enable_ipv6(&mut self, enable_ipv6: bool) -> Result<bool> { if self.tunnel_options.generic.enable_ipv6 != enable_ipv6 { self.tunnel_options.generic.enable_ipv6 = enable_ipv6; diff --git a/talpid-core/src/process/openvpn.rs b/talpid-core/src/process/openvpn.rs index 50c0c61206..c3e7bfcf16 100644 --- a/talpid-core/src/process/openvpn.rs +++ b/talpid-core/src/process/openvpn.rs @@ -62,6 +62,7 @@ pub struct OpenVpnCommand { plugin: Option<(PathBuf, Vec<String>)>, log: Option<PathBuf>, tunnel_options: net::openvpn::TunnelOptions, + proxy_settings: Option<net::openvpn::ProxySettings>, tunnel_alias: Option<OsString>, enable_ipv6: bool, proxy_port: Option<u16>, @@ -83,6 +84,7 @@ impl OpenVpnCommand { plugin: None, log: None, tunnel_options: net::openvpn::TunnelOptions::default(), + proxy_settings: None, tunnel_alias: None, enable_ipv6: true, proxy_port: None, @@ -283,7 +285,7 @@ impl OpenVpnCommand { fn proxy_arguments(&self) -> Vec<String> { let mut args = vec![]; - match self.tunnel_options.proxy { + match self.proxy_settings { Some(net::openvpn::ProxySettings::Local(ref local_proxy)) => { args.push("--socks-proxy".to_owned()); args.push("127.0.0.1".to_owned()); diff --git a/talpid-core/src/tunnel/openvpn.rs b/talpid-core/src/tunnel/openvpn.rs index 8186ab9683..f6174c5e7c 100644 --- a/talpid-core/src/tunnel/openvpn.rs +++ b/talpid-core/src/tunnel/openvpn.rs @@ -147,8 +147,8 @@ impl OpenVpnMonitor<OpenVpnCommand> { Self::create_credentials_file(¶ms.config.username, ¶ms.config.password) .map_err(Error::CredentialsWriteError)?; - let proxy_auth_file = Self::create_proxy_auth_file(¶ms.options.proxy) - .map_err(Error::CredentialsWriteError)?; + let proxy_auth_file = + Self::create_proxy_auth_file(¶ms.proxy).map_err(Error::CredentialsWriteError)?; let user_pass_file_path = user_pass_file.to_path_buf(); @@ -184,7 +184,7 @@ impl OpenVpnMonitor<OpenVpnCommand> { log_dir, }; - let proxy_monitor = Self::start_proxy(¶ms.options.proxy, &proxy_resources)?; + let proxy_monitor = Self::start_proxy(¶ms.proxy, &proxy_resources)?; let cmd = Self::create_openvpn_cmd( params, diff --git a/talpid-core/src/tunnel_state_machine/connected_state.rs b/talpid-core/src/tunnel_state_machine/connected_state.rs index 324d2cbbc8..bc1ca6d4af 100644 --- a/talpid-core/src/tunnel_state_machine/connected_state.rs +++ b/talpid-core/src/tunnel_state_machine/connected_state.rs @@ -61,7 +61,7 @@ impl ConnectedState { fn get_endpoint_from_params(&self) -> Endpoint { match self.tunnel_parameters { - TunnelParameters::OpenVpn(ref config) => match config.options.proxy { + TunnelParameters::OpenVpn(ref config) => match config.proxy { Some(ref proxy_settings) => proxy_settings.get_endpoint(), None => self.tunnel_parameters.get_tunnel_endpoint().endpoint, }, diff --git a/talpid-core/src/tunnel_state_machine/connecting_state.rs b/talpid-core/src/tunnel_state_machine/connecting_state.rs index ddb591bad0..1f6afc16ad 100644 --- a/talpid-core/src/tunnel_state_machine/connecting_state.rs +++ b/talpid-core/src/tunnel_state_machine/connecting_state.rs @@ -286,7 +286,7 @@ fn get_openvpn_proxy_settings( tunnel_parameters: &TunnelParameters, ) -> &Option<openvpn::ProxySettings> { match tunnel_parameters { - TunnelParameters::OpenVpn(ref config) => &config.options.proxy, + TunnelParameters::OpenVpn(ref config) => &config.proxy, _ => &None, } } diff --git a/talpid-types/src/net/openvpn.rs b/talpid-types/src/net/openvpn.rs index 4eaa2cb218..a763b01b12 100644 --- a/talpid-types/src/net/openvpn.rs +++ b/talpid-types/src/net/openvpn.rs @@ -7,6 +7,7 @@ pub struct TunnelParameters { pub config: ConnectionConfig, pub options: TunnelOptions, pub generic_options: GenericTunnelOptions, + pub proxy: Option<ProxySettings>, } #[derive(Debug, Clone, Eq, PartialEq, Hash, Deserialize, Serialize)] @@ -40,8 +41,6 @@ pub struct TunnelOptions { /// Optional argument for openvpn to try and limit TCP packet size, /// as discussed [here](https://openvpn.net/archive/openvpn-users/2003-11/msg00154.html) pub mssfix: Option<u16>, - /// Proxy settings, for when the relay connection should be via a proxy. - pub proxy: Option<ProxySettings>, } #[derive(Debug, Clone, PartialEq, Eq, Hash, Deserialize, Serialize)] |
