allow setting custom bind address for socks5 client

This commit is contained in:
Jędrzej Stuczyński
2023-10-13 10:57:58 +01:00
parent e02eae8fb3
commit 0a7826d286
12 changed files with 251 additions and 43 deletions
+13 -3
View File
@@ -22,6 +22,7 @@ use nym_sphinx::addressing::clients::Recipient;
use nym_topology::NymTopology;
use serde::Serialize;
use std::fmt::Display;
use std::net::{IpAddr, SocketAddr};
use std::path::PathBuf;
use std::{fs, io};
use tap::TapFallible;
@@ -77,6 +78,10 @@ pub(crate) struct Init {
#[clap(short, long)]
port: Option<u16>,
/// The custom host on which the socks5 client will be listening for requests
#[clap(long)]
host: Option<IpAddr>,
/// Path to .json file containing custom network specification.
#[clap(long, group = "network", hide = true)]
custom_mixnet: Option<PathBuf>,
@@ -103,6 +108,7 @@ impl From<Init> for OverrideConfig {
fn from(init_config: Init) -> Self {
OverrideConfig {
nym_apis: init_config.nym_apis,
ip: init_config.host,
port: init_config.port,
use_anonymous_replies: init_config.use_reply_surbs,
fastmode: init_config.fastmode,
@@ -120,7 +126,7 @@ impl From<Init> for OverrideConfig {
pub struct InitResults {
#[serde(flatten)]
client_core: nym_client_core::init::types::InitResults,
socks5_listening_port: u16,
socks5_listening_address: SocketAddr,
client_address: String,
}
@@ -132,7 +138,7 @@ impl InitResults {
address,
gateway,
),
socks5_listening_port: config.core.socks5.listening_port,
socks5_listening_address: config.core.socks5.bind_adddress,
client_address: address.to_string(),
}
}
@@ -141,7 +147,11 @@ impl InitResults {
impl Display for InitResults {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
writeln!(f, "{}", self.client_core)?;
writeln!(f, "SOCKS5 listening port: {}", self.socks5_listening_port)?;
writeln!(
f,
"SOCKS5 listening address: {}",
self.socks5_listening_address
)?;
write!(f, "Address of this client: {}", self.client_address)
}
}
+35 -11
View File
@@ -4,7 +4,8 @@
use crate::config::old_config_v1_1_13::OldConfigV1_1_13;
use crate::config::old_config_v1_1_20::ConfigV1_1_20;
use crate::config::old_config_v1_1_20_2::ConfigV1_1_20_2;
use crate::config::{BaseClientConfig, Config};
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
use crate::config::{BaseClientConfig, Config, SocksClientPaths};
use crate::error::Socks5ClientError;
use clap::CommandFactory;
use clap::{Parser, Subcommand};
@@ -22,6 +23,7 @@ use nym_client_core::error::ClientCoreError;
use nym_config::OptionalSet;
use nym_sphinx::params::{PacketSize, PacketType};
use std::error::Error;
use std::net::IpAddr;
pub(crate) mod build_info;
pub mod init;
@@ -72,6 +74,7 @@ pub(crate) enum Commands {
// Configuration that can be overridden.
pub(crate) struct OverrideConfig {
nym_apis: Option<Vec<url::Url>>,
ip: Option<IpAddr>,
port: Option<u16>,
use_anonymous_replies: Option<bool>,
fastmode: bool,
@@ -145,6 +148,7 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
)
.with_optional(Config::with_anonymous_replies, args.use_anonymous_replies)
.with_optional(Config::with_port, args.port)
.with_optional(Config::with_ip, args.ip)
.with_optional_base_custom_env(
BaseClientConfig::with_custom_nym_apis,
args.nym_apis,
@@ -164,12 +168,11 @@ pub(crate) fn override_config(config: Config, args: OverrideConfig) -> Config {
}
fn persist_gateway_details(
config: &Config,
storage_paths: &SocksClientPaths,
details: GatewayEndpointConfig,
) -> Result<(), Socks5ClientError> {
let details_store =
OnDiskGatewayDetails::new(&config.storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(config.storage_paths.common_paths.keys.clone());
let details_store = OnDiskGatewayDetails::new(&storage_paths.common_paths.gateway_details);
let keys_store = OnDiskKeys::new(storage_paths.common_paths.keys.clone());
let shared_keys = keys_store.ephemeral_load_gateway_keys().map_err(|source| {
Socks5ClientError::ClientCoreError(ClientCoreError::KeyStoreError {
source: Box::new(source),
@@ -199,9 +202,10 @@ fn try_upgrade_v1_1_13_config(id: &str) -> Result<bool, Socks5ClientError> {
let updated_step1: ConfigV1_1_20 = old_config.into();
let updated_step2: ConfigV1_1_20_2 = updated_step1.into();
let (updated, gateway_config) = updated_step2.upgrade()?;
persist_gateway_details(&updated, gateway_config)?;
let (updated_step3, gateway_config) = updated_step2.upgrade()?;
persist_gateway_details(&updated_step3.storage_paths, gateway_config)?;
let updated: Config = updated_step3.into();
updated.save_to_default_location()?;
Ok(true)
}
@@ -219,9 +223,10 @@ fn try_upgrade_v1_1_20_config(id: &str) -> Result<bool, Socks5ClientError> {
info!("It is going to get updated to the current specification.");
let updated_step1: ConfigV1_1_20_2 = old_config.into();
let (updated, gateway_config) = updated_step1.upgrade()?;
persist_gateway_details(&updated, gateway_config)?;
let (updated_step2, gateway_config) = updated_step1.upgrade()?;
persist_gateway_details(&updated_step2.storage_paths, gateway_config)?;
let updated: Config = updated_step2.into();
updated.save_to_default_location()?;
Ok(true)
}
@@ -236,9 +241,25 @@ fn try_upgrade_v1_1_20_2_config(id: &str) -> Result<bool, Socks5ClientError> {
info!("It seems the client is using <= v1.1.20_2 config template.");
info!("It is going to get updated to the current specification.");
let (updated, gateway_config) = old_config.upgrade()?;
persist_gateway_details(&updated, gateway_config)?;
let (updated_step1, gateway_config) = old_config.upgrade()?;
persist_gateway_details(&updated_step1.storage_paths, gateway_config)?;
let updated: Config = updated_step1.into();
updated.save_to_default_location()?;
Ok(true)
}
fn try_upgrade_v1_1_30_config(id: &str) -> Result<bool, Socks5ClientError> {
// explicitly load it as v1.1.20_2 (which is incompatible with the current one, i.e. +1.1.21)
let Ok(old_config) = ConfigV1_1_30::read_from_default_path(id) else {
// if we failed to load it, there might have been nothing to upgrade
// or maybe it was an even older file. in either way. just ignore it and carry on with our day
return Ok(false);
};
info!("It seems the client is using <= v1.1.30 config template.");
info!("It is going to get updated to the current specification.");
let updated: Config = old_config.into();
updated.save_to_default_location()?;
Ok(true)
}
@@ -253,6 +274,9 @@ fn try_upgrade_config(id: &str) -> Result<(), Socks5ClientError> {
if try_upgrade_v1_1_20_2_config(id)? {
return Ok(());
}
if try_upgrade_v1_1_30_config(id)? {
return Ok(());
}
Ok(())
}
+6
View File
@@ -1,6 +1,7 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use std::net::IpAddr;
use crate::commands::try_load_current_config;
use crate::config::Config;
use crate::{
@@ -53,6 +54,10 @@ pub(crate) struct Run {
#[clap(short, long)]
port: Option<u16>,
/// The custom host on which the socks5 client will be listening for requests
#[clap(long)]
host: Option<IpAddr>,
/// Path to .json file containing custom network specification.
#[clap(long, group = "network", group = "routing", hide = true)]
custom_mixnet: Option<PathBuf>,
@@ -88,6 +93,7 @@ impl From<Run> for OverrideConfig {
fn from(run_config: Run) -> Self {
OverrideConfig {
nym_apis: run_config.nym_apis,
ip: run_config.host,
port: run_config.port,
use_anonymous_replies: run_config.use_anonymous_replies,
fastmode: run_config.fastmode,
+11 -2
View File
@@ -1,7 +1,6 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::template::CONFIG_TEMPLATE;
use nym_bin_common::logging::LoggingSettings;
use nym_config::{
@@ -11,15 +10,18 @@ use nym_config::{
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::io;
use std::net::IpAddr;
use std::path::{Path, PathBuf};
use std::str::FromStr;
pub use crate::config::persistence::SocksClientPaths;
pub use nym_client_core::config::Config as BaseClientConfig;
pub use nym_socks5_client_core::config::Config as CoreConfig;
pub mod old_config_v1_1_13;
pub mod old_config_v1_1_20;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_30;
mod persistence;
mod template;
@@ -102,8 +104,15 @@ impl Config {
self.core.validate()
}
#[must_use]
pub fn with_port(mut self, port: u16) -> Self {
self.core.socks5.listening_port = port;
self.core = self.core.with_port(port);
self
}
#[must_use]
pub fn with_ip(mut self, ip: IpAddr) -> Self {
self.core = self.core.with_ip(ip);
self
}
@@ -1,11 +1,11 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::old_config_v1_1_30::ConfigV1_1_30;
use crate::{
config::{default_config_filepath, persistence::SocksClientPaths, Config},
config::{default_config_filepath, persistence::SocksClientPaths},
error::Socks5ClientError,
};
use nym_bin_common::logging::LoggingSettings;
use nym_client_core::config::disk_persistence::old_v1_1_20_2::CommonClientPathsV1_1_20_2;
use nym_client_core::config::GatewayEndpointConfig;
@@ -43,9 +43,9 @@ impl ConfigV1_1_20_2 {
// in this upgrade, gateway endpoint configuration was moved out of the config file,
// so its returned to be stored elsewhere.
pub fn upgrade(self) -> Result<(Config, GatewayEndpointConfig), Socks5ClientError> {
pub fn upgrade(self) -> Result<(ConfigV1_1_30, GatewayEndpointConfig), Socks5ClientError> {
let gateway_details = self.core.base.client.gateway_endpoint.clone().into();
let config = Config {
let config = ConfigV1_1_30 {
core: self.core.into(),
storage_paths: SocksClientPaths {
common_paths: self.storage_paths.common_paths.upgrade_default()?,
@@ -0,0 +1,44 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::persistence::SocksClientPaths;
use crate::config::{default_config_filepath, Config};
use nym_bin_common::logging::LoggingSettings;
use nym_config::read_config_from_toml_file;
use nym_socks5_client_core::config::old_config_v1_1_30::ConfigV1_1_30 as CoreConfigV1_1_30;
use serde::{Deserialize, Serialize};
use std::io;
use std::path::Path;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_30 {
pub core: CoreConfigV1_1_30,
// I'm leaving a landmine here for when the paths actually do change the next time,
// but propagating the change right now (in ALL clients) would be such a hassle...,
// so sorry for the next person looking at it : )
pub storage_paths: SocksClientPaths,
pub logging: LoggingSettings,
}
impl From<ConfigV1_1_30> for Config {
fn from(value: ConfigV1_1_30) -> Self {
Config {
core: value.core.into(),
storage_paths: value.storage_paths,
logging: LoggingSettings::default(),
}
}
}
impl ConfigV1_1_30 {
pub fn read_from_toml_file<P: AsRef<Path>>(path: P) -> io::Result<Self> {
read_config_from_toml_file(path)
}
pub fn read_from_default_path<P: AsRef<Path>>(id: P) -> io::Result<Self> {
Self::read_from_toml_file(default_config_filepath(id))
}
}
+17 -4
View File
@@ -7,9 +7,11 @@ use nym_config::OptionalSet;
use nym_sphinx::addressing::clients::Recipient;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
use std::str::FromStr;
pub mod old_config_v1_1_20_2;
pub mod old_config_v1_1_30;
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
pub use nym_socks5_requests::Socks5ProtocolVersion;
@@ -43,8 +45,15 @@ impl Config {
self.base.validate()
}
#[must_use]
pub fn with_port(mut self, port: u16) -> Self {
self.socks5.listening_port = port;
self.socks5.bind_adddress = SocketAddr::new(self.socks5.bind_adddress.ip(), port);
self
}
#[must_use]
pub fn with_ip(mut self, ip: IpAddr) -> Self {
self.socks5.bind_adddress = SocketAddr::new(ip, self.socks5.bind_adddress.port());
self
}
@@ -101,8 +110,9 @@ impl Config {
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5 {
/// The port on which the client will be listening for incoming requests
pub listening_port: u16,
/// The address on which the client will be listening for incoming requests
/// (default: 127.0.0.1:1080)
pub bind_adddress: SocketAddr,
/// The mix address of the provider to which all requests are going to be sent.
pub provider_mix_address: String,
@@ -131,7 +141,10 @@ pub struct Socks5 {
impl Socks5 {
pub fn new<S: Into<String>>(provider_mix_address: S) -> Self {
Socks5 {
listening_port: DEFAULT_SOCKS5_LISTENING_PORT,
bind_adddress: SocketAddr::new(
IpAddr::V4(Ipv4Addr::LOCALHOST),
DEFAULT_SOCKS5_LISTENING_PORT,
),
provider_mix_address: provider_mix_address.into(),
provider_interface_version: ProviderInterfaceVersion::Legacy,
socks5_protocol_version: Socks5ProtocolVersion::Legacy,
@@ -1,13 +1,12 @@
// Copyright 2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::old_config_v1_1_30::{ConfigV1_1_30, Socks5DebugV1_1_30, Socks5V1_1_30};
pub use nym_client_core::config::old_config_v1_1_20_2::ConfigV1_1_20_2 as BaseClientConfigV1_1_20_2;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use crate::config::{Config, Socks5, Socks5Debug};
pub use nym_service_providers_common::interface::ProviderInterfaceVersion;
pub use nym_socks5_requests::Socks5ProtocolVersion;
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
@@ -21,10 +20,10 @@ pub struct ConfigV1_1_20_2 {
pub socks5: Socks5V1_1_20_2,
}
impl From<ConfigV1_1_20_2> for Config {
impl From<ConfigV1_1_20_2> for ConfigV1_1_30 {
fn from(value: ConfigV1_1_20_2) -> Self {
Config {
base: value.base.into(),
ConfigV1_1_30 {
base: value.base,
socks5: value.socks5.into(),
}
}
@@ -45,9 +44,9 @@ pub struct Socks5V1_1_20_2 {
pub socks5_debug: Socks5DebugV1_1_20_2,
}
impl From<Socks5V1_1_20_2> for Socks5 {
impl From<Socks5V1_1_20_2> for Socks5V1_1_30 {
fn from(value: Socks5V1_1_20_2) -> Self {
Socks5 {
Socks5V1_1_30 {
listening_port: value.listening_port,
provider_mix_address: value.provider_mix_address,
provider_interface_version: value.provider_interface_version,
@@ -68,9 +67,9 @@ pub struct Socks5DebugV1_1_20_2 {
pub per_request_surbs: u32,
}
impl From<Socks5DebugV1_1_20_2> for Socks5Debug {
impl From<Socks5DebugV1_1_20_2> for Socks5DebugV1_1_30 {
fn from(value: Socks5DebugV1_1_20_2) -> Self {
Socks5Debug {
Socks5DebugV1_1_30 {
connection_start_surbs: value.connection_start_surbs,
per_request_surbs: value.per_request_surbs,
}
@@ -0,0 +1,106 @@
// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use super::old_config_v1_1_20_2::BaseClientConfigV1_1_20_2;
use super::{Config, Socks5, Socks5Debug};
use serde::{Deserialize, Serialize};
use std::fmt::Debug;
use std::net::{IpAddr, Ipv4Addr, SocketAddr};
// TODO: those should really be redefined here in case we change them...
use nym_service_providers_common::interface::ProviderInterfaceVersion;
use nym_socks5_requests::Socks5ProtocolVersion;
const DEFAULT_CONNECTION_START_SURBS: u32 = 20;
const DEFAULT_PER_REQUEST_SURBS: u32 = 3;
#[derive(Clone, Debug, Deserialize, PartialEq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct ConfigV1_1_30 {
#[serde(flatten)]
// base client config hasn't changed between v1.1.20 and v1.1.30
pub base: BaseClientConfigV1_1_20_2,
pub socks5: Socks5V1_1_30,
}
impl From<ConfigV1_1_30> for Config {
fn from(value: ConfigV1_1_30) -> Self {
Config {
base: value.base.into(),
socks5: value.socks5.into(),
}
}
}
#[derive(Clone, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(deny_unknown_fields)]
pub struct Socks5V1_1_30 {
/// The port on which the client will be listening for incoming requests
pub listening_port: u16,
/// The mix address of the provider to which all requests are going to be sent.
pub provider_mix_address: String,
/// The version of the 'service provider' this client is going to use in its communication with the
/// specified socks5 provider.
// if in doubt, use the legacy version as initially nobody will be using the updated binaries
#[serde(default)]
pub provider_interface_version: ProviderInterfaceVersion,
#[serde(default)]
pub socks5_protocol_version: Socks5ProtocolVersion,
/// Specifies whether this client is going to use an anonymous sender tag for communication with the service provider.
/// While this is going to hide its actual address information, it will make the actual communication
/// slower and consume nearly double the bandwidth as it will require sending reply SURBs.
///
/// Note that some service providers might not support this.
#[serde(default)]
pub send_anonymously: bool,
#[serde(default)]
pub socks5_debug: Socks5DebugV1_1_30,
}
impl From<Socks5V1_1_30> for Socks5 {
fn from(value: Socks5V1_1_30) -> Self {
Socks5 {
// in <= 1.1.30 the address was hardcoded to 127.0.0.1
bind_adddress: SocketAddr::new(IpAddr::V4(Ipv4Addr::LOCALHOST), value.listening_port),
provider_mix_address: value.provider_mix_address,
provider_interface_version: value.provider_interface_version,
socks5_protocol_version: value.socks5_protocol_version,
send_anonymously: value.send_anonymously,
socks5_debug: value.socks5_debug.into(),
}
}
}
#[derive(Clone, Copy, Debug, Deserialize, PartialEq, Eq, Serialize)]
#[serde(default, deny_unknown_fields)]
pub struct Socks5DebugV1_1_30 {
/// Number of reply SURBs attached to each `Request::Connect` message.
pub connection_start_surbs: u32,
/// Number of reply SURBs attached to each `Request::Send` message.
pub per_request_surbs: u32,
}
impl From<Socks5DebugV1_1_30> for Socks5Debug {
fn from(value: Socks5DebugV1_1_30) -> Self {
Socks5Debug {
connection_start_surbs: value.connection_start_surbs,
per_request_surbs: value.per_request_surbs,
}
}
}
impl Default for Socks5DebugV1_1_30 {
fn default() -> Self {
Socks5DebugV1_1_30 {
connection_start_surbs: DEFAULT_CONNECTION_START_SURBS,
per_request_surbs: DEFAULT_PER_REQUEST_SURBS,
}
}
}
+1 -1
View File
@@ -123,7 +123,7 @@ where
let authenticator = Authenticator::new(auth_methods, allowed_users);
let mut sphinx_socks = NymSocksServer::new(
socks5_config.listening_port,
socks5_config.bind_adddress,
authenticator,
socks5_config.get_provider_mix_address(),
self_address,
@@ -33,7 +33,7 @@ impl NymSocksServer {
/// Create a new SphinxSocks instance
#[allow(clippy::too_many_arguments)]
pub(crate) fn new(
port: u16,
bind_adddress: SocketAddr,
authenticator: Authenticator,
service_provider: Recipient,
self_address: Recipient,
@@ -42,13 +42,10 @@ impl NymSocksServer {
shutdown: TaskClient,
packet_type: PacketType,
) -> 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);
info!("Listening on {bind_adddress}");
NymSocksServer {
authenticator,
listening_address: format!("{ip}:{port}").parse().unwrap(),
listening_address: bind_adddress,
service_provider,
self_address,
client_config,
+1 -1
View File
@@ -56,7 +56,7 @@ impl Socks5MixnetClient {
/// Get the SOCKS5 proxy URL that a HTTP(S) client can connect to.
pub fn socks5_url(&self) -> String {
format!("socks5h://127.0.0.1:{}", self.socks5_config.listening_port)
format!("socks5h://{}", self.socks5_config.bind_adddress)
}
/// Get a shallow clone of [`LaneQueueLengths`]. This is useful to manually implement some form