use std::sync::Arc; use actix_web::web::Data; use actix_web::{middleware, web, App, HttpRequest, HttpResponse, HttpServer, Responder}; use crate::base::err_utils::{encpasulate_error, new_err}; use crate::base::{cert_utils, RelayedPort}; use crate::tcp_relay_server::relay_ws::relay_ws; use crate::tcp_relay_server::server_config::ServerConfig; use crate::tcp_relay_server::tls_cert_client_verifier::CustomCertClientVerifier; mod relay_ws; pub mod server_config; mod tls_cert_client_verifier; pub async fn hello_route() -> &'static str { "Hello world!" } pub async fn config_route(req: HttpRequest, data: Data>) -> impl Responder { if data.has_token_auth() { let token = req .headers() .get("Authorization") .map(|t| t.to_str().unwrap_or_default()) .unwrap_or_default() .strip_prefix("Bearer ") .unwrap_or_default(); if !data.tokens.iter().any(|t| t.eq(token)) { return HttpResponse::Unauthorized().json("Missing / invalid token"); } } HttpResponse::Ok().json( data.ports .iter() .enumerate() .map(|(id, port)| RelayedPort { id, port: port + data.increment_ports, }) .collect::>(), ) } pub async fn run_app(mut config: ServerConfig) -> std::io::Result<()> { // Check if no port are to be forwarded if config.ports.is_empty() { return Err(new_err("No port to forward!")); } // Read tokens from file, if any if let Some(file) = &config.tokens_file { std::fs::read_to_string(file) .map_err(|e| encpasulate_error(e, "Failed to read tokens file!"))? .split('\n') .filter(|l| !l.is_empty()) .for_each(|t| config.tokens.push(t.to_string())); } if !config.has_auth() { return Err(new_err("No authentication method specified!")); } if config.tls_cert.is_some() != config.tls_key.is_some() { return Err(new_err("Incomplete server TLS configuration!")); } if config.has_tls_client_auth() && !config.has_tls_config() { return Err(new_err( "Cannot provide client auth without TLS configuration!", )); } let args = Arc::new(config); // Load TLS configuration, if any let tls_config = if let (Some(cert), Some(key)) = (&args.tls_cert, &args.tls_key) { // Load TLS certificate & private key let cert_file = std::fs::read(cert) .map_err(|e| encpasulate_error(e, "Failed to read certificate file"))?; let key_file = std::fs::read(key) .map_err(|e| encpasulate_error(e, "Failed to read server private key"))?; // Get certificates chain let cert_chain = cert_utils::parse_pem_certificates(&cert_file) .map_err(|e| encpasulate_error(e, "Failed to extract certificates"))?; // Get private key let key = cert_utils::parse_pem_private_key(&key_file) .map_err(|e| encpasulate_error(e, "Failed to extract private key!"))?; let config = rustls::ServerConfig::builder().with_safe_defaults(); let config = match args.has_tls_client_auth() { true => config .with_client_cert_verifier(Arc::new(CustomCertClientVerifier::new(args.clone())?)), false => config.with_no_client_auth(), }; let config = config .with_single_cert(cert_chain, key) .expect("Failed to load TLS certificate!"); Some(config) } else { None }; log::info!("Starting relay on http://{}", args.listen_address); let args_clone = args.clone(); let server = HttpServer::new(move || { App::new() .wrap(middleware::Logger::default()) .app_data(Data::new(args_clone.clone())) .route("/", web::get().to(hello_route)) .route("/config", web::get().to(config_route)) .route("/ws", web::get().to(relay_ws)) }); if let Some(tls_conf) = tls_config { server.bind_rustls_021(&args.listen_address, tls_conf)? } else { server.bind(&args.listen_address)? } .run() .await }