From e692e9b9d2245ec7acdf8aca8a3bfa6c27c0eba2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 10:22:45 +0200 Subject: [PATCH 01/12] Setup ConnectArgs --- .../command_interface/connection_handler.rs | 6 ++++-- nym-vpnd/src/service/mod.rs | 3 ++- nym-vpnd/src/service/vpn_service.rs | 21 ++++++++++++++----- 3 files changed, 22 insertions(+), 8 deletions(-) diff --git a/nym-vpnd/src/command_interface/connection_handler.rs b/nym-vpnd/src/command_interface/connection_handler.rs index ecf7918bf2..3ec2e6cfae 100644 --- a/nym-vpnd/src/command_interface/connection_handler.rs +++ b/nym-vpnd/src/command_interface/connection_handler.rs @@ -5,7 +5,8 @@ use tokio::sync::{mpsc::UnboundedSender, oneshot}; use tracing::{info, warn}; use crate::service::{ - VpnServiceCommand, VpnServiceConnectResult, VpnServiceDisconnectResult, VpnServiceStatusResult, + ConnectArgs, VpnServiceCommand, VpnServiceConnectResult, VpnServiceDisconnectResult, + VpnServiceStatusResult, }; pub(super) struct CommandInterfaceConnectionHandler { @@ -20,8 +21,9 @@ impl CommandInterfaceConnectionHandler { pub(crate) async fn handle_connect(&self) -> VpnServiceConnectResult { info!("Starting VPN"); let (tx, rx) = oneshot::channel(); + let connect_args = ConnectArgs::Default; self.vpn_command_tx - .send(VpnServiceCommand::Connect(tx)) + .send(VpnServiceCommand::Connect(tx, connect_args)) .unwrap(); info!("Sent start command to VPN"); info!("Waiting for response"); diff --git a/nym-vpnd/src/service/mod.rs b/nym-vpnd/src/service/mod.rs index 2f0d6cf043..0e15047c8b 100644 --- a/nym-vpnd/src/service/mod.rs +++ b/nym-vpnd/src/service/mod.rs @@ -9,5 +9,6 @@ mod vpn_service; pub(crate) use start::start_vpn_service; pub(crate) use vpn_service::{ - VpnServiceCommand, VpnServiceConnectResult, VpnServiceDisconnectResult, VpnServiceStatusResult, + ConnectArgs, VpnServiceCommand, VpnServiceConnectResult, VpnServiceDisconnectResult, + VpnServiceStatusResult, }; diff --git a/nym-vpnd/src/service/vpn_service.rs b/nym-vpnd/src/service/vpn_service.rs index 55c1bf4c9e..9e3a9f6935 100644 --- a/nym-vpnd/src/service/vpn_service.rs +++ b/nym-vpnd/src/service/vpn_service.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use futures::channel::mpsc::UnboundedSender; use futures::SinkExt; -use nym_vpn_lib::gateway_directory; +use nym_vpn_lib::gateway_directory::{self}; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::oneshot; use tracing::info; @@ -28,11 +28,20 @@ pub enum VpnState { #[derive(Debug)] pub enum VpnServiceCommand { - Connect(oneshot::Sender), + Connect(oneshot::Sender, ConnectArgs), Disconnect(oneshot::Sender), Status(oneshot::Sender), } +#[derive(Debug)] +pub enum ConnectArgs { + // Read the entry and exit points from the config file. + Default, + + #[allow(unused)] + Custom(String, String), +} + #[derive(Debug)] pub enum VpnServiceConnectResult { Success, @@ -104,9 +113,11 @@ impl NymVpnService { Ok(config) } - async fn handle_connect(&mut self) -> VpnServiceConnectResult { + async fn handle_connect(&mut self, _connect_args: ConnectArgs) -> VpnServiceConnectResult { self.set_shared_state(VpnState::Connecting); + // TODO: use connect_args here + let config = match self.try_setup_config() { Ok(config) => config, Err(err) => { @@ -175,8 +186,8 @@ impl NymVpnService { while let Some(command) = self.vpn_command_rx.recv().await { info!("VPN: Received command: {:?}", command); match command { - VpnServiceCommand::Connect(tx) => { - let result = self.handle_connect().await; + VpnServiceCommand::Connect(tx, connect_args) => { + let result = self.handle_connect(connect_args).await; tx.send(result).unwrap(); } VpnServiceCommand::Disconnect(tx) => { From 9f68e48c3d1738c2985a8a3faab0e55266a58f7d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 10:42:35 +0200 Subject: [PATCH 02/12] Fix removing old socket file when starting --- nym-vpnd/src/command_interface/listener.rs | 2 +- nym-vpnd/src/command_interface/start.rs | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/nym-vpnd/src/command_interface/listener.rs b/nym-vpnd/src/command_interface/listener.rs index 73bc4d05b6..39eab89a2d 100644 --- a/nym-vpnd/src/command_interface/listener.rs +++ b/nym-vpnd/src/command_interface/listener.rs @@ -48,7 +48,7 @@ impl CommandInterface { } } - fn remove_previous_socket_file(&self) { + pub(super) fn remove_previous_socket_file(&self) { if let ListenerType::Path(ref socket_path) = self.listener { match fs::remove_file(socket_path) { Ok(_) => info!( diff --git a/nym-vpnd/src/command_interface/start.rs b/nym-vpnd/src/command_interface/start.rs index 97ef2f82ef..215f7a98e6 100644 --- a/nym-vpnd/src/command_interface/start.rs +++ b/nym-vpnd/src/command_interface/start.rs @@ -32,6 +32,7 @@ fn spawn_socket_listener(vpn_command_tx: UnboundedSender, soc info!("Starting socket listener on: {}", socket_path.display()); tokio::task::spawn(async move { let command_interface = CommandInterface::new_with_path(vpn_command_tx, &socket_path); + command_interface.remove_previous_socket_file(); let incoming = setup_socket_stream(&socket_path); Server::builder() .add_service(NymVpndServer::new(command_interface)) From 517f8d0bd3015ea4d5cebf630a0b8dec30129846 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 10:43:20 +0200 Subject: [PATCH 03/12] Use persistent keys and storage --- nym-vpnd/src/service/config.rs | 9 +++++++++ nym-vpnd/src/service/vpn_service.rs | 19 ++++++++++++++++--- 2 files changed, 25 insertions(+), 3 deletions(-) diff --git a/nym-vpnd/src/service/config.rs b/nym-vpnd/src/service/config.rs index ddcd72ac46..79d217e2a3 100644 --- a/nym-vpnd/src/service/config.rs +++ b/nym-vpnd/src/service/config.rs @@ -90,3 +90,12 @@ pub(super) fn read_config_file( error, }) } + +pub(super) fn create_data_dir(data_dir: &PathBuf) -> Result<(), ConfigSetupError> { + fs::create_dir_all(data_dir).map_err(|error| ConfigSetupError::CreateDirectory { + dir: data_dir.clone(), + error, + })?; + info!("Data directory created at {:?}", data_dir); + Ok(()) +} diff --git a/nym-vpnd/src/service/vpn_service.rs b/nym-vpnd/src/service/vpn_service.rs index 9e3a9f6935..2dcced0009 100644 --- a/nym-vpnd/src/service/vpn_service.rs +++ b/nym-vpnd/src/service/vpn_service.rs @@ -12,7 +12,7 @@ use tokio::sync::oneshot; use tracing::info; use super::config::{ - create_config_file, read_config_file, ConfigSetupError, NymVpnServiceConfig, + create_config_file, create_data_dir, read_config_file, ConfigSetupError, NymVpnServiceConfig, DEFAULT_CONFIG_DIR, DEFAULT_CONFIG_FILE, DEFAULT_DATA_DIR, }; use super::exit_listener::VpnServiceExitListener; @@ -81,7 +81,6 @@ pub(super) struct NymVpnService { vpn_command_rx: UnboundedReceiver, vpn_ctrl_sender: Option>, config_file: PathBuf, - #[allow(unused)] data_dir: PathBuf, } @@ -126,9 +125,22 @@ impl NymVpnService { } }; - let mut nym_vpn = nym_vpn_lib::NymVpn::new(config.entry_point, config.exit_point); + // Make sure the data dir exists + match create_data_dir(&self.data_dir) { + Ok(()) => {} + Err(err) => { + self.set_shared_state(VpnState::NotConnected); + return VpnServiceConnectResult::Fail(format!( + "Failed to create data directory {:?}: {}", + self.data_dir, err + )); + } + } + let mut nym_vpn = nym_vpn_lib::NymVpn::new(config.entry_point, config.exit_point); nym_vpn.gateway_config = gateway_directory::Config::new_from_env(); + nym_vpn.mixnet_data_path = Some(self.data_dir.clone()); + let handle = nym_vpn_lib::spawn_nym_vpn_with_new_runtime(nym_vpn).unwrap(); let nym_vpn_lib::NymVpnHandle { @@ -151,6 +163,7 @@ impl NymVpnService { } fn set_shared_state(&self, state: VpnState) { + info!("VPN: Setting shared state to {:?}", state); *self.shared_vpn_state.lock().unwrap() = state; } From c1daedbf52a968af48f87556ba0403607f65fd2f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 10:57:06 +0200 Subject: [PATCH 04/12] VpnImportCredential service command --- nym-vpnd/src/service/vpn_service.rs | 20 ++++++++++++++++++++ 1 file changed, 20 insertions(+) diff --git a/nym-vpnd/src/service/vpn_service.rs b/nym-vpnd/src/service/vpn_service.rs index 2dcced0009..00a3c6e0bf 100644 --- a/nym-vpnd/src/service/vpn_service.rs +++ b/nym-vpnd/src/service/vpn_service.rs @@ -31,6 +31,8 @@ pub enum VpnServiceCommand { Connect(oneshot::Sender, ConnectArgs), Disconnect(oneshot::Sender), Status(oneshot::Sender), + #[allow(unused)] + ImportCredential(oneshot::Sender, String), } #[derive(Debug)] @@ -76,6 +78,13 @@ pub enum VpnServiceStatusResult { Disconnecting, } +#[allow(unused)] +#[derive(Debug)] +pub enum VpnImportCredentialResult { + Success, + Fail(String), +} + pub(super) struct NymVpnService { shared_vpn_state: Arc>, vpn_command_rx: UnboundedReceiver, @@ -195,6 +204,13 @@ impl NymVpnService { } } + async fn handle_import_credential( + &mut self, + _credential: String, + ) -> VpnImportCredentialResult { + todo!() + } + pub(super) async fn run(mut self) -> anyhow::Result<()> { while let Some(command) = self.vpn_command_rx.recv().await { info!("VPN: Received command: {:?}", command); @@ -211,6 +227,10 @@ impl NymVpnService { let result = self.handle_status().await; tx.send(result).unwrap(); } + VpnServiceCommand::ImportCredential(tx, credential) => { + let result = self.handle_import_credential(credential).await; + tx.send(result).unwrap(); + } } } Ok(()) From 0281d7061354bdef1a70aba4806c8874b15f851c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 13:51:00 +0200 Subject: [PATCH 05/12] Add connection failed status --- nym-vpnd/src/command_interface/listener.rs | 1 + nym-vpnd/src/service/vpn_service.rs | 13 +++++++------ proto/nym/vpn.proto | 13 +++++++------ 3 files changed, 15 insertions(+), 12 deletions(-) diff --git a/nym-vpnd/src/command_interface/listener.rs b/nym-vpnd/src/command_interface/listener.rs index 39eab89a2d..343a7ddff1 100644 --- a/nym-vpnd/src/command_interface/listener.rs +++ b/nym-vpnd/src/command_interface/listener.rs @@ -131,6 +131,7 @@ impl From for ConnectionStatus { VpnServiceStatusResult::Connecting => ConnectionStatus::Connecting, VpnServiceStatusResult::Connected => ConnectionStatus::Connected, VpnServiceStatusResult::Disconnecting => ConnectionStatus::Disconnecting, + VpnServiceStatusResult::ConnectionFailed(_reason) => ConnectionStatus::ConnectionFailed, } } } diff --git a/nym-vpnd/src/service/vpn_service.rs b/nym-vpnd/src/service/vpn_service.rs index 00a3c6e0bf..410ffcbc1c 100644 --- a/nym-vpnd/src/service/vpn_service.rs +++ b/nym-vpnd/src/service/vpn_service.rs @@ -24,6 +24,8 @@ pub enum VpnState { Connecting, Connected, Disconnecting, + #[allow(unused)] + ConnectionFailed(String), } #[derive(Debug)] @@ -70,12 +72,13 @@ impl VpnServiceDisconnectResult { } } -#[derive(Copy, Clone, Debug)] +#[derive(Clone, Debug)] pub enum VpnServiceStatusResult { NotConnected, Connecting, Connected, Disconnecting, + ConnectionFailed(String), } #[allow(unused)] @@ -196,18 +199,16 @@ impl NymVpnService { } async fn handle_status(&self) -> VpnServiceStatusResult { - match *self.shared_vpn_state.lock().unwrap() { + match self.shared_vpn_state.lock().unwrap().clone() { VpnState::NotConnected => VpnServiceStatusResult::NotConnected, VpnState::Connecting => VpnServiceStatusResult::Connecting, VpnState::Connected => VpnServiceStatusResult::Connected, VpnState::Disconnecting => VpnServiceStatusResult::Disconnecting, + VpnState::ConnectionFailed(reason) => VpnServiceStatusResult::ConnectionFailed(reason), } } - async fn handle_import_credential( - &mut self, - _credential: String, - ) -> VpnImportCredentialResult { + async fn handle_import_credential(&mut self, _credential: String) -> VpnImportCredentialResult { todo!() } diff --git a/proto/nym/vpn.proto b/proto/nym/vpn.proto index be34572829..d62469bdca 100644 --- a/proto/nym/vpn.proto +++ b/proto/nym/vpn.proto @@ -44,12 +44,13 @@ enum VpnMode { } enum ConnectionStatus { - STATUS_UNSPECIFIED = 0; - NOT_CONNECTED = 1; - CONNECTING = 2; - CONNECTED = 3; - DISCONNECTING = 4; - UNKNOWN = 5; // errored pending state etc + UNKNOWN = 0; + STATUS_UNSPECIFIED = 1; + NOT_CONNECTED = 2; + CONNECTING = 3; + CONNECTED = 4; + DISCONNECTING = 5; + CONNECTION_FAILED = 6; } message LocationListResponse { From 4d28b4cdce5581eb65c3c230ea3d2601f9960b61 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 14:05:34 +0200 Subject: [PATCH 06/12] Return reason for connection failure --- nym-vpnd/src/command_interface/listener.rs | 12 +++++++++++- nym-vpnd/src/service/exit_listener.rs | 2 +- proto/nym/vpn.proto | 1 + 3 files changed, 13 insertions(+), 2 deletions(-) diff --git a/nym-vpnd/src/command_interface/listener.rs b/nym-vpnd/src/command_interface/listener.rs index 343a7ddff1..2c12160608 100644 --- a/nym-vpnd/src/command_interface/listener.rs +++ b/nym-vpnd/src/command_interface/listener.rs @@ -9,7 +9,7 @@ use std::{ use nym_vpn_proto::{ nym_vpnd_server::NymVpnd, ConnectRequest, ConnectResponse, ConnectionStatus, DisconnectRequest, - DisconnectResponse, StatusRequest, StatusResponse, + DisconnectResponse, Error as ProtoError, StatusRequest, StatusResponse, }; use tokio::sync::mpsc::UnboundedSender; use tracing::{error, info}; @@ -117,9 +117,19 @@ impl NymVpnd for CommandInterface { .handle_status() .await; + let error = match status { + VpnServiceStatusResult::NotConnected => None, + VpnServiceStatusResult::Connecting => None, + VpnServiceStatusResult::Connected => None, + VpnServiceStatusResult::Disconnecting => None, + VpnServiceStatusResult::ConnectionFailed(ref reason) => Some(reason.clone()), + } + .map(|reason| ProtoError { message: reason }); + info!("Returning status response"); Ok(tonic::Response::new(StatusResponse { status: ConnectionStatus::from(status) as i32, + error, })) } } diff --git a/nym-vpnd/src/service/exit_listener.rs b/nym-vpnd/src/service/exit_listener.rs index aba8867c53..eed28b4f58 100644 --- a/nym-vpnd/src/service/exit_listener.rs +++ b/nym-vpnd/src/service/exit_listener.rs @@ -29,7 +29,7 @@ impl VpnServiceExitListener { } nym_vpn_lib::NymVpnExitStatusMessage::Failed(err) => { error!("VPN exit: fail: {err}"); - self.set_shared_state(VpnState::NotConnected); + self.set_shared_state(VpnState::ConnectionFailed(err.to_string())); } }, Err(err) => { diff --git a/proto/nym/vpn.proto b/proto/nym/vpn.proto index d62469bdca..d1c7e16ae7 100644 --- a/proto/nym/vpn.proto +++ b/proto/nym/vpn.proto @@ -72,6 +72,7 @@ message GatewayResponse { message StatusRequest {} message StatusResponse { ConnectionStatus status = 1; + optional Error error = 2; } message ConnectionStatusUpdate { From 1fb30242e22f183cfaff58967b91bb1bc77787c7 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 14:36:48 +0200 Subject: [PATCH 07/12] Add ImportUserCredential command --- .../command_interface/connection_handler.rs | 17 ++++++++++++++++- nym-vpnd/src/command_interface/listener.rs | 19 ++++++++++++++++++- nym-vpnd/src/service/mod.rs | 2 +- nym-vpnd/src/service/vpn_service.rs | 18 +++++++++++++++--- proto/nym/vpn.proto | 10 +++++++++- 5 files changed, 59 insertions(+), 7 deletions(-) diff --git a/nym-vpnd/src/command_interface/connection_handler.rs b/nym-vpnd/src/command_interface/connection_handler.rs index 3ec2e6cfae..85a652c17f 100644 --- a/nym-vpnd/src/command_interface/connection_handler.rs +++ b/nym-vpnd/src/command_interface/connection_handler.rs @@ -6,7 +6,7 @@ use tracing::{info, warn}; use crate::service::{ ConnectArgs, VpnServiceCommand, VpnServiceConnectResult, VpnServiceDisconnectResult, - VpnServiceStatusResult, + VpnServiceImportUserCredentialResult, VpnServiceStatusResult, }; pub(super) struct CommandInterfaceConnectionHandler { @@ -72,4 +72,19 @@ impl CommandInterfaceConnectionHandler { info!("VPN status: {:?}", status); status } + + pub(crate) async fn handle_import_credential( + &self, + credential: String, + ) -> VpnServiceImportUserCredentialResult { + let (tx, rx) = oneshot::channel(); + self.vpn_command_tx + .send(VpnServiceCommand::ImportCredential(tx, credential)) + .unwrap(); + info!("Sent import credential command to VPN"); + info!("Waiting for response"); + let result = rx.await.unwrap(); + info!("VPN import credential result: {:?}", result); + result + } } diff --git a/nym-vpnd/src/command_interface/listener.rs b/nym-vpnd/src/command_interface/listener.rs index 2c12160608..7136e20d67 100644 --- a/nym-vpnd/src/command_interface/listener.rs +++ b/nym-vpnd/src/command_interface/listener.rs @@ -9,7 +9,8 @@ use std::{ use nym_vpn_proto::{ nym_vpnd_server::NymVpnd, ConnectRequest, ConnectResponse, ConnectionStatus, DisconnectRequest, - DisconnectResponse, Error as ProtoError, StatusRequest, StatusResponse, + DisconnectResponse, Error as ProtoError, ImportUserCredentialRequest, + ImportUserCredentialResponse, StatusRequest, StatusResponse, }; use tokio::sync::mpsc::UnboundedSender; use tracing::{error, info}; @@ -132,6 +133,22 @@ impl NymVpnd for CommandInterface { error, })) } + + async fn import_user_credential( + &self, + _request: tonic::Request, + ) -> Result, tonic::Status> { + info!("Got import credential request"); + + let status = CommandInterfaceConnectionHandler::new(self.vpn_command_tx.clone()) + .handle_import_credential("".to_string()) + .await; + + info!("Returning import credential response"); + Ok(tonic::Response::new(ImportUserCredentialResponse { + success: status.is_success(), + })) + } } impl From for ConnectionStatus { diff --git a/nym-vpnd/src/service/mod.rs b/nym-vpnd/src/service/mod.rs index 0e15047c8b..84c03aada4 100644 --- a/nym-vpnd/src/service/mod.rs +++ b/nym-vpnd/src/service/mod.rs @@ -10,5 +10,5 @@ mod vpn_service; pub(crate) use start::start_vpn_service; pub(crate) use vpn_service::{ ConnectArgs, VpnServiceCommand, VpnServiceConnectResult, VpnServiceDisconnectResult, - VpnServiceStatusResult, + VpnServiceImportUserCredentialResult, VpnServiceStatusResult, }; diff --git a/nym-vpnd/src/service/vpn_service.rs b/nym-vpnd/src/service/vpn_service.rs index 410ffcbc1c..c2775ab2bb 100644 --- a/nym-vpnd/src/service/vpn_service.rs +++ b/nym-vpnd/src/service/vpn_service.rs @@ -34,7 +34,10 @@ pub enum VpnServiceCommand { Disconnect(oneshot::Sender), Status(oneshot::Sender), #[allow(unused)] - ImportCredential(oneshot::Sender, String), + ImportCredential( + oneshot::Sender, + String, + ), } #[derive(Debug)] @@ -83,11 +86,17 @@ pub enum VpnServiceStatusResult { #[allow(unused)] #[derive(Debug)] -pub enum VpnImportCredentialResult { +pub enum VpnServiceImportUserCredentialResult { Success, Fail(String), } +impl VpnServiceImportUserCredentialResult { + pub fn is_success(&self) -> bool { + matches!(self, VpnServiceImportUserCredentialResult::Success) + } +} + pub(super) struct NymVpnService { shared_vpn_state: Arc>, vpn_command_rx: UnboundedReceiver, @@ -208,7 +217,10 @@ impl NymVpnService { } } - async fn handle_import_credential(&mut self, _credential: String) -> VpnImportCredentialResult { + async fn handle_import_credential( + &mut self, + _credential: String, + ) -> VpnServiceImportUserCredentialResult { todo!() } diff --git a/proto/nym/vpn.proto b/proto/nym/vpn.proto index d1c7e16ae7..4779e6d2ea 100644 --- a/proto/nym/vpn.proto +++ b/proto/nym/vpn.proto @@ -95,11 +95,19 @@ message SetUserCredentialsRequest { string key = 1; } +message ImportUserCredentialRequest { + string credential = 1; +} + +message ImportUserCredentialResponse { + bool success = 1; +} + service NymVpnd { - // rpc SetUserCredentials (SetUserCredentialsRequest) returns (Empty) {} rpc VpnConnect (ConnectRequest) returns (ConnectResponse) {} rpc VpnDisconnect (DisconnectRequest) returns (DisconnectResponse) {} rpc VpnStatus (StatusRequest) returns (StatusResponse) {} + rpc ImportUserCredential (ImportUserCredentialRequest) returns (ImportUserCredentialResponse) {} // rpc ListenToConnectionStatus (Empty) returns (stream ConnectionStatusUpdate) {} // // Cancel any connection pending state (connecting, disconnecting etc) // // and return to disconnected state From 1b14476df078a6df3099d4283868d6b3ec646a3c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 15:13:18 +0200 Subject: [PATCH 08/12] Handle import credential --- nym-vpnd/src/service/vpn_service.rs | 15 +++++++++++++-- 1 file changed, 13 insertions(+), 2 deletions(-) diff --git a/nym-vpnd/src/service/vpn_service.rs b/nym-vpnd/src/service/vpn_service.rs index c2775ab2bb..f6b88bb1e2 100644 --- a/nym-vpnd/src/service/vpn_service.rs +++ b/nym-vpnd/src/service/vpn_service.rs @@ -6,6 +6,7 @@ use std::sync::Arc; use futures::channel::mpsc::UnboundedSender; use futures::SinkExt; +use nym_vpn_lib::credentials::import_credential_base58; use nym_vpn_lib::gateway_directory::{self}; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::oneshot; @@ -219,9 +220,19 @@ impl NymVpnService { async fn handle_import_credential( &mut self, - _credential: String, + credential: String, ) -> VpnServiceImportUserCredentialResult { - todo!() + let is_running = self.vpn_ctrl_sender.is_some(); + if is_running { + return VpnServiceImportUserCredentialResult::Fail( + "Can't import credential while VPN is running".to_string(), + ); + } + + match import_credential_base58(&credential, self.data_dir.clone()).await { + Ok(()) => VpnServiceImportUserCredentialResult::Success, + Err(err) => VpnServiceImportUserCredentialResult::Fail(err.to_string()), + } } pub(super) async fn run(mut self) -> anyhow::Result<()> { From 857264f821f9c79f1eb98a15f9e8c3cd08f16ec1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 15:16:07 +0200 Subject: [PATCH 09/12] Add import_credential to client --- nym-vpnc/src/main.rs | 15 ++++++++++++++- 1 file changed, 14 insertions(+), 1 deletion(-) diff --git a/nym-vpnc/src/main.rs b/nym-vpnc/src/main.rs index e6e7f0ab43..d07f920d99 100644 --- a/nym-vpnc/src/main.rs +++ b/nym-vpnc/src/main.rs @@ -6,7 +6,8 @@ use std::path::{Path, PathBuf}; use anyhow::Context; use clap::{Parser, Subcommand}; use nym_vpn_proto::{ - nym_vpnd_client::NymVpndClient, ConnectRequest, DisconnectRequest, StatusRequest, + nym_vpnd_client::NymVpndClient, ConnectRequest, DisconnectRequest, ImportUserCredentialRequest, + StatusRequest, }; use parity_tokio_ipc::Endpoint as IpcEndpoint; use tonic::transport::{Channel as TonicChannel, Endpoint as TonicEndpoint}; @@ -27,6 +28,7 @@ enum Command { Connect, Disconnect, Status, + ImportCredential, } #[tokio::main] @@ -36,6 +38,7 @@ async fn main() -> anyhow::Result<()> { Command::Connect => connect(&args).await?, Command::Disconnect => disconnect(&args).await?, Command::Status => status(&args).await?, + Command::ImportCredential => import_credential(&args).await?, } Ok(()) } @@ -97,3 +100,13 @@ async fn status(args: &CliArgs) -> anyhow::Result<()> { println!("{:?}", response); Ok(()) } + +async fn import_credential(args: &CliArgs) -> anyhow::Result<()> { + let mut client = get_client(args).await?; + let request = tonic::Request::new(ImportUserCredentialRequest { + credential: "some_credential".to_string(), + }); + let response = client.import_user_credential(request).await?.into_inner(); + println!("{:?}", response); + Ok(()) +} From 376bdb6040894c09e033adeade48a25426cd4808 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 16:02:13 +0200 Subject: [PATCH 10/12] Import base58 credential end-to-end --- Cargo.lock | 1 + nym-vpnc/Cargo.toml | 1 + nym-vpnc/src/main.rs | 65 +++++++++++++++++-- .../command_interface/connection_handler.rs | 2 +- nym-vpnd/src/command_interface/listener.rs | 6 +- nym-vpnd/src/service/vpn_service.rs | 11 ++-- proto/nym/vpn.proto | 2 +- 7 files changed, 71 insertions(+), 17 deletions(-) diff --git a/Cargo.lock b/Cargo.lock index 7239d23431..c076c822d5 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -4702,6 +4702,7 @@ name = "nym-vpnc" version = "0.1.0" dependencies = [ "anyhow", + "bs58 0.5.1", "clap", "nym-vpn-proto", "parity-tokio-ipc", diff --git a/nym-vpnc/Cargo.toml b/nym-vpnc/Cargo.toml index 41f0a4d0ef..ec332c597f 100644 --- a/nym-vpnc/Cargo.toml +++ b/nym-vpnc/Cargo.toml @@ -12,6 +12,7 @@ license.workspace = true [dependencies] anyhow.workspace = true +bs58.workspace = true clap = { workspace = true, features = ["derive"] } parity-tokio-ipc.workspace = true prost.workspace = true diff --git a/nym-vpnc/src/main.rs b/nym-vpnc/src/main.rs index d07f920d99..8590ba3755 100644 --- a/nym-vpnc/src/main.rs +++ b/nym-vpnc/src/main.rs @@ -4,7 +4,7 @@ use std::path::{Path, PathBuf}; use anyhow::Context; -use clap::{Parser, Subcommand}; +use clap::{Args, Parser, Subcommand}; use nym_vpn_proto::{ nym_vpnd_client::NymVpndClient, ConnectRequest, DisconnectRequest, ImportUserCredentialRequest, StatusRequest, @@ -28,7 +28,49 @@ enum Command { Connect, Disconnect, Status, - ImportCredential, + ImportCredential(ImportCredentialArgs), +} + +#[derive(Args)] +pub(crate) struct ImportCredentialArgs { + #[command(flatten)] + pub(crate) credential_type: ImportCredentialType, + + // currently hidden as there exists only a single serialization standard + #[arg(long, hide = true)] + pub(crate) version: Option, +} + +#[derive(Args, Clone)] +#[group(required = true, multiple = false)] +pub(crate) struct ImportCredentialType { + /// Credential encoded using base58. + #[arg(long)] + pub(crate) credential_data: Option, + + /// Path to the credential file. + #[arg(long)] + pub(crate) credential_path: Option, +} + +fn parse_encoded_credential_data(raw: &str) -> bs58::decode::Result> { + bs58::decode(raw).into_vec() +} + +// Workaround until clap supports enums for ArgGroups +pub(crate) enum ImportCredentialTypeEnum { + Path(PathBuf), + Data(String), +} + +impl From for ImportCredentialTypeEnum { + fn from(ict: ImportCredentialType) -> Self { + match (ict.credential_data, ict.credential_path) { + (Some(data), None) => ImportCredentialTypeEnum::Data(data), + (None, Some(path)) => ImportCredentialTypeEnum::Path(path), + _ => unreachable!(), + } + } } #[tokio::main] @@ -38,7 +80,7 @@ async fn main() -> anyhow::Result<()> { Command::Connect => connect(&args).await?, Command::Disconnect => disconnect(&args).await?, Command::Status => status(&args).await?, - Command::ImportCredential => import_credential(&args).await?, + Command::ImportCredential(ref import_args) => import_credential(&args, import_args).await?, } Ok(()) } @@ -101,11 +143,20 @@ async fn status(args: &CliArgs) -> anyhow::Result<()> { Ok(()) } -async fn import_credential(args: &CliArgs) -> anyhow::Result<()> { +async fn import_credential( + args: &CliArgs, + import_args: &ImportCredentialArgs, +) -> anyhow::Result<()> { + let import_type: ImportCredentialTypeEnum = import_args.credential_type.clone().into(); + let request = match import_type { + ImportCredentialTypeEnum::Path(_path) => todo!(), + ImportCredentialTypeEnum::Data(data) => { + let bin = parse_encoded_credential_data(&data)?; + tonic::Request::new(ImportUserCredentialRequest { credential: bin }) + } + }; + let mut client = get_client(args).await?; - let request = tonic::Request::new(ImportUserCredentialRequest { - credential: "some_credential".to_string(), - }); let response = client.import_user_credential(request).await?.into_inner(); println!("{:?}", response); Ok(()) diff --git a/nym-vpnd/src/command_interface/connection_handler.rs b/nym-vpnd/src/command_interface/connection_handler.rs index 85a652c17f..0d3b1443f2 100644 --- a/nym-vpnd/src/command_interface/connection_handler.rs +++ b/nym-vpnd/src/command_interface/connection_handler.rs @@ -75,7 +75,7 @@ impl CommandInterfaceConnectionHandler { pub(crate) async fn handle_import_credential( &self, - credential: String, + credential: Vec, ) -> VpnServiceImportUserCredentialResult { let (tx, rx) = oneshot::channel(); self.vpn_command_tx diff --git a/nym-vpnd/src/command_interface/listener.rs b/nym-vpnd/src/command_interface/listener.rs index 7136e20d67..ea784c60b5 100644 --- a/nym-vpnd/src/command_interface/listener.rs +++ b/nym-vpnd/src/command_interface/listener.rs @@ -136,12 +136,14 @@ impl NymVpnd for CommandInterface { async fn import_user_credential( &self, - _request: tonic::Request, + request: tonic::Request, ) -> Result, tonic::Status> { info!("Got import credential request"); + let credential = request.into_inner().credential; + let status = CommandInterfaceConnectionHandler::new(self.vpn_command_tx.clone()) - .handle_import_credential("".to_string()) + .handle_import_credential(credential) .await; info!("Returning import credential response"); diff --git a/nym-vpnd/src/service/vpn_service.rs b/nym-vpnd/src/service/vpn_service.rs index f6b88bb1e2..05f98a3417 100644 --- a/nym-vpnd/src/service/vpn_service.rs +++ b/nym-vpnd/src/service/vpn_service.rs @@ -6,7 +6,7 @@ use std::sync::Arc; use futures::channel::mpsc::UnboundedSender; use futures::SinkExt; -use nym_vpn_lib::credentials::import_credential_base58; +use nym_vpn_lib::credentials::import_credential; use nym_vpn_lib::gateway_directory::{self}; use tokio::sync::mpsc::UnboundedReceiver; use tokio::sync::oneshot; @@ -34,10 +34,9 @@ pub enum VpnServiceCommand { Connect(oneshot::Sender, ConnectArgs), Disconnect(oneshot::Sender), Status(oneshot::Sender), - #[allow(unused)] ImportCredential( oneshot::Sender, - String, + Vec, ), } @@ -85,7 +84,6 @@ pub enum VpnServiceStatusResult { ConnectionFailed(String), } -#[allow(unused)] #[derive(Debug)] pub enum VpnServiceImportUserCredentialResult { Success, @@ -220,8 +218,9 @@ impl NymVpnService { async fn handle_import_credential( &mut self, - credential: String, + credential: Vec, ) -> VpnServiceImportUserCredentialResult { + // BUG: this is not correct after a connect/disconnect cycle let is_running = self.vpn_ctrl_sender.is_some(); if is_running { return VpnServiceImportUserCredentialResult::Fail( @@ -229,7 +228,7 @@ impl NymVpnService { ); } - match import_credential_base58(&credential, self.data_dir.clone()).await { + match import_credential(credential, self.data_dir.clone()).await { Ok(()) => VpnServiceImportUserCredentialResult::Success, Err(err) => VpnServiceImportUserCredentialResult::Fail(err.to_string()), } diff --git a/proto/nym/vpn.proto b/proto/nym/vpn.proto index 4779e6d2ea..7f653a9265 100644 --- a/proto/nym/vpn.proto +++ b/proto/nym/vpn.proto @@ -96,7 +96,7 @@ message SetUserCredentialsRequest { } message ImportUserCredentialRequest { - string credential = 1; + bytes credential = 1; } message ImportUserCredentialResponse { From b157d97405f01b8a5d7534f4f80eb0e3f1f45378 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 21:26:32 +0200 Subject: [PATCH 11/12] STATUS_UNSPECIFIED as tag 0 --- proto/nym/vpn.proto | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/proto/nym/vpn.proto b/proto/nym/vpn.proto index 7f653a9265..e609b3acc8 100644 --- a/proto/nym/vpn.proto +++ b/proto/nym/vpn.proto @@ -44,8 +44,8 @@ enum VpnMode { } enum ConnectionStatus { - UNKNOWN = 0; - STATUS_UNSPECIFIED = 1; + STATUS_UNSPECIFIED = 0; + UNKNOWN = 1; NOT_CONNECTED = 2; CONNECTING = 3; CONNECTED = 4; From e001146ef7a5acb99a10c2242d4becca6059916c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jon=20H=C3=A4ggblad?= Date: Tue, 23 Apr 2024 21:35:32 +0200 Subject: [PATCH 12/12] Remove optional field specifier --- proto/nym/vpn.proto | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/proto/nym/vpn.proto b/proto/nym/vpn.proto index e609b3acc8..557c6c1d0f 100644 --- a/proto/nym/vpn.proto +++ b/proto/nym/vpn.proto @@ -72,7 +72,7 @@ message GatewayResponse { message StatusRequest {} message StatusResponse { ConnectionStatus status = 1; - optional Error error = 2; + Error error = 2; } message ConnectionStatusUpdate {