From 66eb1913ba8a6db53ea121932789a407ab7d404c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?J=C4=99drzej=20Stuczy=C5=84ski?= Date: Thu, 10 Sep 2020 10:57:35 +0100 Subject: [PATCH] Made gateway mandatory during init (#331) --- clients/native/src/commands/init.rs | 143 ++++++++------------------- clients/socks5/src/commands/init.rs | 145 +++++++++------------------- 2 files changed, 85 insertions(+), 203 deletions(-) diff --git a/clients/native/src/commands/init.rs b/clients/native/src/commands/init.rs index 4885481266..9f1162d190 100644 --- a/clients/native/src/commands/init.rs +++ b/clients/native/src/commands/init.rs @@ -40,8 +40,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { ) .arg(Arg::with_name("gateway") .long("gateway") - .help("Id of the gateway we have preference to connect to. If left empty, a random gateway will be chosen.") + .help("Id of the gateway we are going to connect to") .takes_value(true) + .required(true) ) .arg(Arg::with_name("directory") .long("directory") @@ -65,85 +66,40 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { ) } -async fn try_gateway_registration( - gateways: &[gateway::Node], +async fn register_with_gateway( + gateway: &gateway::Node, our_identity: Arc, -) -> Option<(String, String, SharedKeys)> { +) -> SharedKeys { let timeout = Duration::from_millis(1500); - for gateway in gateways { - let mut gateway_client = GatewayClient::new_init( - url::Url::parse(&gateway.client_listener).unwrap(), - gateway.identity_key, - our_identity.clone(), - timeout, - ); - if gateway_client.establish_connection().await.is_ok() { - if let Ok(shared_key) = gateway_client.register().await { - if let Err(err) = gateway_client.close_connection().await { - eprintln!("Error while closing connection to the gateway! - {:?}", err); - continue; - } else { - return Some(( - gateway.identity_key.to_base58_string(), - gateway.client_listener.clone(), - shared_key, - )); - } - } - } - } - None + let mut gateway_client = GatewayClient::new_init( + url::Url::parse(&gateway.client_listener).unwrap(), + gateway.identity_key, + our_identity.clone(), + timeout, + ); + gateway_client + .establish_connection() + .await + .expect("failed to establish connection with the gateway!"); + gateway_client + .register() + .await + .expect("failed to register with the gateway!") } -async fn choose_gateway( - directory_server: String, - our_identity: Arc, -) -> (String, String, SharedKeys) { - let directory_client_config = directory_client::Config::new(directory_server.clone()); +async fn gateway_details(directory_server: &str, gateway_id: &str) -> gateway::Node { + let directory_client_config = directory_client::Config::new(directory_server.to_string()); let directory_client = directory_client::Client::new(directory_client_config); let topology = directory_client.get_topology().await.unwrap(); let nym_topology: NymTopology = topology.try_into().expect("Invalid topology data!"); - let version_filtered_topology = nym_topology.filter_system_version(built_info::PKG_VERSION); - // don't care about health of the networks as mixes can go up and down any time, - // but DO care about gateways - let gateways = version_filtered_topology.gateways(); - // try to perform registration so that we wouldn't need to do it at startup - // + at the same time we'll know if we can actually talk with that gateway - let registration_result = try_gateway_registration(gateways, our_identity).await; - match registration_result { - None => { - // while technically there's no issue client-side, it will be impossible to execute - // `nym-client run` as no gateway is available so it might be best to not finalize - // the init and rely on users trying to init another time? - panic!( - "Currently there are no valid gateways available on the network ({}). \ - Please try to run `init` again at later time or change your directory server", - directory_server - ) - } - Some((gateway_id, gateway_listener, shared_key)) => { - (gateway_id, gateway_listener, shared_key) - } - } -} - -async fn get_gateway_listener(directory_server: String, gateway_identity: &str) -> Option { - let directory_client_config = directory_client::Config::new(directory_server); - let directory_client = directory_client::Client::new(directory_client_config); - let topology = directory_client.get_topology().await.unwrap(); - - // technically we don't need to do conversion here, but let's be consistent - let nym_topology: NymTopology = topology.try_into().ok()?; - let gateways = nym_topology.gateways(); - - for gateway in gateways { - if gateway.identity_key.to_base58_string() == gateway_identity { - return Some(gateway.client_listener.clone()); - } - } - None + version_filtered_topology + .gateways() + .iter() + .find(|gateway| gateway.identity_key.to_base58_string() == gateway_id) + .expect(&*format!("no gateway with id {} exists!", gateway_id)) + .clone() } pub fn execute(matches: &ArgMatches) { @@ -161,38 +117,23 @@ pub fn execute(matches: &ArgMatches) { // create identity, encryption and ack keys. let mut key_manager = KeyManager::new(&mut rng); - // if there is no gateway chosen, get a random-ish one from the topology - if config.get_base().get_gateway_id().is_empty() { - // TODO: is there perhaps a way to make it work without having to spawn entire runtime? - let mut rt = tokio::runtime::Runtime::new().unwrap(); - let (gateway_id, gateway_listener, shared_key) = rt.block_on(choose_gateway( - config.get_base().get_directory_server(), - key_manager.identity_keypair(), - )); + let gateway_id = matches.value_of("gateway").unwrap(); - config.get_base_mut().with_gateway_id(gateway_id); - config - .get_base_mut() - .with_gateway_listener(gateway_listener); + let registration_fut = async { + let gate_details = + gateway_details(&config.get_base().get_directory_server(), gateway_id).await; + let shared_keys = + register_with_gateway(&gate_details, key_manager.identity_keypair()).await; + (shared_keys, gate_details.client_listener) + }; - key_manager.insert_gateway_shared_key(shared_key) - } - - // we specified our gateway but don't know its physical address - if config.get_base().get_gateway_listener().is_empty() { - // TODO: is there perhaps a way to make it work without having to spawn entire runtime? - let mut rt = tokio::runtime::Runtime::new().unwrap(); - let gateway_listener = rt - .block_on(get_gateway_listener( - config.get_base().get_directory_server(), - &config.get_base().get_gateway_id(), - )) - .expect("No gateway with provided id exists!"); - - config - .get_base_mut() - .with_gateway_listener(gateway_listener); - } + // TODO: is there perhaps a way to make it work without having to spawn entire runtime? + let mut rt = tokio::runtime::Runtime::new().unwrap(); + let (shared_keys, gateway_listener) = rt.block_on(registration_fut); + config + .get_base_mut() + .with_gateway_listener(gateway_listener); + key_manager.insert_gateway_shared_key(shared_keys); let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base()); key_manager diff --git a/clients/socks5/src/commands/init.rs b/clients/socks5/src/commands/init.rs index 4d0deed6bd..b0df220d72 100644 --- a/clients/socks5/src/commands/init.rs +++ b/clients/socks5/src/commands/init.rs @@ -46,8 +46,9 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { ) .arg(Arg::with_name("gateway") .long("gateway") - .help("Id of the gateway we have preference to connect to. If left empty, a random gateway will be chosen.") + .help("Id of the gateway we are going to connect to") .takes_value(true) + .required(true) ) .arg(Arg::with_name("directory") .long("directory") @@ -67,85 +68,40 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> { ) } -async fn try_gateway_registration( - gateways: &[gateway::Node], +async fn register_with_gateway( + gateway: &gateway::Node, our_identity: Arc, -) -> Option<(String, String, SharedKeys)> { +) -> SharedKeys { let timeout = Duration::from_millis(1500); - for gateway in gateways { - let mut gateway_client = GatewayClient::new_init( - url::Url::parse(&gateway.client_listener).unwrap(), - gateway.identity_key, - our_identity.clone(), - timeout, - ); - if gateway_client.establish_connection().await.is_ok() { - if let Ok(shared_key) = gateway_client.register().await { - if let Err(err) = gateway_client.close_connection().await { - eprintln!("Error while closing connection to the gateway! - {:?}", err); - continue; - } else { - return Some(( - gateway.identity_key.to_base58_string(), - gateway.client_listener.clone(), - shared_key, - )); - } - } - } - } - None + let mut gateway_client = GatewayClient::new_init( + url::Url::parse(&gateway.client_listener).unwrap(), + gateway.identity_key, + our_identity.clone(), + timeout, + ); + gateway_client + .establish_connection() + .await + .expect("failed to establish connection with the gateway!"); + gateway_client + .register() + .await + .expect("failed to register with the gateway!") } -async fn choose_gateway( - directory_server: String, - our_identity: Arc, -) -> (String, String, SharedKeys) { - let directory_client_config = directory_client::Config::new(directory_server.clone()); +async fn gateway_details(directory_server: &str, gateway_id: &str) -> gateway::Node { + let directory_client_config = directory_client::Config::new(directory_server.to_string()); let directory_client = directory_client::Client::new(directory_client_config); let topology = directory_client.get_topology().await.unwrap(); let nym_topology: NymTopology = topology.try_into().expect("Invalid topology data!"); - let version_filtered_topology = nym_topology.filter_system_version(built_info::PKG_VERSION); - // don't care about health of the networks as mixes can go up and down any time, - // but DO care about gateways - let gateways = version_filtered_topology.gateways(); - // try to perform registration so that we wouldn't need to do it at startup - // + at the same time we'll know if we can actually talk with that gateway - let registration_result = try_gateway_registration(gateways, our_identity).await; - match registration_result { - None => { - // while technically there's no issue client-side, it will be impossible to execute - // `nym-client run` as no gateway is available so it might be best to not finalize - // the init and rely on users trying to init another time? - panic!( - "Currently there are no valid gateways available on the network ({}). \ - Please try to run `init` again at later time or change your directory server", - directory_server - ) - } - Some((gateway_id, gateway_listener, shared_key)) => { - (gateway_id, gateway_listener, shared_key) - } - } -} - -async fn get_gateway_listener(directory_server: String, gateway_identity: &str) -> Option { - let directory_client_config = directory_client::Config::new(directory_server); - let directory_client = directory_client::Client::new(directory_client_config); - let topology = directory_client.get_topology().await.unwrap(); - - // technically we don't need to do conversion here, but let's be consistent - let nym_topology: NymTopology = topology.try_into().ok()?; - let gateways = nym_topology.gateways(); - - for gateway in gateways { - if gateway.identity_key.to_base58_string() == gateway_identity { - return Some(gateway.client_listener.clone()); - } - } - None + version_filtered_topology + .gateways() + .iter() + .find(|gateway| gateway.identity_key.to_base58_string() == gateway_id) + .expect(&*format!("no gateway with id {} exists!", gateway_id)) + .clone() } pub fn execute(matches: &ArgMatches) { @@ -165,38 +121,23 @@ pub fn execute(matches: &ArgMatches) { // create identity, encryption and ack keys. let mut key_manager = KeyManager::new(&mut rng); - // if there is no gateway chosen, get a random-ish one from the topology - if config.get_base().get_gateway_id().is_empty() { - // TODO: is there perhaps a way to make it work without having to spawn entire runtime? - let mut rt = tokio::runtime::Runtime::new().unwrap(); - let (gateway_id, gateway_listener, shared_key) = rt.block_on(choose_gateway( - config.get_base().get_directory_server(), - key_manager.identity_keypair(), - )); + let gateway_id = matches.value_of("gateway").unwrap(); - config.get_base_mut().with_gateway_id(gateway_id); - config - .get_base_mut() - .with_gateway_listener(gateway_listener); + let registration_fut = async { + let gate_details = + gateway_details(&config.get_base().get_directory_server(), gateway_id).await; + let shared_keys = + register_with_gateway(&gate_details, key_manager.identity_keypair()).await; + (shared_keys, gate_details.client_listener) + }; - key_manager.insert_gateway_shared_key(shared_key) - } - - // we specified our gateway but don't know its physical address - if config.get_base().get_gateway_listener().is_empty() { - // TODO: is there perhaps a way to make it work without having to spawn entire runtime? - let mut rt = tokio::runtime::Runtime::new().unwrap(); - let gateway_listener = rt - .block_on(get_gateway_listener( - config.get_base().get_directory_server(), - &config.get_base().get_gateway_id(), - )) - .expect("No gateway with provided id exists!"); - - config - .get_base_mut() - .with_gateway_listener(gateway_listener); - } + // TODO: is there perhaps a way to make it work without having to spawn entire runtime? + let mut rt = tokio::runtime::Runtime::new().unwrap(); + let (shared_keys, gateway_listener) = rt.block_on(registration_fut); + config + .get_base_mut() + .with_gateway_listener(gateway_listener); + key_manager.insert_gateway_shared_key(shared_keys); let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base()); key_manager @@ -211,7 +152,7 @@ pub fn execute(matches: &ArgMatches) { println!("Saved configuration file to {:?}", config_save_location); println!( - "Unless overridden in all `nym-client run` we will be talking to the following gateway: {}...", + "Unless overridden in all `nym-socks5-client run` we will be talking to the following gateway: {}...", config.get_base().get_gateway_id(), );