Files
Jędrzej Stuczyński 3e4f563dce Merge pull request #6099 from nymtech/bugfix/incompatibility-fixes
Bugfix/incompatibility fixes
2025-10-16 15:58:43 +01:00

266 lines
8.6 KiB
Rust

// Copyright 2021-2023 - Nym Technologies SA <contact@nymtech.net>
// SPDX-License-Identifier: Apache-2.0
use crate::config::Config;
use crate::error::Socks5ClientCoreError;
use crate::socks::{
authentication::{AuthenticationMethods, Authenticator, User},
server::NymSocksServer,
};
use futures::channel::mpsc;
use futures::StreamExt;
use log::*;
use nym_client_core::client::base_client::non_wasm_helpers::default_query_dkg_client_from_config;
use nym_client_core::client::base_client::storage::GatewaysDetailsStore;
use nym_client_core::client::base_client::storage::MixnetClientStorage;
use nym_client_core::client::base_client::{
BaseClientBuilder, ClientInput, ClientOutput, ClientState,
};
use nym_client_core::client::key_manager::persistence::KeyStore;
use nym_client_core::client::replies::reply_storage::ReplyStorageBackend;
use nym_client_core::config::DebugConfig;
use nym_client_core::init::types::GatewaySetup;
use nym_credential_storage::storage::Storage as CredentialStorage;
use nym_sphinx::addressing::clients::Recipient;
use nym_sphinx::params::PacketType;
use nym_task::{ShutdownManager, ShutdownTracker};
use nym_validator_client::UserAgent;
use std::error::Error;
use std::path::PathBuf;
pub mod config;
pub mod error;
pub mod socks;
// Channels used to control the main task from outside
pub type Socks5ControlMessageSender = mpsc::UnboundedSender<Socks5ControlMessage>;
pub type Socks5ControlMessageReceiver = mpsc::UnboundedReceiver<Socks5ControlMessage>;
#[derive(Debug)]
pub enum Socks5ControlMessage {
/// Tell the main task to stop
Stop,
}
pub struct StartedSocks5Client {
/// Handle for managing graceful shutdown of this client. If dropped, the client will be stopped.
pub shutdown_handle: ShutdownManager,
/// Address of the started client
pub address: Recipient,
}
pub struct NymClient<S> {
/// Client configuration options, including, among other things, packet sending rates,
/// key filepaths, etc.
config: Config,
storage: S,
setup_method: GatewaySetup,
user_agent: UserAgent,
/// Optional path to a .json file containing standalone network details.
custom_mixnet: Option<PathBuf>,
shutdown_manager: ShutdownManager,
}
impl<S> NymClient<S>
where
S: MixnetClientStorage + 'static,
S::ReplyStore: Send + Sync,
<S::ReplyStore as ReplyStorageBackend>::StorageError: Sync + Send,
<S::CredentialStore as CredentialStorage>::StorageError: Send + Sync,
<S::GatewaysDetailsStore as GatewaysDetailsStore>::StorageError: Sync + Send,
<S::KeyStore as KeyStore>::StorageError: Send + Sync,
{
pub fn config(&self) -> Config {
self.config.clone()
}
pub fn new(
config: Config,
storage: S,
user_agent: UserAgent,
custom_mixnet: Option<PathBuf>,
) -> Self {
NymClient {
config,
storage,
setup_method: GatewaySetup::MustLoad { gateway_id: None },
user_agent,
custom_mixnet,
shutdown_manager: Default::default(),
}
}
pub fn with_gateway_setup(mut self, setup: GatewaySetup) -> Self {
self.setup_method = setup;
self
}
#[allow(clippy::too_many_arguments)]
pub fn start_socks5_listener(
socks5_config: &config::Socks5,
base_debug: DebugConfig,
client_input: ClientInput,
client_output: ClientOutput,
client_status: ClientState,
self_address: Recipient,
shutdown: ShutdownTracker,
packet_type: PacketType,
) {
info!("Starting socks5 listener...");
let auth_methods = vec![AuthenticationMethods::NoAuth as u8];
let allowed_users: Vec<User> = Vec::new();
let ClientInput {
connection_command_sender,
input_sender,
..
} = client_input;
let ClientOutput {
received_buffer_request_sender,
} = client_output;
let ClientState {
shared_lane_queue_lengths,
..
} = client_status;
let packet_size = base_debug
.traffic
.secondary_packet_size
.unwrap_or(base_debug.traffic.primary_packet_size);
let authenticator = Authenticator::new(auth_methods, allowed_users);
let mut sphinx_socks = NymSocksServer::new(
socks5_config.bind_address,
authenticator,
socks5_config.get_provider_mix_address(),
self_address,
shared_lane_queue_lengths,
socks::client::Config::new(
packet_size,
socks5_config.provider_interface_version,
socks5_config.socks5_protocol_version,
socks5_config.send_anonymously,
socks5_config.socks5_debug,
),
shutdown,
packet_type,
);
nym_task::spawn_future(async move {
sphinx_socks
.serve(
input_sender,
received_buffer_request_sender,
connection_command_sender,
)
.await
});
}
/// blocking version of `start` method. Will run forever (or until SIGINT is sent)
pub async fn run_forever(self) -> Result<(), Box<dyn Error + Send + Sync>> {
let mut started = self.start().await?;
started.shutdown_handle.run_until_shutdown().await;
log::info!("Stopping nym-socks5-client");
Ok(())
}
// Variant of `run_forever` that listens for remote control messages
pub async fn run_and_listen(
self,
mut receiver: Socks5ControlMessageReceiver,
) -> Result<(), Box<dyn Error + Send + Sync>> {
// Start the main task
let started = self.start().await?;
let mut task_manager = started.shutdown_handle;
let mut shutdown_signals = task_manager.detach_shutdown_signals();
let res = tokio::select! {
biased;
message = receiver.next() => {
log::debug!("Received message: {message:?}");
match message {
Some(Socks5ControlMessage::Stop) => {
log::info!("Received stop message");
}
None => {
log::info!("Channel closed, stopping");
}
}
Ok(())
},
_ = shutdown_signals.wait_for_signal() => {
log::info!("Received shutdown signal");
Ok(())
},
};
if !task_manager.is_cancelled() {
log::info!("Sending shutdown");
task_manager.send_cancellation();
}
log::info!("Waiting for tasks to finish... (Press ctrl-c to force)");
task_manager.perform_shutdown().await;
log::info!("Stopping nym-socks5-client");
res
}
pub async fn start(self) -> Result<StartedSocks5Client, Socks5ClientCoreError> {
// don't create dkg client for the bandwidth controller if credentials are disabled
let dkg_query_client = if self.config.base.client.disabled_credentials_mode {
None
} else {
Some(default_query_dkg_client_from_config(&self.config.base)?)
};
let mut base_builder =
BaseClientBuilder::new(self.config.base(), self.storage, dkg_query_client)
.with_shutdown(self.shutdown_manager.shutdown_tracker_owned())
.with_gateway_setup(self.setup_method)
.with_user_agent(self.user_agent);
if let Some(custom_mixnet) = &self.custom_mixnet {
base_builder = base_builder.with_stored_topology(custom_mixnet)?;
}
let packet_type = self.config.base().debug.traffic.packet_type;
let mut started_client = base_builder.start_base().await?;
let self_address = started_client.address;
let client_input = started_client.client_input.register_producer();
let client_output = started_client.client_output.register_consumer();
let client_state = started_client.client_state;
info!("Running with {packet_type} packets",);
Self::start_socks5_listener(
&self.config.socks5,
self.config.base().debug,
client_input,
client_output,
client_state,
self_address,
self.shutdown_manager.shutdown_tracker_owned(),
packet_type,
);
info!("Client startup finished!");
info!("The address of this client is: {self_address}");
Ok(StartedSocks5Client {
shutdown_handle: self.shutdown_manager,
address: self_address,
})
}
}