summaryrefslogtreecommitdiffstats
path: root/src/main.rs
diff options
context:
space:
mode:
Diffstat (limited to 'src/main.rs')
-rw-r--r--src/main.rs139
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>> {