From 4de4656c02bb6a6eccfcc7599a6c022e509b3137 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Adam=20Cig=C3=A1nek?= Date: Wed, 14 Aug 2024 13:26:44 +0200 Subject: [PATCH] cli: Add global pex config --- bridge/src/config.rs | 20 ++++++++++++++++++ bridge/src/network.rs | 45 ++++++++++++++++++++++++++++++++++++++++ cli/src/handler/local.rs | 44 ++++++++++++++++++++++++++++++++++----- cli/src/protocol.rs | 45 +++++++++++++++++++++++++++++++++++----- 4 files changed, 144 insertions(+), 10 deletions(-) diff --git a/bridge/src/config.rs b/bridge/src/config.rs index d2f2cd539..4abd5d204 100644 --- a/bridge/src/config.rs +++ b/bridge/src/config.rs @@ -171,6 +171,26 @@ where } } +impl ConfigEntry +where + T: Serialize + DeserializeOwned + fmt::Debug + Default, +{ + pub async fn modify(&self, f: F) -> Result<(), ConfigError> + where + F: FnOnce(&mut T), + { + let mut value = match self.get().await { + Ok(value) => value, + Err(ConfigError::NotFound) => T::default(), + Err(error) => return Err(error), + }; + + f(&mut value); + + self.set(&value).await + } +} + #[derive(Error, Debug)] pub enum ConfigError { #[error("config entry not found")] diff --git a/bridge/src/network.rs b/bridge/src/network.rs index 485d0b56f..9578f46a2 100644 --- a/bridge/src/network.rs +++ b/bridge/src/network.rs @@ -43,6 +43,8 @@ const LAST_USED_UDP_PORT_COMMENT: &str = This, in turn, is mainly useful for users who can't or don't want to use UPnP and have to\n\ default to manually setting up port forwarding on their routers."; +const PEX_KEY: ConfigKey = ConfigKey::new("pex", "Peer exchange configuration"); + #[derive(Eq, PartialEq, Debug, Serialize, Deserialize)] pub struct NetworkDefaults { pub port_forwarding_enabled: bool, @@ -72,6 +74,10 @@ pub async fn init(network: &Network, config: &ConfigStore, defaults: NetworkDefa for peer in peers { network.add_user_provided_peer(&peer); } + + let PexConfig { send, recv } = config.entry(PEX_KEY).get().await.unwrap_or_default(); + network.set_pex_send_enabled(send); + network.set_pex_recv_enabled(recv); } /// Binds the network to the specified addresses. @@ -162,6 +168,28 @@ pub async fn user_provided_peers(config: &ConfigStore) -> Vec { config.entry(PEERS_KEY).get().await.unwrap_or_default() } +/// Enables/disables sending contacts over PEX. +pub async fn set_pex_send_enabled(network: &Network, config: &ConfigStore, enabled: bool) { + config + .entry(PEX_KEY) + .modify(|pex_config| pex_config.send = enabled) + .await + .ok(); + + network.set_pex_send_enabled(enabled); +} + +/// Enables/disables receiving contacts over PEX. +pub async fn set_pex_recv_enabled(network: &Network, config: &ConfigStore, enabled: bool) { + config + .entry(PEX_KEY) + .modify(|pex_config| pex_config.recv = enabled) + .await + .ok(); + + network.set_pex_recv_enabled(enabled); +} + /// Utility to help reuse bind ports across network restarts. struct LastUsedPorts { quic_v4: u16, @@ -269,6 +297,23 @@ impl LastUsedPorts { } } +#[derive(Debug, Serialize, Deserialize)] +struct PexConfig { + // Is sending contacts over PEX enabled? + send: bool, + // Is receiving contacts over PEX enabled? + recv: bool, +} + +impl Default for PexConfig { + fn default() -> Self { + Self { + send: true, + recv: true, + } + } +} + #[cfg(test)] mod tests { use super::*; diff --git a/cli/src/handler/local.rs b/cli/src/handler/local.rs index ecbdfac0a..5eb3b6449 100644 --- a/cli/src/handler/local.rs +++ b/cli/src/handler/local.rs @@ -382,14 +382,48 @@ impl ouisync_bridge::transport::Handler for LocalHandler { Ok(holder.registration.is_dht_enabled().into()) } } - Request::Pex { name, enabled } => { - let holder = self.state.repositories.find(&name)?; + Request::Pex { + name, + enabled, + send, + recv, + } => { + if let Some(name) = name { + let holder = self.state.repositories.find(&name)?; + + if let Some(enabled) = enabled { + holder.registration.set_pex_enabled(enabled).await; + Ok(().into()) + } else { + Ok(holder.registration.is_pex_enabled().into()) + } + } else if send.is_some() || recv.is_some() { + if let Some(send) = send { + ouisync_bridge::network::set_pex_send_enabled( + &self.state.network, + &self.state.config, + send, + ) + .await; + } + + if let Some(recv) = recv { + ouisync_bridge::network::set_pex_recv_enabled( + &self.state.network, + &self.state.config, + recv, + ) + .await; + } - if let Some(enabled) = enabled { - holder.registration.set_pex_enabled(enabled).await; Ok(().into()) } else { - Ok(holder.registration.is_pex_enabled().into()) + Ok(format!( + "send: {} recv: {}", + self.state.network.is_pex_send_enabled(), + self.state.network.is_pex_recv_enabled(), + ) + .into()) } } Request::Quota { diff --git a/cli/src/protocol.rs b/cli/src/protocol.rs index ffc9b1f5d..70e35b838 100644 --- a/cli/src/protocol.rs +++ b/cli/src/protocol.rs @@ -1,5 +1,8 @@ use chrono::{DateTime, SecondsFormat, Utc}; -use clap::{builder::BoolishValueParser, Subcommand, ValueEnum}; +use clap::{ + builder::{ArgPredicate, BoolishValueParser}, + Subcommand, ValueEnum, +}; use ouisync_lib::{AccessMode, PeerAddr, PeerInfo, PeerSource, PeerState, StorageSize}; use serde::{Deserialize, Serialize}; use std::{ @@ -210,13 +213,39 @@ pub(crate) enum Request { #[arg(value_parser = BoolishValueParser::new())] enabled: Option, }, - /// Enable or disable Peer Exchange (PEX) + /// Configure Peer Exchange (PEX) Pex { + /// Name of the repository to enable/disable PEX for. #[arg(short = 'n', long)] - name: String, + name: Option, - /// Whether to enable or disable. If omitted, prints the current state. - #[arg(value_parser = BoolishValueParser::new())] + /// Globally enable/disable sending contacts over PEX. If all of name, send, recv are + /// omitted, prints the current state. + #[arg( + short, + long, + conflicts_with_all = ["name", "enabled"], + value_parser = BoolishValueParser::new(), + value_name = "ENABLED" + )] + send: Option, + + /// Globally enable/disable receiving contacts over PEX. If all of name, send, recv are + /// omitted, prints the current state. + #[arg( + short, + long, + conflicts_with_all = ["name", "enabled"], + value_parser = BoolishValueParser::new(), + value_name = "ENABLED" + )] + recv: Option, + + /// Enable/disable PEX for the specified repository. If omitted, prints the current state. + #[arg( + requires_if(ArgPredicate::IsPresent, "name"), + value_parser = BoolishValueParser::new(), + )] enabled: Option, }, /// Get or set storage quota @@ -316,6 +345,12 @@ impl From for Response { } } +impl<'a> From<&'a str> for Response { + fn from(value: &'a str) -> Self { + Self::String(value.to_owned()) + } +} + impl From> for Response { fn from(value: Vec) -> Self { Self::Strings(value)