From ff90ea7889ab1554562493027f42e80152c93fe4 Mon Sep 17 00:00:00 2001 From: Markus Pettersson Date: Wed, 18 Oct 2023 11:50:54 +0200 Subject: [PATCH] Add configurable transport protocol to local SOCKS5 access method --- mullvad-cli/src/cmds/api_access.rs | 25 +++++++++-- .../proto/management_interface.proto | 1 + .../src/types/conversions/access_method.rs | 41 ++++++++++++------- mullvad-types/src/access_method.rs | 34 +++++++++++++-- 4 files changed, 81 insertions(+), 20 deletions(-) diff --git a/mullvad-cli/src/cmds/api_access.rs b/mullvad-cli/src/cmds/api_access.rs index a03d3ba11fbb..c0ff176cc7b4 100644 --- a/mullvad-cli/src/cmds/api_access.rs +++ b/mullvad-cli/src/cmds/api_access.rs @@ -4,7 +4,7 @@ use mullvad_types::access_method::{AccessMethod, AccessMethodSetting, CustomAcce use std::net::IpAddr; use clap::{Args, Subcommand}; -use talpid_types::net::openvpn::SHADOWSOCKS_CIPHERS; +use talpid_types::net::{openvpn::SHADOWSOCKS_CIPHERS, TransportProtocol}; #[derive(Subcommand, Debug, Clone)] pub enum ApiAccess { @@ -119,8 +119,13 @@ impl ApiAccess { let ip = cmd.params.ip.unwrap_or(local.peer.ip()).to_string(); let port = cmd.params.port.unwrap_or(local.peer.port()); let local_port = cmd.params.local_port.unwrap_or(local.port); - mullvad_types::access_method::Socks5Local::from_args(ip, port, local_port) - .map(AccessMethod::from) + mullvad_types::access_method::Socks5Local::from_args( + ip, + port, + local_port, + local.peer_transport_protol, + ) + .map(AccessMethod::from) } mullvad_types::access_method::Socks5::Remote(remote) => { let ip = cmd.params.ip.unwrap_or(remote.peer.ip()).to_string(); @@ -315,6 +320,18 @@ pub enum AddSocks5Commands { remote_ip: IpAddr, /// The port of the remote peer remote_port: u16, + /// The Mullvad App can not know which transport protocol that the + /// remote peer accepts, but it needs to know this in order to correctly + /// exempt the connection traffic in the firewall. + /// + /// By default, the transport protocol is assumed to be `TCP`, but it + /// can optionally be set to `UDP` as well. + /// + /// There exists obfuscation protocols which use other transport + /// protocols, such as ICMP. However, by supporting both TCP and UDP we + /// should cover ~99% of all use cases. + #[arg(default_value_t = TransportProtocol::Tcp)] + remote_transport_protocol: TransportProtocol, /// Disable the use of this custom access method. It has to be manually /// enabled at a later stage to be used when accessing the Mullvad API. #[arg(default_value_t = false, short, long)] @@ -430,12 +447,14 @@ mod conversions { remote_port, name: _, disabled: _, + remote_transport_protocol, } => { println!("Adding SOCKS5-proxy: localhost:{local_port} => {remote_ip}:{remote_port}"); daemon_types::Socks5Local::from_args( remote_ip.to_string(), remote_port, local_port, + remote_transport_protocol, ) .map(daemon_types::Socks5::Local) .map(daemon_types::AccessMethod::from) diff --git a/mullvad-management-interface/proto/management_interface.proto b/mullvad-management-interface/proto/management_interface.proto index 4846f896e2a1..81dc4edbd6d8 100644 --- a/mullvad-management-interface/proto/management_interface.proto +++ b/mullvad-management-interface/proto/management_interface.proto @@ -343,6 +343,7 @@ message AccessMethod { string ip = 1; uint32 port = 2; uint32 local_port = 3; + TransportProtocol peer_transport_protocol = 4; } message SocksAuth { string username = 1; diff --git a/mullvad-management-interface/src/types/conversions/access_method.rs b/mullvad-management-interface/src/types/conversions/access_method.rs index 8907c4da2947..09a9b1f9fc7a 100644 --- a/mullvad-management-interface/src/types/conversions/access_method.rs +++ b/mullvad-management-interface/src/types/conversions/access_method.rs @@ -142,11 +142,19 @@ mod data { type Error = FromProtobufTypeError; fn try_from(value: proto::access_method::Socks5Local) -> Result { - Socks5Local::from_args(value.ip, value.port as u16, value.local_port as u16) - .ok_or(FromProtobufTypeError::InvalidArgument( - "Could not parse Socks5 (local) message from protobuf", - )) - .map(AccessMethod::from) + use crate::types::conversions::net::try_transport_protocol_from_i32; + let peer_transport_protocol = + try_transport_protocol_from_i32(value.peer_transport_protocol)?; + Socks5Local::from_args( + value.ip, + value.port as u16, + value.local_port as u16, + peer_transport_protocol, + ) + .ok_or(FromProtobufTypeError::InvalidArgument( + "Could not parse Socks5 (local) message from protobuf", + )) + .map(AccessMethod::from) } } @@ -216,15 +224,20 @@ mod data { }, ) } - CustomAccessMethod::Socks5(Socks5::Local(Socks5Local { peer, port })) => { - proto::access_method::AccessMethod::Socks5local( - proto::access_method::Socks5Local { - ip: peer.ip().to_string(), - port: peer.port() as u32, - local_port: port as u32, - }, - ) - } + CustomAccessMethod::Socks5(Socks5::Local(Socks5Local { + peer, + port, + peer_transport_protol, + })) => proto::access_method::AccessMethod::Socks5local( + proto::access_method::Socks5Local { + ip: peer.ip().to_string(), + port: peer.port() as u32, + local_port: port as u32, + peer_transport_protocol: i32::from(proto::TransportProtocol::from( + peer_transport_protol, + )), + }, + ), CustomAccessMethod::Socks5(Socks5::Remote(Socks5Remote { peer, authentication, diff --git a/mullvad-types/src/access_method.rs b/mullvad-types/src/access_method.rs index 30c1f25192ae..a0028a1e9a21 100644 --- a/mullvad-types/src/access_method.rs +++ b/mullvad-types/src/access_method.rs @@ -2,6 +2,7 @@ use std::str::FromStr; use serde::{Deserialize, Serialize}; use std::net::{IpAddr, Ipv4Addr, SocketAddr, SocketAddrV4}; +use talpid_types::net::TransportProtocol; /// Daemon settings for API access methods. #[derive(Debug, Clone, Serialize, Deserialize, PartialEq)] @@ -202,6 +203,8 @@ pub struct Shadowsocks { #[derive(Clone, Debug, Serialize, Deserialize, PartialEq, Eq, Hash)] pub struct Socks5Local { pub peer: SocketAddr, + /// The transport protocol which should be allowed in the firewall. + pub peer_transport_protol: TransportProtocol, /// Port on localhost where the SOCKS5-proxy listens to. pub port: u16, } @@ -255,15 +258,40 @@ impl Shadowsocks { impl Socks5Local { pub fn new(peer: SocketAddr, port: u16) -> Self { - Self { peer, port } + Self { + peer, + port, + peer_transport_protol: TransportProtocol::Tcp, + } + } + + pub fn new_with_transport_protocol( + peer: SocketAddr, + port: u16, + transport_protocol: TransportProtocol, + ) -> Self { + Self { + peer, + port, + peer_transport_protol: transport_protocol, + } } /// 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 { + pub fn from_args( + ip: String, + port: u16, + localport: u16, + transport_protocol: TransportProtocol, + ) -> Option { let peer_ip = IpAddr::V4(Ipv4Addr::from_str(&ip).ok()?); let peer = SocketAddr::new(peer_ip, port); - Some(Self::new(peer, localport)) + Some(Self::new_with_transport_protocol( + peer, + localport, + transport_protocol, + )) } }