From 16b5dc56f0b429da5856a6893b8d25505c35e268 Mon Sep 17 00:00:00 2001 From: vladon Date: Mon, 16 Feb 2026 17:26:46 +0300 Subject: [PATCH] feat: extend announce_ip to accept hostnames MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add new 'announce' field to ListenerConfig that accepts both IP addresses and hostnames for proxy link generation. The old 'announce_ip' field is deprecated but still supported via automatic migration. Changes: - Add 'announce: Option' field to ListenerConfig - Add migration logic: announce_ip → announce if announce not set - Update main.rs to use announce field for link generation - Support both hostnames (e.g., 'proxy.example.com') and IPs Backward compatible: existing configs using announce_ip continue to work. --- src/config/mod.rs | 15 +++++++++++++++ src/main.rs | 18 ++++++++++++------ 2 files changed, 27 insertions(+), 6 deletions(-) diff --git a/src/config/mod.rs b/src/config/mod.rs index 42190e6..f9d2131 100644 --- a/src/config/mod.rs +++ b/src/config/mod.rs @@ -497,6 +497,12 @@ pub struct UpstreamConfig { #[derive(Debug, Clone, Serialize, Deserialize)] pub struct ListenerConfig { pub ip: IpAddr, + /// IP address or hostname to announce in proxy links. + /// Takes precedence over `announce_ip` if both are set. + #[serde(default)] + pub announce: Option, + /// Deprecated: Use `announce` instead. IP address to announce in proxy links. + /// Migrated to `announce` automatically if `announce` is not set. #[serde(default)] pub announce_ip: Option, } @@ -716,6 +722,7 @@ impl ProxyConfig { if let Ok(ipv4) = ipv4_str.parse::() { config.server.listeners.push(ListenerConfig { ip: ipv4, + announce: None, announce_ip: None, }); } @@ -723,12 +730,20 @@ impl ProxyConfig { if let Ok(ipv6) = ipv6_str.parse::() { config.server.listeners.push(ListenerConfig { ip: ipv6, + announce: None, announce_ip: None, }); } } } + // Migration: announce_ip → announce for each listener + for listener in &mut config.server.listeners { + if listener.announce.is_none() && listener.announce_ip.is_some() { + listener.announce = Some(listener.announce_ip.unwrap().to_string()); + } + } + // Migration: show_link (top-level) → general.links.show if !config.show_link.is_empty() && config.general.links.show.is_empty() { config.general.links.show = config.show_link.clone(); diff --git a/src/main.rs b/src/main.rs index 4a6d43d..ba00879 100644 --- a/src/main.rs +++ b/src/main.rs @@ -563,22 +563,28 @@ match crate::transport::middle_proxy::fetch_proxy_secret(proxy_secret_path).awai let listener = TcpListener::from_std(socket.into())?; info!("Listening on {}", addr); - let public_ip = if let Some(ip) = listener_conf.announce_ip { - ip + // Resolve the public host for link generation + let public_host = if let Some(ref announce) = listener_conf.announce { + announce.clone() // Use announce (IP or hostname) if explicitly set } else if listener_conf.ip.is_unspecified() { + // Auto-detect for unspecified addresses if listener_conf.ip.is_ipv4() { - detected_ip.ipv4.unwrap_or(listener_conf.ip) + detected_ip.ipv4 + .map(|ip| ip.to_string()) + .unwrap_or_else(|| listener_conf.ip.to_string()) } else { - detected_ip.ipv6.unwrap_or(listener_conf.ip) + detected_ip.ipv6 + .map(|ip| ip.to_string()) + .unwrap_or_else(|| listener_conf.ip.to_string()) } } else { - listener_conf.ip + listener_conf.ip.to_string() }; // Show per-listener proxy links only when public_host is not set if config.general.links.public_host.is_none() && !config.general.links.show.is_empty() { let link_port = config.general.links.public_port.unwrap_or(config.server.port); - print_proxy_links(&public_ip.to_string(), link_port, &config); + print_proxy_links(&public_host, link_port, &config); } listeners.push(listener);