//! echoIRCd entry point: read config, bind the plaintext (and, if configured, //! the TLS) listener, then run the single-threaded core while the accept loops //! feed it connections. #![forbid(unsafe_code)] use std::net::TcpListener; use std::sync::atomic::AtomicU64; use std::sync::mpsc; use std::sync::Arc; use std::thread; use echoircd::config::Config; use echoircd::ircd::{Event, Ircd}; use echoircd::socketengine; use echoircd::tls::{OpensslBackend, TlsBackend}; fn main() { let path = std::env::args() .nth(1) .unwrap_or_else(|| "echoircd.conf".to_string()); let cfg = Config::load(&path); // Client plaintext connections run on the mio reactor, so bind a mio listener // (fail fast if the main port is taken). let bind_addr: std::net::SocketAddr = match cfg.bind.parse() { Ok(a) => a, Err(e) => { eprintln!("echoircd: bad bind address {}: {e}", cfg.bind); std::process::exit(1); } }; let client_listener = match mio::net::TcpListener::bind(bind_addr) { Ok(l) => l, Err(e) => { eprintln!("echoircd: cannot bind {}: {e}", cfg.bind); std::process::exit(1); } }; eprintln!( "echoircd {} on {} (network {}, server {})", env!("CARGO_PKG_VERSION"), cfg.bind, cfg.network, cfg.servername ); let (tx, rx) = mpsc::channel(); let core_cfg = cfg.clone(); let core_tx = tx.clone(); // the core self-injects events (DNS results) let core = thread::spawn(move || Ircd::new(core_cfg, core_tx).run(rx)); // background timer: drives ping/idle timeouts let tick_tx = tx.clone(); thread::spawn(move || loop { thread::sleep(std::time::Duration::from_secs(echoircd::server::TICK_SECS)); if tick_tx.send(Event::Tick).is_err() { break; } }); // one uid counter shared by every listener so ids stay unique let counter = Arc::new(AtomicU64::new(1)); // optional TLS listener (bind_tls + tls_cert + tls_key). A cert/bind problem // disables TLS but never takes the plaintext listener down. if let (Some(bind_tls), Some(cert), Some(key)) = (&cfg.bind_tls, &cfg.tls_cert, &cfg.tls_key) { match OpensslBackend::new(cert, key) { Ok(backend) => match TcpListener::bind(bind_tls) { Ok(tls_listener) => { eprintln!("echoircd TLS on {bind_tls} (openssl)"); let backend: Arc = Arc::new(backend); let tls_tx = tx.clone(); let tls_counter = counter.clone(); thread::spawn(move || { socketengine::accept_loop( tls_listener, tls_tx, Some(backend), tls_counter, false, ) }); } Err(e) => eprintln!("echoircd: cannot bind TLS {bind_tls}: {e}"), }, Err(e) => eprintln!("echoircd: TLS disabled (cert/key error): {e}"), } } // server-to-server link listener (see crate::link) if let Some(bind_srv) = &cfg.bind_server { match TcpListener::bind(bind_srv) { Ok(sl) => { eprintln!("echoircd S2S link listener on {bind_srv} (sid {})", cfg.sid); let s_tx = tx.clone(); let s_counter = counter.clone(); thread::spawn(move || socketengine::accept_loop(sl, s_tx, None, s_counter, true)); } Err(e) => eprintln!("echoircd: cannot bind server port {bind_srv}: {e}"), } } // dial any autoconnect uplinks (after a short delay so the peer can boot) for block in cfg.links.iter().filter(|b| b.autoconnect) { let addr = format!("{}:{}", block.ip, block.port); let u_tx = tx.clone(); let u_counter = counter.clone(); thread::spawn(move || { thread::sleep(std::time::Duration::from_secs(2)); socketengine::connect_link(&addr, u_tx, u_counter); }); } // client plaintext connections: one mio reactor thread drives them all thread::spawn(move || socketengine::run_reactor(client_listener, tx, counter)); let _ = core.join(); }