diff options
Diffstat (limited to 'src/main.rs')
| -rw-r--r-- | src/main.rs | 139 |
1 files changed, 91 insertions, 48 deletions
diff --git a/src/main.rs b/src/main.rs index e6a27f4..f16138e 100644 --- a/src/main.rs +++ b/src/main.rs @@ -52,10 +52,17 @@ async fn main() -> Result<()> { return Ok(()); } let args = &config.settings; + if args.relay_only { + return relay_only(args).await; + } // Read before anything starts, so a bad key file fails right away. + let node_key_file = args + .node_key_file + .as_deref() + .context("node-key-file is required")?; let node_key = Arc::new(identity::NodeKey::load( - &args.node_key_file, + node_key_file, args.create_node_key, )?); info!(node_id = %node_key.id(), "Node identity"); @@ -71,7 +78,7 @@ async fn main() -> Result<()> { // the spot, and startup (spawning Mycelium, bootstrap) can take minutes. let stop = watch_for_shutdown().context("Installing signal handlers")?; // A broken htpasswd will abort startup. - let auth_state = AuthState::new(&args.htpasswd)?; + let auth_state = AuthState::new(args.htpasswd.as_deref().context("htpasswd is required")?)?; let uses = |name: &str| args.transports.iter().any(|transport| transport == name); let mut mycelium_setup = if uses("mycelium") { @@ -183,52 +190,12 @@ async fn main() -> Result<()> { ) } #[cfg(feature = "libp2p")] - "libp2p" => { - let listen = args - .libp2p_listen - .iter() - .map(|address| { - address - .parse::<libp2p::Multiaddr>() - .with_context(|| format!("libp2p-listen {address:?}")) - }) - .collect::<Result<Vec<_>>>()?; - let announce = args - .libp2p_announce - .iter() - .map(|address| { - address - .parse::<libp2p::Multiaddr>() - .with_context(|| format!("libp2p-announce {address:?}")) - }) - .collect::<Result<Vec<_>>>()?; - let relays = args - .libp2p_relays - .iter() - .map(|address| { - address - .parse::<libp2p::Multiaddr>() - .with_context(|| format!("libp2p-relays {address:?}")) - }) - .collect::<Result<Vec<_>>>()?; - let key = - transport::libp2p::Libp2pConfig::load_key(args.libp2p_key_file.as_deref())?; - ( - transport::libp2p::SCHEME, - Arc::new(transport::libp2p::Libp2pTransport::start( - transport::libp2p::Libp2pConfig { - key, - listen, - announce, - relays, - relay: args.libp2p_relay, - mdns: args.libp2p_mdns, - public: args.libp2p_public, - memory: false, - }, - )?), - ) - } + "libp2p" => ( + transport::libp2p::SCHEME, + Arc::new(transport::libp2p::Libp2pTransport::start(libp2p_config( + args, false, + )?)?), + ), #[cfg(feature = "tor")] "tor" => { let state_dir = args @@ -397,6 +364,82 @@ async fn main() -> Result<()> { Ok(()) } +/// The libp2p transport's configuration; `relay_only`: a relay that is no node. +#[cfg(feature = "libp2p")] +fn libp2p_config( + args: &config::Settings, + relay_only: bool, +) -> Result<transport::libp2p::Libp2pConfig> { + let parse = |option: &str, addresses: &[String]| { + addresses + .iter() + .map(|address| { + address + .parse::<libp2p::Multiaddr>() + .with_context(|| format!("{option} {address:?}")) + }) + .collect::<Result<Vec<_>>>() + }; + Ok(transport::libp2p::Libp2pConfig { + key: transport::libp2p::Libp2pConfig::load_key(args.libp2p_key_file.as_deref())?, + listen: parse("libp2p-listen", &args.libp2p_listen)?, + announce: parse("libp2p-announce", &args.libp2p_announce)?, + relays: parse("libp2p-relays", &args.libp2p_relays)?, + relay: args.libp2p_relay || relay_only, + continuum: !relay_only, + mdns: args.libp2p_mdns, + public: args.libp2p_public, + memory: false, + }) +} + +/// `--relay-only`: libp2p relaying for nodes of swarms, and the HTTP API's relay part +/// (with `--htpasswd`), until stopped. +#[cfg(feature = "libp2p")] +async fn relay_only(args: &config::Settings) -> Result<()> { + let stop = watch_for_shutdown().context("Installing signal handlers")?; + let auth_state = args.htpasswd.as_deref().map(AuthState::new).transpose()?; + let transport: Arc<dyn Transport> = Arc::new(transport::libp2p::Libp2pTransport::start( + libp2p_config(args, true)?, + )?); + info!(peer_id = transport.local_id(), "Relaying only (no node)"); + + let server = match auth_state { + Some(auth_state) => { + let app = api::relay_only_router(Arc::clone(&transport), auth_state); + let listener = tokio::net::TcpListener::bind(args.bind_addr) + .await + .with_context(|| format!("Binding local API to {}", args.bind_addr))?; + info!(address = %listener.local_addr()?, "HTTP API listening"); + let stop = stop.clone(); + Some(tokio::spawn(async move { + axum::serve(listener, app) + .with_graceful_shutdown(stopped(stop)) + .await + })) + } + None => { + info!("No htpasswd: no HTTP API"); + None + } + }; + + tokio::select! { + exit = transport.wait_for_exit() => error!(status = ?exit, "The libp2p transport stopped"), + _ = stopped(stop.clone()) => {} + } + if let Some(server) = server { + let _ = server.await; + } + info!("Relay stopped"); + Ok(()) +} + +#[cfg(not(feature = "libp2p"))] +async fn relay_only(_args: &config::Settings) -> Result<()> { + anyhow::bail!("relay-only relays over libp2p; this build has no libp2p") +} + /// Turns SIGINT and SIGTERM into a shutdown request. A second signal exits at once (the /// supervised Mycelium still gets its parent-death signal then). fn watch_for_shutdown() -> Result<watch::Receiver<bool>> { |
