From 51ddf2340f636932e8b50357758fd6d9a8fc65a0 Mon Sep 17 00:00:00 2001 From: Antonio Yang Date: Wed, 17 Jun 2026 16:45:05 +0800 Subject: [PATCH 1/5] server: add try_from_auth_and_config of ServerConfig Downstream implementing can easier to resue method of ServerConfig. --- lightway-server/src/lib.rs | 59 ++++++++++++++++++++++++++++++ lightway-server/src/main.rs | 71 ++++++------------------------------- 2 files changed, 69 insertions(+), 61 deletions(-) diff --git a/lightway-server/src/lib.rs b/lightway-server/src/lib.rs index db6e0a5f..1c59ef47 100644 --- a/lightway-server/src/lib.rs +++ b/lightway-server/src/lib.rs @@ -262,6 +262,65 @@ pub struct ServerConfig ServerAuth>> { pub randomize_ippool: bool, } +impl ServerAuth>> ServerConfig { + pub fn try_from_auth_and_config(auth: SA, config: config::Config) -> Result { + let mut tun_config = lightway_app_utils::TunConfig::default(); + if let Some(tun_name) = config.tun_name { + tun_config.tun_name(tun_name); + } + tun_config.up(); + + Ok(crate::ServerConfig { + mode: match config.mode { + lightway_app_utils::args::ConnectionType::Udp => { + crate::ServerConnectionMode::Datagram(None) + } + lightway_app_utils::args::ConnectionType::Tcp => { + crate::ServerConnectionMode::Stream(None) + } + }, + auth, + server_cert: config.server_cert, + server_key: config.server_key, + tun_config, + ip_pool: config.ip_pool, + ip_map: config.ip_map.unwrap_or_default().try_into()?, + inside_io: None, + tun_ip: config.tun_ip, + lightway_server_ip: config.lightway_server_ip, + lightway_client_ip: config.lightway_client_ip, + lightway_dns_ip: config.lightway_dns_ip, + use_dynamic_client_ip: false, + enable_expresslane: config.enable_expresslane, + expresslane_keys_rotation_interval: config.expresslane_keys_rotation_interval.into(), + expresslane_cb: None, + expresslane_metrics: None, + event_cb: None, + enable_pqc: config.enable_pqc, + #[cfg(target_os = "linux")] + enable_tun_offload: config.enable_tun_offload, + #[cfg(feature = "io-uring")] + enable_tun_iouring: config.enable_tun_iouring, + #[cfg(feature = "io-uring")] + iouring_entry_count: config.iouring_entry_count, + #[cfg(feature = "io-uring")] + iouring_sqpoll_idle_time: config.iouring_sqpoll_idle_time.into(), + key_update_interval: config.key_update_interval.into(), + connection_age_expiration_interval: config.connection_age_expiration_interval.into(), + statistics_reporting_interval: config.statistics_reporting_interval.into(), + inside_plugins: Default::default(), + outside_plugins: Default::default(), + inside_pkt_codec: None, + bind_address: config.bind_address, + proxy_protocol: config.proxy_protocol, + udp_buffer_size: config.udp_buffer_size, + enable_batch_receive: config.enable_batch_receive, + #[cfg(feature = "debug")] + randomize_ippool: config.randomize_ippool, + }) + } +} + pub(crate) fn handle_inside_io_error(conn: Arc, result: ConnectionResult<()>) { match result { Ok(()) => {} diff --git a/lightway-server/src/main.rs b/lightway-server/src/main.rs index a7941745..43120fe8 100644 --- a/lightway-server/src/main.rs +++ b/lightway-server/src/main.rs @@ -1,5 +1,4 @@ mod auth; -mod config; use anyhow::{Context, Result, anyhow}; use clap::Parser; @@ -10,10 +9,10 @@ use tokio::fs::read_to_string; use tokio_stream::StreamExt; use tracing::{error, trace}; -use config::{Config, ConfigPatch}; -use lightway_app_utils::{TunConfig, Validate, validate_configuration_file_path}; +use lightway_app_utils::{Validate, validate_configuration_file_path}; #[cfg(feature = "debug")] use lightway_core::set_logging_callback; +use lightway_server::config::{Config, ConfigPatch}; use lightway_server::*; async fn metrics_debug() { @@ -140,62 +139,12 @@ async fn main() -> Result<()> { } }); - let auth = auth::Auth::new( - config.user_db.as_ref().map(AsRef::as_ref), - config.token_rsa_pub_key_pem.as_ref().map(AsRef::as_ref), - )?; - - let mut tun_config = TunConfig::default(); - if let Some(tun_name) = config.tun_name { - tun_config.tun_name(tun_name); - } - tun_config.up(); - let mode = match config.mode { - lightway_app_utils::args::ConnectionType::Udp => ServerConnectionMode::Datagram(None), - lightway_app_utils::args::ConnectionType::Tcp => ServerConnectionMode::Stream(None), - }; - - let config = ServerConfig { - mode, - auth, - server_cert: config.server_cert, - server_key: config.server_key, - tun_config, - ip_pool: config.ip_pool, - ip_map: config.ip_map.unwrap_or_default().try_into()?, - inside_io: None, - tun_ip: config.tun_ip, - lightway_server_ip: config.lightway_server_ip, - lightway_client_ip: config.lightway_client_ip, - lightway_dns_ip: config.lightway_dns_ip, - use_dynamic_client_ip: false, - enable_expresslane: config.enable_expresslane, - expresslane_keys_rotation_interval: config.expresslane_keys_rotation_interval.into(), - expresslane_cb: None, - expresslane_metrics: None, - event_cb: None, - enable_pqc: config.enable_pqc, - #[cfg(target_os = "linux")] - enable_tun_offload: config.enable_tun_offload, - #[cfg(feature = "io-uring")] - enable_tun_iouring: config.enable_tun_iouring, - #[cfg(feature = "io-uring")] - iouring_entry_count: config.iouring_entry_count, - #[cfg(feature = "io-uring")] - iouring_sqpoll_idle_time: config.iouring_sqpoll_idle_time.into(), - key_update_interval: config.key_update_interval.into(), - connection_age_expiration_interval: config.connection_age_expiration_interval.into(), - statistics_reporting_interval: config.statistics_reporting_interval.into(), - inside_plugins: Default::default(), - outside_plugins: Default::default(), - inside_pkt_codec: None, - bind_address: config.bind_address, - proxy_protocol: config.proxy_protocol, - udp_buffer_size: config.udp_buffer_size, - enable_batch_receive: config.enable_batch_receive, - #[cfg(feature = "debug")] - randomize_ippool: config.randomize_ippool, - }; - - server(config).await + server(crate::ServerConfig::try_from_auth_and_config( + crate::auth::Auth::new( + config.user_db.as_ref().map(AsRef::as_ref), + config.token_rsa_pub_key_pem.as_ref().map(AsRef::as_ref), + )?, + config, + )?) + .await } From 3a6e07c29004b207f0b1ddf628d3165aeb7c6f5f Mon Sep 17 00:00:00 2001 From: Antonio Yang Date: Wed, 17 Jun 2026 16:46:04 +0800 Subject: [PATCH 2/5] server: config: add validate method Define a validation method of the server configuration. --- lightway-server/src/config.rs | 20 ++++++++++++++++++++ lightway-server/src/main.rs | 1 + 2 files changed, 21 insertions(+) diff --git a/lightway-server/src/config.rs b/lightway-server/src/config.rs index 81638478..01eb4616 100644 --- a/lightway-server/src/config.rs +++ b/lightway-server/src/config.rs @@ -225,3 +225,23 @@ impl Default for Config { } } } + +impl Config { + /// Ensure the config is validated, and alerted when there's a conflict in the settings. + pub fn validate(&self) -> anyhow::Result<()> { + Ok(()) + } +} + +// Note it easier to see what is different from default in each testcase +#[allow(clippy::field_reassign_with_default)] +#[cfg(test)] +mod tests { + use super::*; + + #[test] + fn validate_default_config() { + let config = Config::default(); + assert!(config.validate().is_ok()); + } +} diff --git a/lightway-server/src/main.rs b/lightway-server/src/main.rs index 43120fe8..5e9aed8d 100644 --- a/lightway-server/src/main.rs +++ b/lightway-server/src/main.rs @@ -97,6 +97,7 @@ async fn main() -> Result<()> { validate_configuration_file_path(user_db, Validate::OwnerOnly) .with_context(|| format!("Invalid user db file {}", user_db.display()))?; } + config.validate()?; #[cfg(feature = "debug")] if config.tls_debug { From 9afbaaa61dc5befe2ccae60cc9df217561df9bc3 Mon Sep 17 00:00:00 2001 From: Antonio Yang Date: Wed, 17 Jun 2026 16:46:44 +0800 Subject: [PATCH 3/5] server: config: validate enable_expresslane Validate expressland, it should only work in udp mode. --- lightway-app-utils/src/args/connection_type.rs | 5 +++++ lightway-server/src/config.rs | 12 ++++++++++++ 2 files changed, 17 insertions(+) diff --git a/lightway-app-utils/src/args/connection_type.rs b/lightway-app-utils/src/args/connection_type.rs index 362d0278..4767eff8 100644 --- a/lightway-app-utils/src/args/connection_type.rs +++ b/lightway-app-utils/src/args/connection_type.rs @@ -23,6 +23,11 @@ impl ConnectionType { pub fn is_tcp(&self) -> bool { *self == ConnectionType::Tcp } + + #[allow(missing_docs)] + pub fn is_udp(&self) -> bool { + *self == ConnectionType::Udp + } } impl From for LWConnectionType { diff --git a/lightway-server/src/config.rs b/lightway-server/src/config.rs index 01eb4616..9a4db6eb 100644 --- a/lightway-server/src/config.rs +++ b/lightway-server/src/config.rs @@ -229,6 +229,10 @@ impl Default for Config { impl Config { /// Ensure the config is validated, and alerted when there's a conflict in the settings. pub fn validate(&self) -> anyhow::Result<()> { + if self.enable_expresslane { + anyhow::ensure!(self.mode.is_udp(), "Expresslane only work in udp mode") + } + Ok(()) } } @@ -244,4 +248,12 @@ mod tests { let config = Config::default(); assert!(config.validate().is_ok()); } + + #[test] + fn validate_enable_expresslane() { + let mut config = Config::default(); + config.mode = ConnectionType::Tcp; + config.enable_expresslane = true; + assert!(config.validate().is_err()); + } } From ed9dc0e7519b1ddaa3b5f88d4e23c11b51a51d59 Mon Sep 17 00:00:00 2001 From: Antonio Yang Date: Wed, 17 Jun 2026 16:47:16 +0800 Subject: [PATCH 4/5] server: config: validate proxy_protocol Check proxy protocol is set only with tcp mode. --- lightway-app-utils/src/args/connection_type.rs | 2 +- lightway-server/src/config.rs | 15 +++++++++++++++ 2 files changed, 16 insertions(+), 1 deletion(-) diff --git a/lightway-app-utils/src/args/connection_type.rs b/lightway-app-utils/src/args/connection_type.rs index 4767eff8..17ccd72e 100644 --- a/lightway-app-utils/src/args/connection_type.rs +++ b/lightway-app-utils/src/args/connection_type.rs @@ -19,7 +19,7 @@ pub enum ConnectionType { } impl ConnectionType { - /// A helper function easier to use especially in mobile + #[allow(missing_docs)] pub fn is_tcp(&self) -> bool { *self == ConnectionType::Tcp } diff --git a/lightway-server/src/config.rs b/lightway-server/src/config.rs index 9a4db6eb..2d8fe5dc 100644 --- a/lightway-server/src/config.rs +++ b/lightway-server/src/config.rs @@ -233,6 +233,13 @@ impl Config { anyhow::ensure!(self.mode.is_udp(), "Expresslane only work in udp mode") } + if self.proxy_protocol { + anyhow::ensure!( + self.mode.is_tcp(), + "Proxy protocol only support with tcp mode" + ) + } + Ok(()) } } @@ -256,4 +263,12 @@ mod tests { config.enable_expresslane = true; assert!(config.validate().is_err()); } + + #[test] + fn validate_proxy_protocol() { + let mut config = Config::default(); + config.mode = ConnectionType::Udp; + config.proxy_protocol = true; + assert!(config.validate().is_err()); + } } From c20fffeb335c08ee44d5f5040ecd064ba344b984 Mon Sep 17 00:00:00 2001 From: Antonio Yang Date: Wed, 17 Jun 2026 16:47:59 +0800 Subject: [PATCH 5/5] server: config: validate when ServerConfig build The validation of confg is enforced when building up ServerConfig, because the validataion is general and should not be skiped. --- lightway-server/src/lib.rs | 2 ++ lightway-server/src/main.rs | 17 ++++++++--------- 2 files changed, 10 insertions(+), 9 deletions(-) diff --git a/lightway-server/src/lib.rs b/lightway-server/src/lib.rs index 1c59ef47..42685d28 100644 --- a/lightway-server/src/lib.rs +++ b/lightway-server/src/lib.rs @@ -264,6 +264,8 @@ pub struct ServerConfig ServerAuth>> { impl ServerAuth>> ServerConfig { pub fn try_from_auth_and_config(auth: SA, config: config::Config) -> Result { + config.validate()?; + let mut tun_config = lightway_app_utils::TunConfig::default(); if let Some(tun_name) = config.tun_name { tun_config.tun_name(tun_name); diff --git a/lightway-server/src/main.rs b/lightway-server/src/main.rs index 5e9aed8d..c50cc06b 100644 --- a/lightway-server/src/main.rs +++ b/lightway-server/src/main.rs @@ -97,7 +97,6 @@ async fn main() -> Result<()> { validate_configuration_file_path(user_db, Validate::OwnerOnly) .with_context(|| format!("Invalid user db file {}", user_db.display()))?; } - config.validate()?; #[cfg(feature = "debug")] if config.tls_debug { @@ -115,6 +114,13 @@ async fn main() -> Result<()> { let fmt = tracing_subscriber::fmt().with_env_filter(filter); config.log_format.init_with_env_filter(fmt); + let server_config = crate::ServerConfig::try_from_auth_and_config( + crate::auth::Auth::new( + config.user_db.as_ref().map(AsRef::as_ref), + config.token_rsa_pub_key_pem.as_ref().map(AsRef::as_ref), + )?, + config, + )?; tokio::spawn(metrics_debug()); @@ -140,12 +146,5 @@ async fn main() -> Result<()> { } }); - server(crate::ServerConfig::try_from_auth_and_config( - crate::auth::Auth::new( - config.user_db.as_ref().map(AsRef::as_ref), - config.token_rsa_pub_key_pem.as_ref().map(AsRef::as_ref), - )?, - config, - )?) - .await + server(server_config).await }