summaryrefslogtreecommitdiffhomepage
path: root/mullvad-cli
diff options
context:
space:
mode:
authorOdd Stranne <odd@mullvad.net>2018-10-30 15:30:03 +0100
committerOdd Stranne <odd@mullvad.net>2018-10-30 15:30:03 +0100
commit2b427dc7fe3117a6eef79890e5f30923982878df (patch)
tree7354b6bbc63e29767c00c55f7772a9ecfd51c5d5 /mullvad-cli
parent313a6657a0c3d35e6e83836d6edf99a87d423fc3 (diff)
parent10045211735d5040de7df927a13dc4182360865d (diff)
downloadmullvadvpn-2b427dc7fe3117a6eef79890e5f30923982878df.tar.xz
mullvadvpn-2b427dc7fe3117a6eef79890e5f30923982878df.zip
Merge branch 'openvpn-proxy'
Diffstat (limited to 'mullvad-cli')
-rw-r--r--mullvad-cli/src/cmds/tunnel.rs370
1 files changed, 277 insertions, 93 deletions
diff --git a/mullvad-cli/src/cmds/tunnel.rs b/mullvad-cli/src/cmds/tunnel.rs
index 8d601cd345..329f72f6bd 100644
--- a/mullvad-cli/src/cmds/tunnel.rs
+++ b/mullvad-cli/src/cmds/tunnel.rs
@@ -1,7 +1,12 @@
-use clap;
+use clap::{self, value_t};
use {new_rpc_client, Command, Result};
-use talpid_types::net::{OpenVpnTunnelOptions, TunnelOptions};
+use talpid_types::net::{
+ LocalOpenVpnProxySettings, OpenVpnProxyAuth, OpenVpnProxySettings,
+ OpenVpnProxySettingsValidation, RemoteOpenVpnProxySettings, TunnelOptions,
+};
+
+use std::net::{IpAddr, SocketAddr};
pub struct Tunnel;
@@ -14,134 +19,313 @@ impl Command for Tunnel {
clap::SubCommand::with_name(self.name())
.about("Manage tunnel specific options")
.setting(clap::AppSettings::SubcommandRequired)
- .subcommand(
- clap::SubCommand::with_name("openvpn")
- .about("Manage options for OpenVPN tunnels")
- .setting(clap::AppSettings::SubcommandRequired)
- .subcommand(
- clap::SubCommand::with_name("set")
- .subcommand(
- clap::SubCommand::with_name("mssfix").arg(
- clap::Arg::with_name("mssfix")
- .help(
- "Sets the optional mssfix parameter. \
- Set an empty string to clear it.",
- )
- .required(true),
- ),
- )
- .setting(clap::AppSettings::SubcommandRequired),
- )
- .subcommand(
- clap::SubCommand::with_name("get")
- .help("Retrieves the current setting for mssfix"),
- ),
- )
- .subcommand(
- clap::SubCommand::with_name("set")
- .subcommand(
- clap::SubCommand::with_name("ipv6").arg(
- clap::Arg::with_name("enable")
- .required(true)
- .takes_value(true)
- .possible_values(&["on", "off"]),
- ),
- )
- .setting(clap::AppSettings::SubcommandRequired),
- )
- .subcommand(
- clap::SubCommand::with_name("get")
- .help("Retrieves the current setting for common tunnel options"),
- )
+ .subcommand(create_openvpn_subcommand())
+ .subcommand(create_ipv6_subcommand())
}
fn run(&self, matches: &clap::ArgMatches) -> Result<()> {
if let Some(openvpn_matches) = matches.subcommand_matches("openvpn") {
Self::handle_openvpn_cmd(openvpn_matches)
- } else if let Some(set_matches) = matches.subcommand_matches("set") {
- Self::set_tunnel_option(set_matches)
- } else if let Some(_) = matches.subcommand_matches("get") {
- let tunnel_options = Self::get_tunnel_options()?;
- Self::print_common_tunnel_options(&tunnel_options);
- Ok(())
+ } else if let Some(ipv6_matches) = matches.subcommand_matches("ipv6") {
+ Self::handle_ipv6_cmd(ipv6_matches)
} else {
- unreachable!("No tunnel command given")
+ unreachable!("unhandled command");
}
}
}
+fn create_openvpn_subcommand() -> clap::App<'static, 'static> {
+ clap::SubCommand::with_name("openvpn")
+ .about("Manage options for OpenVPN tunnels")
+ .setting(clap::AppSettings::SubcommandRequired)
+ .subcommand(create_openvpn_mssfix_subcommand())
+ .subcommand(create_openvpn_proxy_subcommand())
+}
+
+fn create_openvpn_mssfix_subcommand() -> clap::App<'static, 'static> {
+ clap::SubCommand::with_name("mssfix")
+ .about("Configure the optional mssfix parameter")
+ .setting(clap::AppSettings::SubcommandRequired)
+ .subcommand(clap::SubCommand::with_name("get"))
+ .subcommand(clap::SubCommand::with_name("unset"))
+ .subcommand(
+ clap::SubCommand::with_name("set").arg(clap::Arg::with_name("mssfix").required(true)),
+ )
+}
+
+fn create_openvpn_proxy_subcommand() -> clap::App<'static, 'static> {
+ clap::SubCommand::with_name("proxy")
+ .about("Configure a SOCKS5 proxy")
+ .setting(clap::AppSettings::SubcommandRequired)
+ .subcommand(clap::SubCommand::with_name("get"))
+ .subcommand(clap::SubCommand::with_name("unset"))
+ .subcommand(
+ clap::SubCommand::with_name("set")
+ .setting(clap::AppSettings::SubcommandRequired)
+ .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")
+ .index(3),
+ )
+ .arg(
+ clap::Arg::with_name("password")
+ .help("Specifies the password for remote authentication")
+ .index(4),
+ ),
+ ),
+ )
+}
+
+fn create_ipv6_subcommand() -> clap::App<'static, 'static> {
+ clap::SubCommand::with_name("ipv6")
+ .setting(clap::AppSettings::SubcommandRequired)
+ .subcommand(clap::SubCommand::with_name("get"))
+ .subcommand(
+ clap::SubCommand::with_name("set").arg(
+ clap::Arg::with_name("enable")
+ .required(true)
+ .takes_value(true)
+ .possible_values(&["on", "off"]),
+ ),
+ )
+}
+
impl Tunnel {
- fn set_tunnel_option(matches: &clap::ArgMatches) -> Result<()> {
- if let Some(ipv6_args) = matches.subcommand_matches("ipv6") {
- Self::set_enable_ipv6_option(ipv6_args)
+ fn handle_openvpn_cmd(matches: &clap::ArgMatches) -> Result<()> {
+ if let Some(m) = matches.subcommand_matches("mssfix") {
+ Self::handle_openvpn_mssfix_cmd(m)
+ } else if let Some(m) = matches.subcommand_matches("proxy") {
+ Self::handle_openvpn_proxy_cmd(m)
} else {
- unreachable!("Invalid option passed to 'tunnel set'");
+ unreachable!("unhandled command");
}
}
- fn set_enable_ipv6_option(args: &clap::ArgMatches) -> Result<()> {
- let enabled = args.value_of("enable").unwrap() == "on";
+ fn handle_openvpn_mssfix_cmd(matches: &clap::ArgMatches) -> Result<()> {
+ if let Some(_) = matches.subcommand_matches("get") {
+ Self::process_openvpn_mssfix_get()
+ } else if let Some(_) = matches.subcommand_matches("unset") {
+ Self::process_openvpn_mssfix_unset()
+ } else if let Some(m) = matches.subcommand_matches("set") {
+ Self::process_openvpn_mssfix_set(m)
+ } else {
+ unreachable!("unhandled command");
+ }
+ }
- let mut rpc = new_rpc_client()?;
- rpc.set_enable_ipv6(enabled)?;
- println!("IPv6 {}", if enabled { "on" } else { "off" });
- Ok(())
+ fn handle_openvpn_proxy_cmd(matches: &clap::ArgMatches) -> Result<()> {
+ if let Some(_) = matches.subcommand_matches("get") {
+ Self::process_openvpn_proxy_get()
+ } else if let Some(_) = matches.subcommand_matches("unset") {
+ Self::process_openvpn_proxy_unset()
+ } else if let Some(m) = matches.subcommand_matches("set") {
+ Self::process_openvpn_proxy_set(m)
+ } else {
+ unreachable!("unhandled command");
+ }
}
- fn handle_openvpn_cmd(matches: &clap::ArgMatches) -> Result<()> {
- if let Some(set_matches) = matches.subcommand_matches("set") {
- Self::set_openvpn_option(set_matches)
- } else if let Some(_) = matches.subcommand_matches("get") {
- let tunnel_options = Self::get_tunnel_options()?;
- Self::print_openvpn_tunnel_options(tunnel_options.openvpn);
- Ok(())
+ fn handle_ipv6_cmd(matches: &clap::ArgMatches) -> Result<()> {
+ if let Some(_) = matches.subcommand_matches("get") {
+ Self::process_ipv6_get()
+ } else if let Some(m) = matches.subcommand_matches("set") {
+ Self::process_ipv6_set(m)
} else {
- unreachable!("Unrecognized subcommand");
+ unreachable!("unhandled command");
}
}
- fn set_openvpn_option(matches: &clap::ArgMatches) -> Result<()> {
- if let Some(mssfix_args) = matches.subcommand_matches("mssfix") {
- Self::set_openvpn_mssfix_option(mssfix_args)
+ fn process_openvpn_mssfix_get() -> Result<()> {
+ let tunnel_options = Self::get_tunnel_options()?;
+ println!(
+ "mssfix: {}",
+ tunnel_options
+ .openvpn
+ .mssfix
+ .map_or_else(|| "unset".to_string(), |v| v.to_string())
+ );
+ Ok(())
+ }
+
+ fn get_tunnel_options() -> Result<TunnelOptions> {
+ let mut rpc = new_rpc_client()?;
+ Ok(rpc.get_settings()?.get_tunnel_options().clone())
+ }
+
+ fn process_openvpn_mssfix_unset() -> Result<()> {
+ let mut rpc = new_rpc_client()?;
+ rpc.set_openvpn_mssfix(None)?;
+ println!("mssfix parameter has been unset");
+ Ok(())
+ }
+
+ fn process_openvpn_mssfix_set(matches: &clap::ArgMatches) -> Result<()> {
+ let new_value = value_t!(matches.value_of("mssfix"), u16).unwrap_or_else(|e| e.exit());
+ let mut rpc = new_rpc_client()?;
+ rpc.set_openvpn_mssfix(Some(new_value))?;
+ println!("mssfix parameter has been updated");
+ Ok(())
+ }
+
+ fn process_openvpn_proxy_get() -> Result<()> {
+ let tunnel_options = Self::get_tunnel_options()?;
+ if let Some(proxy) = tunnel_options.openvpn.proxy {
+ if let OpenVpnProxySettings::Local(local_proxy) = proxy {
+ Self::print_local_proxy(&local_proxy)
+ } else if let OpenVpnProxySettings::Remote(remote_proxy) = proxy {
+ Self::print_remote_proxy(&remote_proxy)
+ } else {
+ unreachable!("unhandled proxy type");
+ }
} else {
- unreachable!("Invalid option passed to 'openvpn set'");
+ println!("proxy: unset");
}
+ Ok(())
}
- fn set_openvpn_mssfix_option(args: &clap::ArgMatches) -> Result<()> {
- let mssfix_str = args.value_of("mssfix").unwrap();
- let mssfix: Option<u16> = if mssfix_str == "" {
- None
+ fn print_local_proxy(proxy: &LocalOpenVpnProxySettings) {
+ 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: &RemoteOpenVpnProxySettings) {
+ 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 {
- Some(mssfix_str.parse()?)
- };
+ println!(" auth: none");
+ }
+ }
+ fn process_openvpn_proxy_unset() -> Result<()> {
let mut rpc = new_rpc_client()?;
- rpc.set_openvpn_mssfix(mssfix)?;
- println!("mssfix parameter updated");
+ rpc.set_openvpn_proxy(None)?;
+ println!("proxy details have been unset");
Ok(())
}
- fn get_tunnel_options() -> Result<TunnelOptions> {
- let mut rpc = new_rpc_client()?;
- Ok(rpc.get_settings()?.get_tunnel_options().clone())
+ 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 = LocalOpenVpnProxySettings {
+ port: local_port,
+ peer: SocketAddr::new(remote_ip, remote_port),
+ };
+
+ let packed_proxy = OpenVpnProxySettings::Local(proxy);
+
+ if let Err(error) = OpenVpnProxySettingsValidation::validate(&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(OpenVpnProxyAuth {
+ username: username.to_string(),
+ password: password.to_string(),
+ }),
+ _ => None,
+ };
+
+ let proxy = RemoteOpenVpnProxySettings {
+ address: SocketAddr::new(remote_ip, remote_port),
+ auth: auth,
+ };
+
+ let packed_proxy = OpenVpnProxySettings::Remote(proxy);
+
+ if let Err(error) = OpenVpnProxySettingsValidation::validate(&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 print_common_tunnel_options(options: &TunnelOptions) {
- println!("Common tunnel options");
+ fn process_ipv6_get() -> Result<()> {
+ let tunnel_options = Self::get_tunnel_options()?;
println!(
- "\tIPv6: {}",
- if options.enable_ipv6 { "on" } else { "off" }
+ "IPv6: {}",
+ if tunnel_options.enable_ipv6 {
+ "on"
+ } else {
+ "off"
+ }
);
+ Ok(())
}
- fn print_openvpn_tunnel_options(options: OpenVpnTunnelOptions) {
- println!("OpenVPN tunnel options");
- println!(
- "\tmssfix: {}",
- options
- .mssfix
- .map_or_else(|| "UNSET".to_string(), |v| v.to_string())
- );
+ fn process_ipv6_set(matches: &clap::ArgMatches) -> Result<()> {
+ let enabled = matches.value_of("enable").unwrap() == "on";
+
+ let mut rpc = new_rpc_client()?;
+ rpc.set_enable_ipv6(enabled)?;
+ println!("IPv6 setting has been updated");
+ Ok(())
}
}