mod args; mod handler; mod tls; use std::net::TcpListener; use std::sync::Arc; use std::thread; fn main() { let args = args::parse_args(); if args.init { match zagan_bet::server::setup::init_identity(std::path::Path::new("."), "localhost") { Ok(report) => { println!("TLS identity created:"); println!(" {} (PKCS#12, empty password)", report.identity.display()); println!(" {} (private key)", report.key.display()); println!(" {} (certificate)", report.cert.display()); println!("\nStart the server with:\n cargo run --bin server"); println!("Clients will trust this certificate on first contact."); } Err(error) => { eprintln!("TLS setup failed:\n{error}"); std::process::exit(1); } } return; } let listener = match TcpListener::bind(format!("{}:{}", args.ip, args.port)) { Ok(l) => l, Err(e) => { eprintln!("Failed to bind to {}:{}: {}", args.ip, args.port, e); std::process::exit(1); } }; // A round cannot survive the process that held its state. Record any bet // that was reserved but never settled, before serving new players. let db_path = std::env::var("SERVER_DB").unwrap_or_else(|_| "server.db".to_string()); match zagan_bet::server::core::recover_orphaned_reservations(&db_path) { Ok(0) => {} Ok(count) => { println!("Settled {count} orphaned reservation(s) as abandoned after a restart") } Err(error) => { eprintln!("Failed to settle orphaned reservations in {db_path}: {error}"); std::process::exit(1); } } let tls = match tls::TlsServer::new("identity.p12") { Ok(t) => Arc::new(t), Err(e) => { eprintln!("Failed to initialize TLS: {}", e); std::process::exit(1); } }; println!( "Server listening on {} (TLS enabled){}", listener.local_addr().unwrap(), if args.unsafe_mode { " [UNSAFE MODE]" } else { "" } ); for stream in listener.incoming() { let stream = stream.unwrap(); let tls = tls.clone(); thread::spawn(move || match tls.accept(stream) { Ok(tls_stream) => { handler::handle_client_tls(tls_stream); } Err(error) => { // Public ports get a constant stream of scanners and resets. // Keep the useful failures, drop the background noise. let message = error.to_string().to_ascii_lowercase(); let noise = [ "unexpected eof", "connection reset", "broken pipe", "handshake failure", "received fatal alert", "bad record mac", "timed out", ] .iter() .any(|needle| message.contains(needle)); if !noise { eprintln!("TLS handshake failed: {error}"); } } }); } }