use anyhow::{anyhow, Context, Result};
use clap::Parser;
use portredirect::app_data::ServerAppData;
use portredirect::get_config_dir;
use portredirect::quic::server::{run_quic_server, ServerConfig};
use portredirect::server::client_handler::handle_quic_client_connection;
use secrecy::SecretString;
use std::net::{SocketAddr, ToSocketAddrs};
use tracing::{info, span, Level};
#[derive(Parser, Debug)]
struct Args {
#[clap(long)]
config_dir: Option<String>,
#[clap(long)]
local_host: String,
#[clap(long)]
local_port: u16,
#[clap(long)]
clients_additional_listeners: bool,
#[clap(long, default_value = "127.0.0.1")]
quic_server_host: String,
#[clap(long, default_value = "4433")]
quic_server_port: u16,
#[clap(long, default_value = "127.0.0.1")]
quic_cert_hostname: String,
#[clap(long)]
quic_psk: SecretString,
}
#[tokio::main]
async fn main() -> Result<()> {
setup_tracing();
let _root_span = span!(Level::INFO, "prserver_main").entered();
rustls::crypto::ring::default_provider()
.install_default()
.expect("Failed to install rustls crypto provider");
let args = Args::parse();
let config_dir =
get_config_dir(args.config_dir).context("Failed to get configuration directory")?;
info!("Configuration directory: {:?}", config_dir);
let local_addr = resolve_socket_addr(&format!("{}:{}", args.local_host, args.local_port))
.context("Failed to resolve local TCP bind address")?;
let quic_addr = resolve_socket_addr(&format!(
"{}:{}",
args.quic_server_host, args.quic_server_port
))
.context("Failed to resolve QUIC bind address")?;
let app_data = ServerAppData::new(args.quic_psk, local_addr);
info!("QUIC will listen on {}", quic_addr);
let quic_config = ServerConfig::create_default_config(
config_dir,
args.quic_cert_hostname,
quic_addr,
None,
app_data.clone(),
);
run_quic_server(quic_config, handle_quic_client_connection)
.await
.with_context(|| "PortRedirect Server Error")?;
info!("PortRedirect Server exited cleanly");
Ok(())
}
fn setup_tracing() {
tracing_subscriber::fmt()
.with_max_level(Level::DEBUG)
.with_target(true)
.with_line_number(true)
.init();
}
fn resolve_socket_addr(addr: &str) -> Result<SocketAddr> {
addr.to_socket_addrs()?
.next()
.ok_or_else(|| anyhow!("Unable to resolve address: {}", addr))
}