Feature/socks client config (#316)

* Made base client_core config

* Updated socks5 config with mandatory provider field
This commit is contained in:
Jędrzej Stuczyński
2020-08-27 16:28:53 +01:00
committed by GitHub
parent 9efc195046
commit 993d3d10a1
18 changed files with 701 additions and 341 deletions
Generated
+20 -9
View File
@@ -443,7 +443,7 @@ dependencies = [
"config",
"crypto",
"directory-client",
"dirs",
"dirs 2.0.2",
"futures 0.3.4",
"gateway-client",
"gateway-requests",
@@ -721,12 +721,20 @@ dependencies = [
]
[[package]]
name = "dirs-sys"
version = "0.3.4"
name = "dirs"
version = "3.0.1"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "afa0b23de8fd801745c471deffa6e12d248f962c9fd4b4c33787b055599bde7b"
checksum = "142995ed02755914747cc6ca76fc7e4583cd18578746716d0508ea6ed558b9ff"
dependencies = [
"dirs-sys",
]
[[package]]
name = "dirs-sys"
version = "0.3.5"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "8e93d7f5705de3e49895a2b5e0b8855a1c27f080192ae9c32a6432d50741a57a"
dependencies = [
"cfg-if",
"libc",
"redox_users",
"winapi 0.3.8",
@@ -1639,7 +1647,7 @@ dependencies = [
"config",
"crypto",
"directory-client",
"dirs",
"dirs 3.0.1",
"dotenv",
"futures 0.3.4",
"gateway-client",
@@ -1687,7 +1695,7 @@ dependencies = [
"config",
"crypto",
"directory-client",
"dirs",
"dirs 2.0.2",
"dotenv",
"futures 0.3.4",
"gateway-requests",
@@ -1717,7 +1725,7 @@ dependencies = [
"crypto",
"curve25519-dalek",
"directory-client",
"dirs",
"dirs 2.0.2",
"dotenv",
"futures 0.3.4",
"log 0.4.8",
@@ -1742,7 +1750,7 @@ dependencies = [
"byteorder",
"clap",
"config",
"dirs",
"dirs 2.0.2",
"dotenv",
"futures 0.3.4",
"iron",
@@ -2856,6 +2864,7 @@ dependencies = [
"config",
"crypto",
"directory-client",
"dirs 3.0.1",
"dotenv",
"futures 0.3.4",
"gateway-client",
@@ -2865,8 +2874,10 @@ dependencies = [
"pin-project",
"pretty_env_logger",
"rand 0.7.3",
"serde",
"simple-socks5-requests",
"snafu",
"tempfile",
"tokio 0.2.22",
"topology",
"url 2.1.1",
+51 -148
View File
@@ -12,17 +12,15 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::config::template::config_template;
use config::NymConfig;
use serde::{Deserialize, Serialize};
use std::marker::PhantomData;
use std::path::PathBuf;
use std::time;
pub mod persistence;
mod template;
// 'CLIENT'
const DEFAULT_LISTENING_PORT: u16 = 1977;
const DEFAULT_DIRECTORY_SERVER: &str = "https://directory.nymtech.net";
// 'DEBUG'
// where applicable, the below are defined in milliseconds
@@ -40,29 +38,10 @@ const DEFAULT_TOPOLOGY_RESOLUTION_TIMEOUT: u64 = 5_000; // 5s
const DEFAULT_GATEWAY_RESPONSE_TIMEOUT: u64 = 1_500; // 1.5s
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)]
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub enum SocketType {
WebSocket,
None,
}
impl SocketType {
pub fn from_string<S: Into<String>>(val: S) -> Self {
let mut upper = val.into();
upper.make_ascii_uppercase();
match upper.as_ref() {
"WEBSOCKET" | "WS" => SocketType::WebSocket,
_ => SocketType::None,
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
client: Client,
socket: Socket,
pub struct Config<T> {
client: Client<T>,
#[serde(default)]
logging: Logging,
@@ -70,58 +49,24 @@ pub struct Config {
debug: Debug,
}
impl NymConfig for Config {
fn template() -> &'static str {
config_template()
}
fn config_file_name() -> String {
"config.toml".to_string()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("clients")
}
fn root_directory(&self) -> PathBuf {
self.client.nym_root_directory.clone()
}
fn config_directory(&self) -> PathBuf {
self.client
.nym_root_directory
.join(&self.client.id)
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.client
.nym_root_directory
.join(&self.client.id)
.join("data")
}
}
impl Config {
impl<T: NymConfig> Config<T> {
pub fn new<S: Into<String>>(id: S) -> Self {
Config::default().with_id(id)
let mut cfg = Config::default();
cfg.with_id(id);
cfg
}
// builder methods
pub fn with_id<S: Into<String>>(mut self, id: S) -> Self {
pub fn with_id<S: Into<String>>(&mut self, id: S) {
let id = id.into();
// identity key setting
if self.client.private_identity_key_file.as_os_str().is_empty() {
self.client.private_identity_key_file =
self::Client::default_private_identity_key_file(&id);
self::Client::<T>::default_private_identity_key_file(&id);
}
if self.client.public_identity_key_file.as_os_str().is_empty() {
self.client.public_identity_key_file =
self::Client::default_public_identity_key_file(&id);
self::Client::<T>::default_public_identity_key_file(&id);
}
// encryption key setting
@@ -132,7 +77,7 @@ impl Config {
.is_empty()
{
self.client.private_encryption_key_file =
self::Client::default_private_encryption_key_file(&id);
self::Client::<T>::default_private_encryption_key_file(&id);
}
if self
.client
@@ -141,18 +86,18 @@ impl Config {
.is_empty()
{
self.client.public_encryption_key_file =
self::Client::default_public_encryption_key_file(&id);
self::Client::<T>::default_public_encryption_key_file(&id);
}
// shared gateway key setting
if self.client.gateway_shared_key_file.as_os_str().is_empty() {
self.client.gateway_shared_key_file =
self::Client::default_gateway_shared_key_file(&id);
self::Client::<T>::default_gateway_shared_key_file(&id);
}
// ack key setting
if self.client.ack_key_file.as_os_str().is_empty() {
self.client.ack_key_file = self::Client::default_ack_key_file(&id);
self.client.ack_key_file = self::Client::<T>::default_ack_key_file(&id);
}
if self
@@ -162,48 +107,36 @@ impl Config {
.is_empty()
{
self.client.reply_encryption_key_store_path =
self::Client::default_reply_encryption_key_store_path(&id);
self::Client::<T>::default_reply_encryption_key_store_path(&id);
}
self.client.id = id;
self
}
pub fn with_gateway_id<S: Into<String>>(mut self, id: S) -> Self {
pub fn with_gateway_id<S: Into<String>>(&mut self, id: S) {
self.client.gateway_id = id.into();
self
}
pub fn with_gateway_listener<S: Into<String>>(mut self, gateway_listener: S) -> Self {
pub fn with_gateway_listener<S: Into<String>>(&mut self, gateway_listener: S) {
self.client.gateway_listener = gateway_listener.into();
self
}
pub fn with_custom_directory<S: Into<String>>(mut self, directory_server: S) -> Self {
pub fn with_custom_directory<S: Into<String>>(&mut self, directory_server: S) {
self.client.directory_server = directory_server.into();
self
}
pub fn with_socket(mut self, socket_type: SocketType) -> Self {
self.socket.socket_type = socket_type;
self
}
pub fn with_port(mut self, port: u16) -> Self {
self.socket.listening_port = port;
self
}
pub fn set_high_default_traffic_volume(mut self) -> Self {
pub fn set_high_default_traffic_volume(&mut self) {
self.debug.average_packet_delay = 10;
self.debug.loop_cover_traffic_average_delay = 20; // 50 cover messages / s
self.debug.message_sending_average_delay = 5; // 200 "real" messages / s
self
}
// getters
pub fn get_config_file_save_location(&self) -> PathBuf {
self.config_directory().join(Self::config_file_name())
pub fn get_id(&self) -> String {
self.client.id.clone()
}
pub fn get_nym_root_directory(&self) -> PathBuf {
self.client.nym_root_directory.clone()
}
pub fn get_private_identity_key_file(&self) -> PathBuf {
@@ -246,14 +179,6 @@ impl Config {
self.client.gateway_listener.clone()
}
pub fn get_socket_type(&self) -> SocketType {
self.socket.socket_type
}
pub fn get_listening_port(&self) -> u16 {
self.socket.listening_port
}
// Debug getters
pub fn get_average_packet_delay(&self) -> time::Duration {
time::Duration::from_millis(self.debug.average_packet_delay)
@@ -292,9 +217,19 @@ impl Config {
}
}
impl<T: NymConfig> Default for Config<T> {
fn default() -> Self {
Config {
client: Client::<T>::default(),
logging: Default::default(),
debug: Default::default(),
}
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Client {
pub struct Client<T> {
/// ID specifies the human readable ID of this particular client.
id: String,
@@ -335,9 +270,12 @@ pub struct Client {
/// nym_home_directory specifies absolute path to the home nym Clients directory.
/// It is expected to use default value and hence .toml file should not redefine this field.
nym_root_directory: PathBuf,
#[serde(skip)]
super_struct: PhantomData<T>,
}
impl Default for Client {
impl<T: NymConfig> Default for Client<T> {
fn default() -> Self {
// there must be explicit checks for whether id is not empty later
Client {
@@ -352,54 +290,39 @@ impl Default for Client {
reply_encryption_key_store_path: Default::default(),
gateway_id: "".to_string(),
gateway_listener: "".to_string(),
nym_root_directory: Config::default_root_directory(),
nym_root_directory: T::default_root_directory(),
super_struct: Default::default(),
}
}
}
impl Client {
impl<T: NymConfig> Client<T> {
fn default_private_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("private_identity.pem")
T::default_data_directory(Some(id)).join("private_identity.pem")
}
fn default_public_identity_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("public_identity.pem")
T::default_data_directory(Some(id)).join("public_identity.pem")
}
fn default_private_encryption_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("private_encryption.pem")
T::default_data_directory(Some(id)).join("private_encryption.pem")
}
fn default_public_encryption_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("public_encryption.pem")
T::default_data_directory(Some(id)).join("public_encryption.pem")
}
fn default_gateway_shared_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("gateway_shared.pem")
T::default_data_directory(Some(id)).join("gateway_shared.pem")
}
fn default_ack_key_file(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("ack_key.pem")
T::default_data_directory(Some(id)).join("ack_key.pem")
}
fn default_reply_encryption_key_store_path(id: &str) -> PathBuf {
Config::default_data_directory(Some(id)).join("reply_key_store")
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socket {
socket_type: SocketType,
listening_port: u16,
}
impl Default for Socket {
fn default() -> Self {
Socket {
socket_type: SocketType::WebSocket,
listening_port: DEFAULT_LISTENING_PORT,
}
T::default_data_directory(Some(id)).join("reply_key_store")
}
}
@@ -485,23 +408,3 @@ impl Default for Debug {
}
}
}
#[cfg(test)]
mod client_config {
use super::*;
#[test]
fn after_saving_default_config_the_loaded_one_is_identical() {
// need to figure out how to do something similar but without touching the disk
// or the file system at all...
let temp_location = tempfile::tempdir().unwrap().path().join("config.toml");
let default_config = Config::default().with_id("foomp".to_string());
default_config
.save_to_file(Some(temp_location.clone()))
.unwrap();
let loaded_config = Config::load_from_file(Some(temp_location), None).unwrap();
assert_eq!(default_config, loaded_config);
}
}
@@ -13,6 +13,7 @@
// limitations under the License.
use crate::config::Config;
use config::NymConfig;
use std::path::{Path, PathBuf};
#[derive(Debug)]
@@ -39,7 +40,7 @@ impl ClientKeyPathfinder {
}
}
pub fn new_from_config(config: &Config) -> Self {
pub fn new_from_config<T: NymConfig>(config: &Config<T>) -> Self {
ClientKeyPathfinder {
identity_private_key: config.get_private_identity_key_file(),
identity_public_key: config.get_public_identity_key_file(),
+1 -1
View File
@@ -20,7 +20,7 @@ futures = "0.3" # bunch of futures stuff, however, now that I think about it, it
url = "2.1" # I think we should just replace it with String
clap = "2.33.0" # for the command line arguments
dirs = "2.0.2" # for determining default store directories in config
dirs = "3.0" # for determining default store directories in config
dotenv = "0.15.0" # for obtaining environmental variables (only used for RUST_LOG for time being)
log = "0.4" # self explanatory
pretty_env_logger = "0.3" # for formatting log messages
+157
View File
@@ -0,0 +1,157 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::config::template::config_template;
use client_core::config::Config as BaseConfig;
use config::NymConfig;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
mod template;
const DEFAULT_LISTENING_PORT: u16 = 1977;
#[derive(Debug, Deserialize, PartialEq, Serialize, Clone, Copy)]
#[serde(deny_unknown_fields)]
pub enum SocketType {
WebSocket,
None,
}
impl SocketType {
pub fn from_string<S: Into<String>>(val: S) -> Self {
let mut upper = val.into();
upper.make_ascii_uppercase();
match upper.as_ref() {
"WEBSOCKET" | "WS" => SocketType::WebSocket,
_ => SocketType::None,
}
}
}
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
#[serde(flatten)]
base: BaseConfig<Config>,
socket: Socket,
}
impl NymConfig for Config {
fn template() -> &'static str {
config_template()
}
fn config_file_name() -> String {
"config.toml".to_string()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("clients")
}
fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory()
}
fn config_directory(&self) -> PathBuf {
self.root_directory()
.join(self.base.get_id())
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory().join(self.base.get_id()).join("data")
}
}
impl Config {
pub fn new<S: Into<String>>(id: S) -> Self {
Config {
base: BaseConfig::new(id),
socket: Default::default(),
}
}
pub fn with_socket(mut self, socket_type: SocketType) -> Self {
self.socket.socket_type = socket_type;
self
}
pub fn with_port(mut self, port: u16) -> Self {
self.socket.listening_port = port;
self
}
// getters
pub fn get_config_file_save_location(&self) -> PathBuf {
self.config_directory().join(Self::config_file_name())
}
pub fn get_base(&self) -> &BaseConfig<Self> {
&self.base
}
pub fn get_base_mut(&mut self) -> &mut BaseConfig<Self> {
&mut self.base
}
pub fn get_socket_type(&self) -> SocketType {
self.socket.socket_type
}
pub fn get_listening_port(&self) -> u16 {
self.socket.listening_port
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socket {
socket_type: SocketType,
listening_port: u16,
}
impl Default for Socket {
fn default() -> Self {
Socket {
socket_type: SocketType::WebSocket,
listening_port: DEFAULT_LISTENING_PORT,
}
}
}
#[cfg(test)]
mod client_config {
use super::*;
#[test]
fn after_saving_default_config_the_loaded_one_is_identical() {
// need to figure out how to do something similar but without touching the disk
// or the file system at all...
let temp_location = tempfile::tempdir().unwrap().path().join("config.toml");
let default_config = Config::new("foomp".to_string());
default_config
.save_to_file(Some(temp_location.clone()))
.unwrap();
let loaded_config = Config::load_from_file(Some(temp_location), None).unwrap();
assert_eq!(default_config, loaded_config);
}
}
@@ -0,0 +1,102 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub(crate) fn config_template() -> &'static str {
// While using normal toml marshalling would have been way simpler with less overhead,
// I think it's useful to have comments attached to the saved config file to explain behaviour of
// particular fields.
// Note: any changes to the template must be reflected in the appropriate structs in mod.rs.
r#"
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base client config options #####
[client]
# Human readable ID of this particular client.
id = '{{ client.id }}'
# URL to the directory server.
directory_server = '{{ client.directory_server }}'
# Path to file containing private identity key.
private_identity_key_file = '{{ client.private_identity_key_file }}'
# Path to file containing public identity key.
public_identity_key_file = '{{ client.public_identity_key_file }}'
# Path to file containing private encryption key.
private_encryption_key_file = '{{ client.private_encryption_key_file }}'
# Path to file containing public encryption key.
public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# Full path to file containing reply encryption keys of all reply-SURBs we have ever
# sent but not received back.
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
##### additional client config options #####
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_id }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_listener }}'
# A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway.
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to.
ack_key_file = '{{ client.ack_key_file }}'
##### advanced configuration options #####
# Absolute path to the home Nym Clients directory.
nym_root_directory = '{{ client.nym_root_directory }}'
##### socket config options #####
[socket]
# allowed values are 'WebSocket' or 'None'
socket_type = '{{ socket.socket_type }}'
# if applicable (for the case of 'WebSocket'), the port on which the client
# will be listening for incoming requests
listening_port = {{ socket.listening_port }}
##### logging configuration options #####
[logging]
# TODO
##### debug configuration options #####
# The following options should not be modified unless you know EXACTLY what you are doing
# as if set incorrectly, they may impact your anonymity.
[debug]
average_packet_delay = {{ debug.average_packet_delay }}
average_ack_delay = {{ debug.average_ack_delay }}
loop_cover_traffic_average_delay = {{ debug.loop_cover_traffic_average_delay }}
message_sending_average_delay = {{ debug.message_sending_average_delay }}
"#
}
+23 -19
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::config::{Config, SocketType};
use crate::websocket;
use client_core::client::cover_traffic_stream::LoopCoverTrafficStream;
use client_core::client::inbound_messages::{
@@ -32,7 +33,6 @@ use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use client_core::config::{Config, SocketType};
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use gateway_client::{
@@ -46,6 +46,8 @@ use nymsphinx::anonymous_replies::ReplySURB;
use nymsphinx::receiver::ReconstructedMessage;
use tokio::runtime::Runtime;
pub(crate) mod config;
pub struct NymClient {
/// Client configuration options, including, among other things, packet sending rates,
/// key filepaths, etc.
@@ -71,7 +73,7 @@ pub struct NymClient {
impl NymClient {
pub fn new(config: Config) -> Self {
let pathfinder = ClientKeyPathfinder::new_from_config(&config);
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
NymClient {
@@ -89,7 +91,7 @@ impl NymClient {
self.key_manager.encryption_keypair().public_key().clone(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(self.config.get_gateway_id()).unwrap(),
NodeIdentity::from_base58_string(self.config.get_base().get_gateway_id()).unwrap(),
)
}
@@ -107,9 +109,11 @@ impl NymClient {
.enter(|| {
LoopCoverTrafficStream::new(
self.key_manager.ack_key(),
self.config.get_average_ack_delay(),
self.config.get_average_packet_delay(),
self.config.get_loop_cover_traffic_average_delay(),
self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_average_packet_delay(),
self.config
.get_base()
.get_loop_cover_traffic_average_delay(),
mix_tx,
self.as_mix_recipient(),
topology_accessor,
@@ -128,11 +132,11 @@ impl NymClient {
) {
let controller_config = real_messages_control::Config::new(
self.key_manager.ack_key(),
self.config.get_ack_wait_multiplier(),
self.config.get_ack_wait_addition(),
self.config.get_average_ack_delay(),
self.config.get_message_sending_average_delay(),
self.config.get_average_packet_delay(),
self.config.get_base().get_ack_wait_multiplier(),
self.config.get_base().get_ack_wait_addition(),
self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_message_sending_average_delay(),
self.config.get_base().get_average_packet_delay(),
self.as_mix_recipient(),
);
@@ -176,11 +180,11 @@ impl NymClient {
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
) -> GatewayClient<'static, url::Url> {
let gateway_id = self.config.get_gateway_id();
let gateway_id = self.config.get_base().get_gateway_id();
if gateway_id.is_empty() {
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_address_str = self.config.get_gateway_listener();
let gateway_address_str = self.config.get_base().get_gateway_listener();
if gateway_address_str.is_empty() {
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
}
@@ -200,7 +204,7 @@ impl NymClient {
Some(self.key_manager.gateway_shared_key()),
mixnet_message_sender,
ack_sender,
self.config.get_gateway_response_timeout(),
self.config.get_base().get_gateway_response_timeout(),
);
self.runtime.block_on(async {
@@ -217,8 +221,8 @@ impl NymClient {
// the current global view of topology
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_directory_server(),
self.config.get_topology_refresh_rate(),
self.config.get_base().get_directory_server(),
self.config.get_base().get_topology_refresh_rate(),
);
let mut topology_refresher =
TopologyRefresher::new_directory_client(topology_refresher_config, topology_accessor);
@@ -226,7 +230,7 @@ impl NymClient {
// components depending on topology would see a non-empty view
info!(
"Obtaining initial network topology from {}",
self.config.get_directory_server()
self.config.get_base().get_directory_server()
);
self.runtime.block_on(topology_refresher.refresh());
@@ -357,7 +361,7 @@ impl NymClient {
let shared_topology_accessor = TopologyAccessor::new();
let reply_key_storage =
ReplyKeyStorage::load(self.config.get_reply_encryption_key_store_path())
ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path())
.expect("Failed to load reply key storage!");
// the components are started in very specific order. Unless you know what you are doing,
@@ -413,7 +417,7 @@ impl NymClient {
);
info!(
"Gateway identity public key is: {:?}",
self.config.get_gateway_id()
self.config.get_base().get_gateway_id()
);
}
}
+16 -12
View File
@@ -13,6 +13,7 @@
// limitations under the License.
use crate::built_info;
use crate::client::config::Config;
use crate::commands::override_config;
use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager;
@@ -149,48 +150,51 @@ pub fn execute(matches: &ArgMatches) {
println!("Initialising client...");
let id = matches.value_of("id").unwrap(); // required for now
let mut config = client_core::config::Config::new(id);
let mut config = Config::new(id);
let mut rng = OsRng;
config = override_config(config, matches);
if matches.is_present("fastmode") {
config = config.set_high_default_traffic_volume();
config.get_base_mut().set_high_default_traffic_volume();
}
// 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_gateway_id().is_empty() {
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_directory_server(),
config.get_base().get_directory_server(),
key_manager.identity_keypair(),
));
config = config
.with_gateway_id(gateway_id)
config.get_base_mut().with_gateway_id(gateway_id);
config
.get_base_mut()
.with_gateway_listener(gateway_listener);
key_manager.insert_gateway_shared_key(shared_key)
}
// we specified our gateway but don't know its physical address
if config.get_gateway_listener().is_empty() {
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_directory_server(),
&config.get_gateway_id(),
config.get_base().get_directory_server(),
&config.get_base().get_gateway_id(),
))
.expect("No gateway with provided id exists!");
config = config.with_gateway_listener(gateway_listener);
config
.get_base_mut()
.with_gateway_listener(gateway_listener);
}
let pathfinder = ClientKeyPathfinder::new_from_config(&config);
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
key_manager
.store_keys(&pathfinder)
.expect("Failed to generated keys");
@@ -204,7 +208,7 @@ pub fn execute(matches: &ArgMatches) {
println!(
"Unless overridden in all `nym-client run` we will be talking to the following gateway: {}...",
config.get_gateway_id(),
config.get_base().get_gateway_id(),
);
println!("Client configuration completed.\n\n\n")
+3 -3
View File
@@ -12,19 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::config::{Config, SocketType};
use clap::ArgMatches;
use client_core::config::{Config, SocketType};
pub mod init;
pub mod run;
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
if let Some(directory) = matches.value_of("directory") {
config = config.with_custom_directory(directory);
config.get_base_mut().with_custom_directory(directory);
}
if let Some(gateway_id) = matches.value_of("gateway") {
config = config.with_gateway_id(gateway_id);
config.get_base_mut().with_gateway_id(gateway_id);
}
if matches.is_present("disable-socket") {
+1 -1
View File
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::config::Config;
use crate::client::NymClient;
use crate::commands::override_config;
use clap::{App, Arg, ArgMatches};
use client_core::config::Config;
use config::NymConfig;
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
+5
View File
@@ -11,12 +11,14 @@ path = "src/lib.rs"
[dependencies]
clap = "2.33.0"
dirs = "3.0" # for determining default store directories in config
dotenv = "0.15.0"
futures = "0.3"
log = "0.4"
pin-project = "0.4"
pretty_env_logger = "0.3"
rand = { version = "0.7.3", features = ["wasm-bindgen"] }
serde = { version = "1.0", features = ["derive"] } # for config serialization/deserialization
snafu = "0.4.1"
tokio = { version = "0.2", features = ["rt-threaded"] }
url = "2.1"
@@ -35,3 +37,6 @@ utils = { path = "../../common/utils" }
[build-dependencies]
built = "0.4.3"
[dev-dependencies]
tempfile = "3.1.0"
+154
View File
@@ -0,0 +1,154 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::config::template::config_template;
use client_core::config::Config as BaseConfig;
use config::NymConfig;
use nymsphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use std::path::PathBuf;
mod template;
const DEFAULT_LISTENING_PORT: u16 = 1080;
#[derive(Debug, Default, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Config {
#[serde(flatten)]
base: BaseConfig<Config>,
socks5: Socks5,
}
impl NymConfig for Config {
fn template() -> &'static str {
config_template()
}
fn config_file_name() -> String {
"config.toml".to_string()
}
fn default_root_directory() -> PathBuf {
dirs::home_dir()
.expect("Failed to evaluate $HOME value")
.join(".nym")
.join("socks5-clients")
}
fn root_directory(&self) -> PathBuf {
self.base.get_nym_root_directory()
}
fn config_directory(&self) -> PathBuf {
self.root_directory()
.join(self.base.get_id())
.join("config")
}
fn data_directory(&self) -> PathBuf {
self.root_directory().join(self.base.get_id()).join("data")
}
}
impl Config {
pub fn new<S: Into<String>>(id: S, provider_mix_address: S) -> Self {
Config {
base: BaseConfig::new(id),
socks5: Socks5::new(provider_mix_address),
}
}
pub fn with_port(mut self, port: u16) -> Self {
self.socks5.listening_port = port;
self
}
pub fn with_provider_mix_address(mut self, address: String) -> Self {
self.socks5.provider_mix_address = address;
self
}
// getters
pub fn get_config_file_save_location(&self) -> PathBuf {
self.config_directory().join(Self::config_file_name())
}
pub fn get_provider_mix_address(&self) -> Recipient {
Recipient::try_from_base58_string(&self.socks5.provider_mix_address)
.expect("malformed provider address")
}
pub fn get_base(&self) -> &BaseConfig<Self> {
&self.base
}
pub fn get_base_mut(&mut self) -> &mut BaseConfig<Self> {
&mut self.base
}
pub fn get_listening_port(&self) -> u16 {
self.socks5.listening_port
}
}
#[derive(Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5 {
/// The port on which the client will be listening for incoming requests
listening_port: u16,
/// The mix address of the provider to which all requests are going to be sent.
provider_mix_address: String,
}
impl Socks5 {
pub fn new<S: Into<String>>(provider_mix_address: S) -> Self {
Socks5 {
listening_port: DEFAULT_LISTENING_PORT,
provider_mix_address: provider_mix_address.into(),
}
}
}
impl Default for Socks5 {
fn default() -> Self {
Socks5 {
listening_port: DEFAULT_LISTENING_PORT,
provider_mix_address: "".into(),
}
}
}
#[cfg(test)]
mod client_config {
use super::*;
#[test]
fn after_saving_default_config_the_loaded_one_is_identical() {
// need to figure out how to do something similar but without touching the disk
// or the file system at all...
let temp_location = tempfile::tempdir().unwrap().path().join("config.toml");
let fake_address = "CytBseW6yFXUMzz4SGAKdNLGR7q3sJLLYxyBGvutNEQV.4QXYyEVc5fUDjmmi8PrHN9tdUFV4PCvSJE1278cHyvoe@FioFa8nMmPpQnYi7JyojoTuwGLeyNS8BF4ChPr29zUML";
let default_config = Config::new("foomp", fake_address);
default_config
.save_to_file(Some(temp_location.clone()))
.unwrap();
let loaded_config = Config::load_from_file(Some(temp_location), None).unwrap();
assert_eq!(default_config, loaded_config);
}
}
@@ -0,0 +1,101 @@
// Copyright 2020 Nym Technologies SA
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
pub(crate) fn config_template() -> &'static str {
// While using normal toml marshalling would have been way simpler with less overhead,
// I think it's useful to have comments attached to the saved config file to explain behaviour of
// particular fields.
// Note: any changes to the template must be reflected in the appropriate structs in mod.rs.
r#"
# This is a TOML config file.
# For more information, see https://github.com/toml-lang/toml
##### main base client config options #####
[client]
# Human readable ID of this particular client.
id = '{{ client.id }}'
# URL to the directory server.
directory_server = '{{ client.directory_server }}'
# Path to file containing private identity key.
private_identity_key_file = '{{ client.private_identity_key_file }}'
# Path to file containing public identity key.
public_identity_key_file = '{{ client.public_identity_key_file }}'
# Path to file containing private encryption key.
private_encryption_key_file = '{{ client.private_encryption_key_file }}'
# Path to file containing public encryption key.
public_encryption_key_file = '{{ client.public_encryption_key_file }}'
# Full path to file containing reply encryption keys of all reply-SURBs we have ever
# sent but not received back.
reply_encryption_key_store_path = '{{ client.reply_encryption_key_store_path }}'
##### additional client config options #####
# ID of the gateway from which the client should be fetching messages.
gateway_id = '{{ client.gateway_id }}'
# Address of the gateway listener to which all client requests should be sent.
gateway_listener = '{{ client.gateway_listener }}'
# A gateway specific, optional, base58 stringified shared key used for
# communication with particular gateway.
gateway_shared_key_file = '{{ client.gateway_shared_key_file }}'
# Path to file containing key used for encrypting and decrypting the content of an
# acknowledgement so that nobody besides the client knows which packet it refers to.
ack_key_file = '{{ client.ack_key_file }}'
##### advanced configuration options #####
# Absolute path to the home Nym Clients directory.
nym_root_directory = '{{ client.nym_root_directory }}'
##### socket config options #####
[socks5]
# The mix address of the provider to which all requests are going to be sent.
provider_mix_address = '{{ socks5.provider_mix_address }}'
# The port on which the client will be listening for incoming requests
listening_port = {{ socks5.listening_port }}
##### logging configuration options #####
[logging]
# TODO
##### debug configuration options #####
# The following options should not be modified unless you know EXACTLY what you are doing
# as if set incorrectly, they may impact your anonymity.
[debug]
average_packet_delay = {{ debug.average_packet_delay }}
average_ack_delay = {{ debug.average_ack_delay }}
loop_cover_traffic_average_delay = {{ debug.loop_cover_traffic_average_delay }}
message_sending_average_delay = {{ debug.message_sending_average_delay }}
"#
}
+27 -116
View File
@@ -12,6 +12,7 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::config::Config;
use crate::socks::{
authentication::{AuthenticationMethods, Authenticator, User},
server::SphinxSocksServer,
@@ -25,7 +26,6 @@ use client_core::client::mix_traffic::{
MixMessageReceiver, MixMessageSender, MixTrafficController,
};
use client_core::client::real_messages_control::RealMessagesController;
use client_core::client::received_buffer::{ReceivedBufferMessage, ReconstructedMessagesReceiver};
use client_core::client::received_buffer::{
ReceivedBufferRequestReceiver, ReceivedBufferRequestSender, ReceivedMessagesBufferController,
};
@@ -34,7 +34,6 @@ use client_core::client::topology_control::{
TopologyAccessor, TopologyRefresher, TopologyRefresherConfig,
};
use client_core::config::persistence::key_pathfinder::ClientKeyPathfinder;
use client_core::config::{Config, SocketType};
use crypto::asymmetric::identity;
use futures::channel::mpsc;
use gateway_client::{
@@ -42,12 +41,12 @@ use gateway_client::{
MixnetMessageSender,
};
use log::*;
use nymsphinx::addressing::clients::{ClientEncryptionKey, ClientIdentity, Recipient};
use nymsphinx::addressing::clients::Recipient;
use nymsphinx::addressing::nodes::NodeIdentity;
use nymsphinx::anonymous_replies::ReplySURB;
use nymsphinx::receiver::ReconstructedMessage;
use tokio::runtime::Runtime;
pub(crate) mod config;
pub struct NymClient {
/// Client configuration options, including, among other things, packet sending rates,
/// key filepaths, etc.
@@ -60,28 +59,17 @@ pub struct NymClient {
/// KeyManager object containing smart pointers to all relevant keys used by the client.
key_manager: KeyManager,
/// Channel used for transforming 'raw' messages into sphinx packets and sending them
/// through the mix network.
/// It is only available if the client started with the websocket listener disabled.
input_tx: Option<InputMessageSender>,
/// Channel used for obtaining reconstructed messages received from the mix network.
/// It is only available if the client started with the websocket listener disabled.
receive_tx: Option<ReconstructedMessagesReceiver>,
}
impl NymClient {
pub fn new(config: Config) -> Self {
let pathfinder = ClientKeyPathfinder::new_from_config(&config);
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
let key_manager = KeyManager::load_keys(&pathfinder).expect("failed to load stored keys");
NymClient {
runtime: Runtime::new().unwrap(),
config,
key_manager,
input_tx: None,
receive_tx: None,
}
}
@@ -91,7 +79,7 @@ impl NymClient {
self.key_manager.encryption_keypair().public_key().clone(),
// TODO: below only works under assumption that gateway address == gateway id
// (which currently is true)
NodeIdentity::from_base58_string(self.config.get_gateway_id()).unwrap(),
NodeIdentity::from_base58_string(self.config.get_base().get_gateway_id()).unwrap(),
)
}
@@ -109,9 +97,11 @@ impl NymClient {
.enter(|| {
LoopCoverTrafficStream::new(
self.key_manager.ack_key(),
self.config.get_average_ack_delay(),
self.config.get_average_packet_delay(),
self.config.get_loop_cover_traffic_average_delay(),
self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_average_packet_delay(),
self.config
.get_base()
.get_loop_cover_traffic_average_delay(),
mix_tx,
self.as_mix_recipient(),
topology_accessor,
@@ -130,11 +120,11 @@ impl NymClient {
) {
let controller_config = client_core::client::real_messages_control::Config::new(
self.key_manager.ack_key(),
self.config.get_ack_wait_multiplier(),
self.config.get_ack_wait_addition(),
self.config.get_average_ack_delay(),
self.config.get_message_sending_average_delay(),
self.config.get_average_packet_delay(),
self.config.get_base().get_ack_wait_multiplier(),
self.config.get_base().get_ack_wait_addition(),
self.config.get_base().get_average_ack_delay(),
self.config.get_base().get_message_sending_average_delay(),
self.config.get_base().get_average_packet_delay(),
self.as_mix_recipient(),
);
@@ -178,11 +168,11 @@ impl NymClient {
mixnet_message_sender: MixnetMessageSender,
ack_sender: AcknowledgementSender,
) -> GatewayClient<'static, url::Url> {
let gateway_id = self.config.get_gateway_id();
let gateway_id = self.config.get_base().get_gateway_id();
if gateway_id.is_empty() {
panic!("The identity of the gateway is unknown - did you run `nym-client` init?")
}
let gateway_address_str = self.config.get_gateway_listener();
let gateway_address_str = self.config.get_base().get_gateway_listener();
if gateway_address_str.is_empty() {
panic!("The address of the gateway is unknown - did you run `nym-client` init?")
}
@@ -202,7 +192,7 @@ impl NymClient {
Some(self.key_manager.gateway_shared_key()),
mixnet_message_sender,
ack_sender,
self.config.get_gateway_response_timeout(),
self.config.get_base().get_gateway_response_timeout(),
);
self.runtime.block_on(async {
@@ -219,8 +209,8 @@ impl NymClient {
// the current global view of topology
fn start_topology_refresher(&mut self, topology_accessor: TopologyAccessor) {
let topology_refresher_config = TopologyRefresherConfig::new(
self.config.get_directory_server(),
self.config.get_topology_refresh_rate(),
self.config.get_base().get_directory_server(),
self.config.get_base().get_topology_refresh_rate(),
);
let mut topology_refresher =
TopologyRefresher::new_directory_client(topology_refresher_config, topology_accessor);
@@ -228,7 +218,7 @@ impl NymClient {
// components depending on topology would see a non-empty view
info!(
"Obtaining initial network topology from {}",
self.config.get_directory_server()
self.config.get_base().get_directory_server()
);
self.runtime.block_on(topology_refresher.refresh());
@@ -271,74 +261,15 @@ impl NymClient {
let allowed_users: Vec<User> = Vec::new();
let authenticator = Authenticator::new(auth_methods, allowed_users);
let recipient = self.load_socks5_service_provider();
let mut sphinx_socks = SphinxSocksServer::new(
1080,
"127.0.0.1",
self.config.get_listening_port(),
authenticator,
recipient,
self.config.get_provider_mix_address(),
self.as_mix_recipient(),
);
self.runtime
.spawn(async move { sphinx_socks.serve(msg_input, buffer_requester).await });
}
// TODO: make this configurable in the client config file
// TODO: Talk to JS about where I can easily find these.
fn load_socks5_service_provider(&self) -> Recipient {
// load from file here, or better yet, inject it
let identity = "G3WSu7S3Jdm5HHQwZ3bT7NKoEBNSGr8JY2jngbUigqfz";
let encryption_key = "6KUoz9gexdFziLrrh6M2gXpAW4Fdn3HBHss11WJDYKEL";
let gateway_key = "5QAR66H9aMqaEo9y9G4hxAsvenJe13wJSRkwQ8qjsF6C";
let client_identity = ClientIdentity::from_base58_string(identity).unwrap();
let client_encryption_key =
ClientEncryptionKey::from_base58_string(encryption_key).unwrap();
let gateway = NodeIdentity::from_base58_string(gateway_key).unwrap();
Recipient::new(client_identity, client_encryption_key, gateway)
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub fn send_message(&mut self, recipient: Recipient, message: Vec<u8>, with_reply_surb: bool) {
let input_msg = InputMessage::new_fresh(recipient, message, with_reply_surb);
self.input_tx
.as_ref()
.expect("start method was not called before!")
.unbounded_send(input_msg)
.unwrap();
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
pub fn send_reply(&mut self, reply_surb: ReplySURB, message: Vec<u8>) {
let input_msg = InputMessage::new_reply(reply_surb, message);
self.input_tx
.as_ref()
.expect("start method was not called before!")
.unbounded_send(input_msg)
.unwrap();
}
/// EXPERIMENTAL DIRECT RUST API
/// It's untested and there are absolutely no guarantees about it (but seems to have worked
/// well enough in local tests)
/// Note: it waits for the first occurrence of messages being sent to ourselves. If you expect multiple
/// messages, you might have to call this function repeatedly.
// TODO: I guess this should really return something that `impl Stream<Item=ReconstructedMessage>`
pub async fn wait_for_messages(&mut self) -> Vec<ReconstructedMessage> {
use futures::StreamExt;
self.receive_tx
.as_mut()
.expect("start method was not called before!")
.next()
.await
.expect("buffer controller seems to have somehow died!")
}
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub fn run_forever(&mut self) {
@@ -382,7 +313,7 @@ impl NymClient {
let shared_topology_accessor = TopologyAccessor::new();
let reply_key_storage =
ReplyKeyStorage::load(self.config.get_reply_encryption_key_store_path())
ReplyKeyStorage::load(self.config.get_base().get_reply_encryption_key_store_path())
.expect("Failed to load reply key storage!");
// the components are started in very specific order. Unless you know what you are doing,
@@ -405,27 +336,7 @@ impl NymClient {
sphinx_message_sender.clone(),
);
self.start_cover_traffic_stream(shared_topology_accessor, sphinx_message_sender);
match self.config.get_socket_type() {
SocketType::WebSocket => {
self.start_socks5_listener(received_buffer_request_sender, input_sender)
}
SocketType::None => {
// if we did not start the socket, it means we're running (supposedly) in the native mode
// and hence we should announce 'ourselves' to the buffer
let (reconstructed_sender, reconstructed_receiver) = mpsc::unbounded();
// tell the buffer to start sending stuff to us
received_buffer_request_sender
.unbounded_send(ReceivedBufferMessage::ReceiverAnnounce(
reconstructed_sender,
))
.expect("the buffer request failed!");
self.receive_tx = Some(reconstructed_receiver);
self.input_tx = Some(input_sender);
}
}
self.start_socks5_listener(received_buffer_request_sender, input_sender);
info!("Client startup finished!");
info!(
@@ -438,7 +349,7 @@ impl NymClient {
);
info!(
"Gateway identity public key is: {:?}",
self.config.get_gateway_id()
self.config.get_base().get_gateway_id()
);
}
}
+25 -17
View File
@@ -13,6 +13,7 @@
// limitations under the License.
use crate::built_info;
use crate::client::config::Config;
use crate::commands::override_config;
use clap::{App, Arg, ArgMatches};
use client_core::client::key_manager::KeyManager;
@@ -37,6 +38,12 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.takes_value(true)
.required(true)
)
.arg(Arg::with_name("provider")
.long("provider")
.help("Address of the socks5 provider to send messages to.")
.takes_value(true)
.required(true)
)
.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.")
@@ -47,14 +54,10 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.help("Address of the directory server the client is getting topology from")
.takes_value(true),
)
.arg(Arg::with_name("disable-socket")
.long("disable-socket")
.help("Whether to not start the websocket")
)
.arg(Arg::with_name("port")
.short("p")
.long("port")
.help("Port for the socket (if applicable) to listen on in all subsequent runs")
.help("Port for the socket to listen on in all subsequent runs")
.takes_value(true)
)
.arg(Arg::with_name("fastmode")
@@ -149,48 +152,53 @@ pub fn execute(matches: &ArgMatches) {
println!("Initialising client...");
let id = matches.value_of("id").unwrap(); // required for now
let mut config = client_core::config::Config::new(id);
let provider_address = matches.value_of("provider").unwrap();
let mut config = Config::new(id, provider_address);
let mut rng = OsRng;
config = override_config(config, matches);
if matches.is_present("fastmode") {
config = config.set_high_default_traffic_volume();
config.get_base_mut().set_high_default_traffic_volume();
}
// 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_gateway_id().is_empty() {
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_directory_server(),
config.get_base().get_directory_server(),
key_manager.identity_keypair(),
));
config = config
.with_gateway_id(gateway_id)
config.get_base_mut().with_gateway_id(gateway_id);
config
.get_base_mut()
.with_gateway_listener(gateway_listener);
key_manager.insert_gateway_shared_key(shared_key)
}
// we specified our gateway but don't know its physical address
if config.get_gateway_listener().is_empty() {
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_directory_server(),
&config.get_gateway_id(),
config.get_base().get_directory_server(),
&config.get_base().get_gateway_id(),
))
.expect("No gateway with provided id exists!");
config = config.with_gateway_listener(gateway_listener);
config
.get_base_mut()
.with_gateway_listener(gateway_listener);
}
let pathfinder = ClientKeyPathfinder::new_from_config(&config);
let pathfinder = ClientKeyPathfinder::new_from_config(config.get_base());
key_manager
.store_keys(&pathfinder)
.expect("Failed to generated keys");
@@ -204,7 +212,7 @@ pub fn execute(matches: &ArgMatches) {
println!(
"Unless overridden in all `nym-client run` we will be talking to the following gateway: {}...",
config.get_gateway_id(),
config.get_base().get_gateway_id(),
);
println!("Client configuration completed.\n\n\n")
+3 -7
View File
@@ -12,23 +12,19 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::config::Config;
use clap::ArgMatches;
use client_core::config::{Config, SocketType};
pub mod init;
pub mod run;
pub(crate) fn override_config(mut config: Config, matches: &ArgMatches) -> Config {
if let Some(directory) = matches.value_of("directory") {
config = config.with_custom_directory(directory);
config.get_base_mut().with_custom_directory(directory);
}
if let Some(gateway_id) = matches.value_of("gateway") {
config = config.with_gateway_id(gateway_id);
}
if matches.is_present("disable-socket") {
config = config.with_socket(SocketType::None);
config.get_base_mut().with_gateway_id(gateway_id);
}
if let Some(port) = matches.value_of("port").map(|port| port.parse::<u16>()) {
+7 -6
View File
@@ -12,10 +12,10 @@
// See the License for the specific language governing permissions and
// limitations under the License.
use crate::client::config::Config;
use crate::client::NymClient;
use crate::commands::override_config;
use clap::{App, Arg, ArgMatches};
use client_core::config::Config;
use config::NymConfig;
pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
@@ -33,6 +33,11 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.help("Custom path to the nym-mixnet-client configuration file")
.takes_value(true)
)
.arg(Arg::with_name("provider")
.long("provider")
.help("Address of the socks5 provider to send messages to.")
.takes_value(true)
)
.arg(Arg::with_name("directory")
.long("directory")
.help("Address of the directory server the client is getting topology from")
@@ -43,14 +48,10 @@ pub fn command_args<'a, 'b>() -> clap::App<'a, 'b> {
.help("Id of the gateway we want to connect to. If overridden, it is user's responsibility to ensure prior registration happened")
.takes_value(true)
)
.arg(Arg::with_name("disable-socket")
.long("disable-socket")
.help("Whether to not start the websocket")
)
.arg(Arg::with_name("port")
.short("p")
.long("port")
.help("Port for the socket (if applicable) to listen on")
.help("Port for the socket to listen on")
.takes_value(true)
)
}
+3 -1
View File
@@ -25,11 +25,13 @@ impl SphinxSocksServer {
/// Create a new SphinxSocks instance
pub(crate) fn new(
port: u16,
ip: &str,
authenticator: Authenticator,
service_provider: Recipient,
self_address: Recipient,
) -> Self {
// hardcode ip as we (presumably) ONLY want to listen locally. If we change it, we can
// just modify the config
let ip = "127.0.0.1";
info!("Listening on {}:{}", ip, port);
SphinxSocksServer {
authenticator,