mirror of
https://github.com/telemt/telemt.git
synced 2026-04-18 19:14:09 +03:00
Server.Listeners + Upstream V4/V6
Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
@@ -452,7 +452,11 @@ fn build_user_links(
|
|||||||
startup_detected_ip_v6: Option<IpAddr>,
|
startup_detected_ip_v6: Option<IpAddr>,
|
||||||
) -> UserLinks {
|
) -> UserLinks {
|
||||||
let hosts = resolve_link_hosts(cfg, startup_detected_ip_v4, startup_detected_ip_v6);
|
let hosts = resolve_link_hosts(cfg, startup_detected_ip_v4, startup_detected_ip_v6);
|
||||||
let port = cfg.general.links.public_port.unwrap_or(cfg.server.port);
|
let port = cfg
|
||||||
|
.general
|
||||||
|
.links
|
||||||
|
.public_port
|
||||||
|
.unwrap_or(resolve_default_link_port(cfg));
|
||||||
let tls_domains = resolve_tls_domains(cfg);
|
let tls_domains = resolve_tls_domains(cfg);
|
||||||
|
|
||||||
let mut classic = Vec::new();
|
let mut classic = Vec::new();
|
||||||
@@ -490,6 +494,14 @@ fn build_user_links(
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_default_link_port(cfg: &ProxyConfig) -> u16 {
|
||||||
|
cfg.server
|
||||||
|
.listeners
|
||||||
|
.first()
|
||||||
|
.and_then(|listener| listener.port)
|
||||||
|
.unwrap_or(cfg.server.port)
|
||||||
|
}
|
||||||
|
|
||||||
fn resolve_link_hosts(
|
fn resolve_link_hosts(
|
||||||
cfg: &ProxyConfig,
|
cfg: &ProxyConfig,
|
||||||
startup_detected_ip_v4: Option<IpAddr>,
|
startup_detected_ip_v4: Option<IpAddr>,
|
||||||
|
|||||||
@@ -598,16 +598,17 @@ secure = false
|
|||||||
tls = true
|
tls = true
|
||||||
|
|
||||||
[server]
|
[server]
|
||||||
port = {port}
|
|
||||||
listen_addr_ipv4 = "0.0.0.0"
|
listen_addr_ipv4 = "0.0.0.0"
|
||||||
listen_addr_ipv6 = "::"
|
listen_addr_ipv6 = "::"
|
||||||
|
|
||||||
[[server.listeners]]
|
[[server.listeners]]
|
||||||
ip = "0.0.0.0"
|
ip = "0.0.0.0"
|
||||||
|
port = {port}
|
||||||
# reuse_allow = false # Set true only when intentionally running multiple telemt instances on same port
|
# reuse_allow = false # Set true only when intentionally running multiple telemt instances on same port
|
||||||
|
|
||||||
[[server.listeners]]
|
[[server.listeners]]
|
||||||
ip = "::"
|
ip = "::"
|
||||||
|
port = {port}
|
||||||
|
|
||||||
[timeouts]
|
[timeouts]
|
||||||
client_first_byte_idle_secs = 300
|
client_first_byte_idle_secs = 300
|
||||||
|
|||||||
@@ -17,8 +17,9 @@
|
|||||||
//! | `network` | `dns_overrides` | Applied immediately |
|
//! | `network` | `dns_overrides` | Applied immediately |
|
||||||
//! | `access` | All user/quota fields | Effective immediately |
|
//! | `access` | All user/quota fields | Effective immediately |
|
||||||
//!
|
//!
|
||||||
//! Fields that require re-binding sockets (`server.port`, `censorship.*`,
|
//! Fields that require re-binding sockets (`server.listeners`, legacy
|
||||||
//! `network.*`, `use_middle_proxy`) are **not** applied; a warning is emitted.
|
//! `server.port`, `censorship.*`, `network.*`, `use_middle_proxy`) are **not**
|
||||||
|
//! applied; a warning is emitted.
|
||||||
//! Non-hot changes are never mixed into the runtime config snapshot.
|
//! Non-hot changes are never mixed into the runtime config snapshot.
|
||||||
|
|
||||||
use std::collections::BTreeSet;
|
use std::collections::BTreeSet;
|
||||||
@@ -299,6 +300,7 @@ fn listeners_equal(
|
|||||||
}
|
}
|
||||||
lhs.iter().zip(rhs.iter()).all(|(a, b)| {
|
lhs.iter().zip(rhs.iter()).all(|(a, b)| {
|
||||||
a.ip == b.ip
|
a.ip == b.ip
|
||||||
|
&& a.port == b.port
|
||||||
&& a.announce == b.announce
|
&& a.announce == b.announce
|
||||||
&& a.announce_ip == b.announce_ip
|
&& a.announce_ip == b.announce_ip
|
||||||
&& a.proxy_protocol == b.proxy_protocol
|
&& a.proxy_protocol == b.proxy_protocol
|
||||||
@@ -306,6 +308,14 @@ fn listeners_equal(
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_default_link_port(cfg: &ProxyConfig) -> u16 {
|
||||||
|
cfg.server
|
||||||
|
.listeners
|
||||||
|
.first()
|
||||||
|
.and_then(|listener| listener.port)
|
||||||
|
.unwrap_or(cfg.server.port)
|
||||||
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
#[derive(Debug, Clone, Default, PartialEq, Eq)]
|
||||||
struct WatchManifest {
|
struct WatchManifest {
|
||||||
files: BTreeSet<PathBuf>,
|
files: BTreeSet<PathBuf>,
|
||||||
@@ -1120,7 +1130,7 @@ fn log_changes(
|
|||||||
.general
|
.general
|
||||||
.links
|
.links
|
||||||
.public_port
|
.public_port
|
||||||
.unwrap_or(new_cfg.server.port);
|
.unwrap_or(resolve_default_link_port(new_cfg));
|
||||||
for user in &added {
|
for user in &added {
|
||||||
if let Some(secret) = new_hot.users.get(*user) {
|
if let Some(secret) = new_hot.users.get(*user) {
|
||||||
print_user_links(user, secret, &host, port, new_cfg);
|
print_user_links(user, secret, &host, port, new_cfg);
|
||||||
|
|||||||
@@ -253,6 +253,12 @@ fn validate_upstreams(config: &ProxyConfig) -> Result<()> {
|
|||||||
}
|
}
|
||||||
|
|
||||||
for upstream in &config.upstreams {
|
for upstream in &config.upstreams {
|
||||||
|
if matches!(upstream.ipv4, Some(false)) && matches!(upstream.ipv6, Some(false)) {
|
||||||
|
return Err(ProxyError::Config(
|
||||||
|
"upstream.ipv4 and upstream.ipv6 cannot both be false".to_string(),
|
||||||
|
));
|
||||||
|
}
|
||||||
|
|
||||||
if let UpstreamType::Shadowsocks { url, .. } = &upstream.upstream_type {
|
if let UpstreamType::Shadowsocks { url, .. } = &upstream.upstream_type {
|
||||||
let parsed = ShadowsocksServerConfig::from_url(url)
|
let parsed = ShadowsocksServerConfig::from_url(url)
|
||||||
.map_err(|error| ProxyError::Config(format!("invalid shadowsocks url: {error}")))?;
|
.map_err(|error| ProxyError::Config(format!("invalid shadowsocks url: {error}")))?;
|
||||||
@@ -1324,6 +1330,7 @@ impl ProxyConfig {
|
|||||||
if let Ok(ipv4) = ipv4_str.parse::<IpAddr>() {
|
if let Ok(ipv4) = ipv4_str.parse::<IpAddr>() {
|
||||||
config.server.listeners.push(ListenerConfig {
|
config.server.listeners.push(ListenerConfig {
|
||||||
ip: ipv4,
|
ip: ipv4,
|
||||||
|
port: Some(config.server.port),
|
||||||
announce: None,
|
announce: None,
|
||||||
announce_ip: None,
|
announce_ip: None,
|
||||||
proxy_protocol: None,
|
proxy_protocol: None,
|
||||||
@@ -1335,6 +1342,7 @@ impl ProxyConfig {
|
|||||||
{
|
{
|
||||||
config.server.listeners.push(ListenerConfig {
|
config.server.listeners.push(ListenerConfig {
|
||||||
ip: ipv6,
|
ip: ipv6,
|
||||||
|
port: Some(config.server.port),
|
||||||
announce: None,
|
announce: None,
|
||||||
announce_ip: None,
|
announce_ip: None,
|
||||||
proxy_protocol: None,
|
proxy_protocol: None,
|
||||||
@@ -1343,6 +1351,13 @@ impl ProxyConfig {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Migration: listeners[].port fallback to legacy server.port.
|
||||||
|
for listener in &mut config.server.listeners {
|
||||||
|
if listener.port.is_none() {
|
||||||
|
listener.port = Some(config.server.port);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Migration: announce_ip → announce for each listener.
|
// Migration: announce_ip → announce for each listener.
|
||||||
for listener in &mut config.server.listeners {
|
for listener in &mut config.server.listeners {
|
||||||
if listener.announce.is_none()
|
if listener.announce.is_none()
|
||||||
@@ -1369,6 +1384,8 @@ impl ProxyConfig {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -1153,7 +1153,8 @@ pub struct LinksConfig {
|
|||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub public_host: Option<String>,
|
pub public_host: Option<String>,
|
||||||
|
|
||||||
/// Public port for tg:// link generation (overrides server.port).
|
/// Public port for tg:// link generation.
|
||||||
|
/// Overrides listener ports and legacy `server.port`.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
pub public_port: Option<u16>,
|
pub public_port: Option<u16>,
|
||||||
}
|
}
|
||||||
@@ -1375,6 +1376,8 @@ impl Default for ConntrackControlConfig {
|
|||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ServerConfig {
|
pub struct ServerConfig {
|
||||||
|
/// Legacy listener port used for backward compatibility.
|
||||||
|
/// For new configs prefer `[[server.listeners]].port`.
|
||||||
#[serde(default = "default_port")]
|
#[serde(default = "default_port")]
|
||||||
pub port: u16,
|
pub port: u16,
|
||||||
|
|
||||||
@@ -1917,11 +1920,22 @@ pub struct UpstreamConfig {
|
|||||||
pub scopes: String,
|
pub scopes: String,
|
||||||
#[serde(skip)]
|
#[serde(skip)]
|
||||||
pub selected_scope: String,
|
pub selected_scope: String,
|
||||||
|
/// Allow IPv4 DC targets for this upstream.
|
||||||
|
/// `None` means auto-detect from runtime connectivity state.
|
||||||
|
#[serde(default)]
|
||||||
|
pub ipv4: Option<bool>,
|
||||||
|
/// Allow IPv6 DC targets for this upstream.
|
||||||
|
/// `None` means auto-detect from runtime connectivity state.
|
||||||
|
#[serde(default)]
|
||||||
|
pub ipv6: Option<bool>,
|
||||||
}
|
}
|
||||||
|
|
||||||
#[derive(Debug, Clone, Serialize, Deserialize)]
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
pub struct ListenerConfig {
|
pub struct ListenerConfig {
|
||||||
pub ip: IpAddr,
|
pub ip: IpAddr,
|
||||||
|
/// Per-listener TCP port. If omitted, falls back to legacy `server.port`.
|
||||||
|
#[serde(default)]
|
||||||
|
pub port: Option<u16>,
|
||||||
/// IP address or hostname to announce in proxy links.
|
/// IP address or hostname to announce in proxy links.
|
||||||
/// Takes precedence over `announce_ip` if both are set.
|
/// Takes precedence over `announce_ip` if both are set.
|
||||||
#[serde(default)]
|
#[serde(default)]
|
||||||
|
|||||||
@@ -343,15 +343,28 @@ fn command_exists(binary: &str) -> bool {
|
|||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
fn notrack_targets(cfg: &ProxyConfig) -> (Vec<Option<IpAddr>>, Vec<Option<IpAddr>>) {
|
fn listener_port_set(cfg: &ProxyConfig) -> Vec<u16> {
|
||||||
|
let mut ports: BTreeSet<u16> = BTreeSet::new();
|
||||||
|
if cfg.server.listeners.is_empty() {
|
||||||
|
ports.insert(cfg.server.port);
|
||||||
|
} else {
|
||||||
|
for listener in &cfg.server.listeners {
|
||||||
|
ports.insert(listener.port.unwrap_or(cfg.server.port));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ports.into_iter().collect()
|
||||||
|
}
|
||||||
|
|
||||||
|
fn notrack_targets(cfg: &ProxyConfig) -> (Vec<(Option<IpAddr>, u16)>, Vec<(Option<IpAddr>, u16)>) {
|
||||||
let mode = cfg.server.conntrack_control.mode;
|
let mode = cfg.server.conntrack_control.mode;
|
||||||
let mut v4_targets: BTreeSet<Option<IpAddr>> = BTreeSet::new();
|
let mut v4_targets: BTreeSet<(Option<IpAddr>, u16)> = BTreeSet::new();
|
||||||
let mut v6_targets: BTreeSet<Option<IpAddr>> = BTreeSet::new();
|
let mut v6_targets: BTreeSet<(Option<IpAddr>, u16)> = BTreeSet::new();
|
||||||
|
|
||||||
match mode {
|
match mode {
|
||||||
ConntrackMode::Tracked => {}
|
ConntrackMode::Tracked => {}
|
||||||
ConntrackMode::Notrack => {
|
ConntrackMode::Notrack => {
|
||||||
if cfg.server.listeners.is_empty() {
|
if cfg.server.listeners.is_empty() {
|
||||||
|
let port = cfg.server.port;
|
||||||
if let Some(ipv4) = cfg
|
if let Some(ipv4) = cfg
|
||||||
.server
|
.server
|
||||||
.listen_addr_ipv4
|
.listen_addr_ipv4
|
||||||
@@ -359,9 +372,9 @@ fn notrack_targets(cfg: &ProxyConfig) -> (Vec<Option<IpAddr>>, Vec<Option<IpAddr
|
|||||||
.and_then(|s| s.parse::<IpAddr>().ok())
|
.and_then(|s| s.parse::<IpAddr>().ok())
|
||||||
{
|
{
|
||||||
if ipv4.is_unspecified() {
|
if ipv4.is_unspecified() {
|
||||||
v4_targets.insert(None);
|
v4_targets.insert((None, port));
|
||||||
} else {
|
} else {
|
||||||
v4_targets.insert(Some(ipv4));
|
v4_targets.insert((Some(ipv4), port));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let Some(ipv6) = cfg
|
if let Some(ipv6) = cfg
|
||||||
@@ -371,33 +384,39 @@ fn notrack_targets(cfg: &ProxyConfig) -> (Vec<Option<IpAddr>>, Vec<Option<IpAddr
|
|||||||
.and_then(|s| s.parse::<IpAddr>().ok())
|
.and_then(|s| s.parse::<IpAddr>().ok())
|
||||||
{
|
{
|
||||||
if ipv6.is_unspecified() {
|
if ipv6.is_unspecified() {
|
||||||
v6_targets.insert(None);
|
v6_targets.insert((None, port));
|
||||||
} else {
|
} else {
|
||||||
v6_targets.insert(Some(ipv6));
|
v6_targets.insert((Some(ipv6), port));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
for listener in &cfg.server.listeners {
|
for listener in &cfg.server.listeners {
|
||||||
|
let port = listener.port.unwrap_or(cfg.server.port);
|
||||||
if listener.ip.is_ipv4() {
|
if listener.ip.is_ipv4() {
|
||||||
if listener.ip.is_unspecified() {
|
if listener.ip.is_unspecified() {
|
||||||
v4_targets.insert(None);
|
v4_targets.insert((None, port));
|
||||||
} else {
|
} else {
|
||||||
v4_targets.insert(Some(listener.ip));
|
v4_targets.insert((Some(listener.ip), port));
|
||||||
}
|
}
|
||||||
} else if listener.ip.is_unspecified() {
|
} else if listener.ip.is_unspecified() {
|
||||||
v6_targets.insert(None);
|
v6_targets.insert((None, port));
|
||||||
} else {
|
} else {
|
||||||
v6_targets.insert(Some(listener.ip));
|
v6_targets.insert((Some(listener.ip), port));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
ConntrackMode::Hybrid => {
|
ConntrackMode::Hybrid => {
|
||||||
|
let ports = listener_port_set(cfg);
|
||||||
for ip in &cfg.server.conntrack_control.hybrid_listener_ips {
|
for ip in &cfg.server.conntrack_control.hybrid_listener_ips {
|
||||||
if ip.is_ipv4() {
|
if ip.is_ipv4() {
|
||||||
v4_targets.insert(Some(*ip));
|
for port in &ports {
|
||||||
|
v4_targets.insert((Some(*ip), *port));
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
v6_targets.insert(Some(*ip));
|
for port in &ports {
|
||||||
|
v6_targets.insert((Some(*ip), *port));
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -422,19 +441,19 @@ async fn apply_nft_rules(cfg: &ProxyConfig) -> Result<(), String> {
|
|||||||
|
|
||||||
let (v4_targets, v6_targets) = notrack_targets(cfg);
|
let (v4_targets, v6_targets) = notrack_targets(cfg);
|
||||||
let mut rules = Vec::new();
|
let mut rules = Vec::new();
|
||||||
for ip in v4_targets {
|
for (ip, port) in v4_targets {
|
||||||
let rule = if let Some(ip) = ip {
|
let rule = if let Some(ip) = ip {
|
||||||
format!("tcp dport {} ip daddr {} notrack", cfg.server.port, ip)
|
format!("tcp dport {} ip daddr {} notrack", port, ip)
|
||||||
} else {
|
} else {
|
||||||
format!("tcp dport {} notrack", cfg.server.port)
|
format!("tcp dport {} notrack", port)
|
||||||
};
|
};
|
||||||
rules.push(rule);
|
rules.push(rule);
|
||||||
}
|
}
|
||||||
for ip in v6_targets {
|
for (ip, port) in v6_targets {
|
||||||
let rule = if let Some(ip) = ip {
|
let rule = if let Some(ip) = ip {
|
||||||
format!("tcp dport {} ip6 daddr {} notrack", cfg.server.port, ip)
|
format!("tcp dport {} ip6 daddr {} notrack", port, ip)
|
||||||
} else {
|
} else {
|
||||||
format!("tcp dport {} notrack", cfg.server.port)
|
format!("tcp dport {} notrack", port)
|
||||||
};
|
};
|
||||||
rules.push(rule);
|
rules.push(rule);
|
||||||
}
|
}
|
||||||
@@ -498,7 +517,7 @@ async fn apply_iptables_rules_for_binary(
|
|||||||
|
|
||||||
let (v4_targets, v6_targets) = notrack_targets(cfg);
|
let (v4_targets, v6_targets) = notrack_targets(cfg);
|
||||||
let selected = if ipv4 { v4_targets } else { v6_targets };
|
let selected = if ipv4 { v4_targets } else { v6_targets };
|
||||||
for ip in selected {
|
for (ip, port) in selected {
|
||||||
let mut args = vec![
|
let mut args = vec![
|
||||||
"-t".to_string(),
|
"-t".to_string(),
|
||||||
"raw".to_string(),
|
"raw".to_string(),
|
||||||
@@ -507,7 +526,7 @@ async fn apply_iptables_rules_for_binary(
|
|||||||
"-p".to_string(),
|
"-p".to_string(),
|
||||||
"tcp".to_string(),
|
"tcp".to_string(),
|
||||||
"--dport".to_string(),
|
"--dport".to_string(),
|
||||||
cfg.server.port.to_string(),
|
port.to_string(),
|
||||||
];
|
];
|
||||||
if let Some(ip) = ip {
|
if let Some(ip) = ip {
|
||||||
args.push("-d".to_string());
|
args.push("-d".to_string());
|
||||||
|
|||||||
@@ -31,6 +31,19 @@ pub(crate) struct BoundListeners {
|
|||||||
pub(crate) has_unix_listener: bool,
|
pub(crate) has_unix_listener: bool,
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn listener_port_or_legacy(listener: &crate::config::ListenerConfig, config: &ProxyConfig) -> u16 {
|
||||||
|
listener.port.unwrap_or(config.server.port)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn default_link_port(config: &ProxyConfig) -> u16 {
|
||||||
|
config
|
||||||
|
.server
|
||||||
|
.listeners
|
||||||
|
.first()
|
||||||
|
.and_then(|listener| listener.port)
|
||||||
|
.unwrap_or(config.server.port)
|
||||||
|
}
|
||||||
|
|
||||||
#[allow(clippy::too_many_arguments)]
|
#[allow(clippy::too_many_arguments)]
|
||||||
pub(crate) async fn bind_listeners(
|
pub(crate) async fn bind_listeners(
|
||||||
config: &Arc<ProxyConfig>,
|
config: &Arc<ProxyConfig>,
|
||||||
@@ -63,7 +76,8 @@ pub(crate) async fn bind_listeners(
|
|||||||
let mut listeners = Vec::new();
|
let mut listeners = Vec::new();
|
||||||
|
|
||||||
for listener_conf in &config.server.listeners {
|
for listener_conf in &config.server.listeners {
|
||||||
let addr = SocketAddr::new(listener_conf.ip, config.server.port);
|
let listener_port = listener_port_or_legacy(listener_conf, config);
|
||||||
|
let addr = SocketAddr::new(listener_conf.ip, listener_port);
|
||||||
if addr.is_ipv4() && !decision_ipv4_dc {
|
if addr.is_ipv4() && !decision_ipv4_dc {
|
||||||
warn!(%addr, "Skipping IPv4 listener: IPv4 disabled by [network]");
|
warn!(%addr, "Skipping IPv4 listener: IPv4 disabled by [network]");
|
||||||
continue;
|
continue;
|
||||||
@@ -110,7 +124,7 @@ pub(crate) async fn bind_listeners(
|
|||||||
.general
|
.general
|
||||||
.links
|
.links
|
||||||
.public_port
|
.public_port
|
||||||
.unwrap_or(config.server.port);
|
.unwrap_or(listener_port);
|
||||||
print_proxy_links(&public_host, link_port, config);
|
print_proxy_links(&public_host, link_port, config);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -158,7 +172,7 @@ pub(crate) async fn bind_listeners(
|
|||||||
.general
|
.general
|
||||||
.links
|
.links
|
||||||
.public_port
|
.public_port
|
||||||
.unwrap_or(config.server.port),
|
.unwrap_or(default_link_port(config)),
|
||||||
)
|
)
|
||||||
} else {
|
} else {
|
||||||
let ip = detected_ip_v4.or(detected_ip_v6).map(|ip| ip.to_string());
|
let ip = detected_ip_v4.or(detected_ip_v6).map(|ip| ip.to_string());
|
||||||
@@ -173,7 +187,7 @@ pub(crate) async fn bind_listeners(
|
|||||||
.general
|
.general
|
||||||
.links
|
.links
|
||||||
.public_port
|
.public_port
|
||||||
.unwrap_or(config.server.port),
|
.unwrap_or(default_link_port(config)),
|
||||||
)
|
)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|||||||
@@ -37,6 +37,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ fn build_harness(config: ProxyConfig) -> PipelineHarness {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -44,6 +44,8 @@ fn build_harness(secret_hex: &str, mask_port: u16) -> PipelineHarness {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -22,6 +22,8 @@ fn make_test_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -45,6 +45,8 @@ fn build_harness(secret_hex: &str, mask_port: u16) -> RedTeamHarness {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -236,6 +238,8 @@ async fn redteam_03_masking_duration_must_be_less_than_1ms_when_backend_down() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -478,6 +482,8 @@ async fn measure_invalid_probe_duration_ms(delay_ms: u64, tls_len: u16, body_sen
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -553,6 +559,8 @@ async fn capture_forwarded_probe_len(tls_len: u16, body_sent: usize) -> usize {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -19,6 +19,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -17,6 +17,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -31,6 +31,8 @@ fn new_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -338,6 +338,8 @@ async fn relay_task_abort_releases_user_gate_and_ip_reservation() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -453,6 +455,8 @@ async fn relay_cutover_releases_user_gate_and_ip_reservation() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -578,6 +582,8 @@ async fn integration_route_cutover_and_quota_overlap_fails_closed_and_releases_s
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -749,6 +755,8 @@ async fn proxy_protocol_header_is_rejected_when_trust_list_is_empty() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -827,6 +835,8 @@ async fn proxy_protocol_header_from_untrusted_peer_range_is_rejected_under_load(
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -988,6 +998,8 @@ async fn short_tls_probe_is_masked_through_client_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1077,6 +1089,8 @@ async fn tls12_record_probe_is_masked_through_client_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1164,6 +1178,8 @@ async fn handle_client_stream_increments_connects_all_exactly_once() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1258,6 +1274,8 @@ async fn running_client_handler_increments_connects_all_exactly_once() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1349,6 +1367,8 @@ async fn idle_pooled_connection_closes_cleanly_in_generic_stream_path() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1421,6 +1441,8 @@ async fn idle_pooled_connection_closes_cleanly_in_client_handler_path() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1508,6 +1530,8 @@ async fn partial_tls_header_stall_triggers_handshake_timeout() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1834,6 +1858,8 @@ async fn valid_tls_path_does_not_fall_back_to_mask_backend() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1944,6 +1970,8 @@ async fn valid_tls_with_invalid_mtproto_falls_back_to_mask_backend() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -2052,6 +2080,8 @@ async fn client_handler_tls_bad_mtproto_is_forwarded_to_mask_backend() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -2175,6 +2205,8 @@ async fn alpn_mismatch_tls_probe_is_masked_through_client_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -2269,6 +2301,8 @@ async fn invalid_hmac_tls_probe_is_masked_through_client_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -2369,6 +2403,8 @@ async fn burst_invalid_tls_probes_are_masked_verbatim() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -3275,6 +3311,8 @@ async fn relay_connect_error_releases_user_and_ip_before_return() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -3837,6 +3875,8 @@ async fn untrusted_proxy_header_source_is_rejected() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -3908,6 +3948,8 @@ async fn empty_proxy_trusted_cidrs_rejects_proxy_header_by_default() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -4006,6 +4048,8 @@ async fn oversized_tls_record_is_masked_in_generic_stream_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -4110,6 +4154,8 @@ async fn oversized_tls_record_is_masked_in_client_handler_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -4228,6 +4274,8 @@ async fn tls_record_len_min_minus_1_is_rejected_in_generic_stream_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -4332,6 +4380,8 @@ async fn tls_record_len_min_minus_1_is_rejected_in_client_handler_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -4439,6 +4489,8 @@ async fn tls_record_len_16384_is_accepted_in_generic_stream_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -4541,6 +4593,8 @@ async fn tls_record_len_16384_is_accepted_in_client_handler_pipeline() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -30,6 +30,8 @@ fn make_test_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -32,6 +32,8 @@ fn make_test_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ fn make_test_upstream_manager(stats: Arc<Stats>) -> Arc<UpstreamManager> {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ fn build_harness(secret_hex: &str, mask_port: u16) -> PipelineHarness {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -1299,6 +1299,8 @@ async fn direct_relay_abort_midflight_releases_route_gauge() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1407,6 +1409,8 @@ async fn direct_relay_cutover_midflight_releases_route_gauge() {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1530,6 +1534,8 @@ async fn direct_relay_cutover_storm_multi_session_keeps_generic_errors_and_relea
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
@@ -1764,6 +1770,8 @@ async fn negative_direct_relay_dc_connection_refused_fails_fast() {
|
|||||||
bindtodevice: None,
|
bindtodevice: None,
|
||||||
},
|
},
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
100,
|
100,
|
||||||
@@ -1856,6 +1864,8 @@ async fn adversarial_direct_relay_cutover_integrity() {
|
|||||||
bindtodevice: None,
|
bindtodevice: None,
|
||||||
},
|
},
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
100,
|
100,
|
||||||
|
|||||||
@@ -59,6 +59,8 @@ fn new_client_harness() -> ClientHarness {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
1,
|
1,
|
||||||
|
|||||||
@@ -455,6 +455,87 @@ impl UpstreamManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
fn resolve_probe_dc_families(
|
||||||
|
upstream: &UpstreamConfig,
|
||||||
|
ipv4_available: bool,
|
||||||
|
ipv6_available: bool,
|
||||||
|
) -> (bool, bool) {
|
||||||
|
(
|
||||||
|
upstream.ipv4.unwrap_or(ipv4_available),
|
||||||
|
upstream.ipv6.unwrap_or(ipv6_available),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_runtime_dc_families(
|
||||||
|
upstream: &UpstreamConfig,
|
||||||
|
dc_preference: IpPreference,
|
||||||
|
) -> (bool, bool) {
|
||||||
|
let (auto_ipv4, auto_ipv6) = match dc_preference {
|
||||||
|
IpPreference::PreferV4 => (true, false),
|
||||||
|
IpPreference::PreferV6 => (false, true),
|
||||||
|
IpPreference::BothWork | IpPreference::Unknown | IpPreference::Unavailable => {
|
||||||
|
(true, true)
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
(
|
||||||
|
upstream.ipv4.unwrap_or(auto_ipv4),
|
||||||
|
upstream.ipv6.unwrap_or(auto_ipv6),
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn dc_table_addr(dc_idx: i16, ipv6: bool, port: u16) -> Option<SocketAddr> {
|
||||||
|
let arr_idx = UpstreamState::dc_array_idx(dc_idx)?;
|
||||||
|
let ip = if ipv6 {
|
||||||
|
TG_DATACENTERS_V6[arr_idx]
|
||||||
|
} else {
|
||||||
|
TG_DATACENTERS_V4[arr_idx]
|
||||||
|
};
|
||||||
|
Some(SocketAddr::new(ip, port))
|
||||||
|
}
|
||||||
|
|
||||||
|
fn resolve_runtime_dc_target(
|
||||||
|
target: SocketAddr,
|
||||||
|
dc_idx: Option<i16>,
|
||||||
|
upstream: &UpstreamConfig,
|
||||||
|
dc_preference: IpPreference,
|
||||||
|
) -> Result<SocketAddr> {
|
||||||
|
let (allow_ipv4, allow_ipv6) = Self::resolve_runtime_dc_families(upstream, dc_preference);
|
||||||
|
if (target.is_ipv4() && allow_ipv4) || (target.is_ipv6() && allow_ipv6) {
|
||||||
|
return Ok(target);
|
||||||
|
}
|
||||||
|
|
||||||
|
if !allow_ipv4 && !allow_ipv6 {
|
||||||
|
return Err(ProxyError::Config(format!(
|
||||||
|
"Upstream DC family policy blocks all families for target {target}"
|
||||||
|
)));
|
||||||
|
}
|
||||||
|
|
||||||
|
let Some(dc_idx) = dc_idx else {
|
||||||
|
return Err(ProxyError::Config(format!(
|
||||||
|
"Upstream DC family policy cannot remap target {target} without dc_idx"
|
||||||
|
)));
|
||||||
|
};
|
||||||
|
|
||||||
|
let remapped = if target.is_ipv4() {
|
||||||
|
if allow_ipv6 {
|
||||||
|
Self::dc_table_addr(dc_idx, true, target.port())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
}
|
||||||
|
} else if allow_ipv4 {
|
||||||
|
Self::dc_table_addr(dc_idx, false, target.port())
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
|
||||||
|
remapped.ok_or_else(|| {
|
||||||
|
ProxyError::Config(format!(
|
||||||
|
"Upstream DC family policy rejected target {target} (dc_idx={dc_idx})"
|
||||||
|
))
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
#[cfg(unix)]
|
#[cfg(unix)]
|
||||||
fn resolve_interface_addrs(name: &str, want_ipv6: bool) -> Vec<IpAddr> {
|
fn resolve_interface_addrs(name: &str, want_ipv6: bool) -> Vec<IpAddr> {
|
||||||
use nix::ifaddrs::getifaddrs;
|
use nix::ifaddrs::getifaddrs;
|
||||||
@@ -728,18 +809,24 @@ impl UpstreamManager {
|
|||||||
.await
|
.await
|
||||||
.ok_or_else(|| ProxyError::Config("No upstreams available".to_string()))?;
|
.ok_or_else(|| ProxyError::Config("No upstreams available".to_string()))?;
|
||||||
|
|
||||||
let mut upstream = {
|
let (mut upstream, bind_rr, dc_preference) = {
|
||||||
let guard = self.upstreams.read().await;
|
let guard = self.upstreams.read().await;
|
||||||
guard[idx].config.clone()
|
let state = &guard[idx];
|
||||||
|
let dc_preference = dc_idx
|
||||||
|
.and_then(UpstreamState::dc_array_idx)
|
||||||
|
.map(|dc_array_idx| state.dc_ip_pref[dc_array_idx])
|
||||||
|
.unwrap_or(IpPreference::Unknown);
|
||||||
|
(state.config.clone(), Some(state.bind_rr.clone()), dc_preference)
|
||||||
};
|
};
|
||||||
|
|
||||||
if let Some(s) = scope {
|
if let Some(s) = scope {
|
||||||
upstream.selected_scope = s.to_string();
|
upstream.selected_scope = s.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let bind_rr = {
|
let target = if dc_idx.is_some() {
|
||||||
let guard = self.upstreams.read().await;
|
Self::resolve_runtime_dc_target(target, dc_idx, &upstream, dc_preference)?
|
||||||
guard.get(idx).map(|u| u.bind_rr.clone())
|
} else {
|
||||||
|
target
|
||||||
};
|
};
|
||||||
|
|
||||||
let (stream, _) = self
|
let (stream, _) = self
|
||||||
@@ -760,9 +847,14 @@ impl UpstreamManager {
|
|||||||
.await
|
.await
|
||||||
.ok_or_else(|| ProxyError::Config("No upstreams available".to_string()))?;
|
.ok_or_else(|| ProxyError::Config("No upstreams available".to_string()))?;
|
||||||
|
|
||||||
let mut upstream = {
|
let (mut upstream, bind_rr, dc_preference) = {
|
||||||
let guard = self.upstreams.read().await;
|
let guard = self.upstreams.read().await;
|
||||||
guard[idx].config.clone()
|
let state = &guard[idx];
|
||||||
|
let dc_preference = dc_idx
|
||||||
|
.and_then(UpstreamState::dc_array_idx)
|
||||||
|
.map(|dc_array_idx| state.dc_ip_pref[dc_array_idx])
|
||||||
|
.unwrap_or(IpPreference::Unknown);
|
||||||
|
(state.config.clone(), Some(state.bind_rr.clone()), dc_preference)
|
||||||
};
|
};
|
||||||
|
|
||||||
// Set scope for configuration copy
|
// Set scope for configuration copy
|
||||||
@@ -770,9 +862,10 @@ impl UpstreamManager {
|
|||||||
upstream.selected_scope = s.to_string();
|
upstream.selected_scope = s.to_string();
|
||||||
}
|
}
|
||||||
|
|
||||||
let bind_rr = {
|
let target = if dc_idx.is_some() {
|
||||||
let guard = self.upstreams.read().await;
|
Self::resolve_runtime_dc_target(target, dc_idx, &upstream, dc_preference)?
|
||||||
guard.get(idx).map(|u| u.bind_rr.clone())
|
} else {
|
||||||
|
target
|
||||||
};
|
};
|
||||||
|
|
||||||
let (stream, egress) = self
|
let (stream, egress) = self
|
||||||
@@ -1212,6 +1305,8 @@ impl UpstreamManager {
|
|||||||
let mut all_results = Vec::new();
|
let mut all_results = Vec::new();
|
||||||
|
|
||||||
for (upstream_idx, upstream_config, bind_rr) in &upstreams {
|
for (upstream_idx, upstream_config, bind_rr) in &upstreams {
|
||||||
|
let (upstream_ipv4_enabled, upstream_ipv6_enabled) =
|
||||||
|
Self::resolve_probe_dc_families(upstream_config, ipv4_enabled, ipv6_enabled);
|
||||||
let upstream_name = match &upstream_config.upstream_type {
|
let upstream_name = match &upstream_config.upstream_type {
|
||||||
UpstreamType::Direct {
|
UpstreamType::Direct {
|
||||||
interface,
|
interface,
|
||||||
@@ -1244,7 +1339,7 @@ impl UpstreamManager {
|
|||||||
};
|
};
|
||||||
|
|
||||||
let mut v6_results = Vec::with_capacity(NUM_DCS);
|
let mut v6_results = Vec::with_capacity(NUM_DCS);
|
||||||
if ipv6_enabled {
|
if upstream_ipv6_enabled {
|
||||||
for dc_zero_idx in 0..NUM_DCS {
|
for dc_zero_idx in 0..NUM_DCS {
|
||||||
let dc_v6 = TG_DATACENTERS_V6[dc_zero_idx];
|
let dc_v6 = TG_DATACENTERS_V6[dc_zero_idx];
|
||||||
let addr_v6 = SocketAddr::new(dc_v6, TG_DATACENTER_PORT);
|
let addr_v6 = SocketAddr::new(dc_v6, TG_DATACENTER_PORT);
|
||||||
@@ -1295,13 +1390,17 @@ impl UpstreamManager {
|
|||||||
dc_idx: dc_zero_idx + 1,
|
dc_idx: dc_zero_idx + 1,
|
||||||
dc_addr: SocketAddr::new(dc_v6, TG_DATACENTER_PORT),
|
dc_addr: SocketAddr::new(dc_v6, TG_DATACENTER_PORT),
|
||||||
rtt_ms: None,
|
rtt_ms: None,
|
||||||
error: Some("ipv6 disabled".to_string()),
|
error: Some(if ipv6_enabled {
|
||||||
|
"ipv6 disabled by upstream policy".to_string()
|
||||||
|
} else {
|
||||||
|
"ipv6 disabled".to_string()
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
let mut v4_results = Vec::with_capacity(NUM_DCS);
|
let mut v4_results = Vec::with_capacity(NUM_DCS);
|
||||||
if ipv4_enabled {
|
if upstream_ipv4_enabled {
|
||||||
for dc_zero_idx in 0..NUM_DCS {
|
for dc_zero_idx in 0..NUM_DCS {
|
||||||
let dc_v4 = TG_DATACENTERS_V4[dc_zero_idx];
|
let dc_v4 = TG_DATACENTERS_V4[dc_zero_idx];
|
||||||
let addr_v4 = SocketAddr::new(dc_v4, TG_DATACENTER_PORT);
|
let addr_v4 = SocketAddr::new(dc_v4, TG_DATACENTER_PORT);
|
||||||
@@ -1352,7 +1451,11 @@ impl UpstreamManager {
|
|||||||
dc_idx: dc_zero_idx + 1,
|
dc_idx: dc_zero_idx + 1,
|
||||||
dc_addr: SocketAddr::new(dc_v4, TG_DATACENTER_PORT),
|
dc_addr: SocketAddr::new(dc_v4, TG_DATACENTER_PORT),
|
||||||
rtt_ms: None,
|
rtt_ms: None,
|
||||||
error: Some("ipv4 disabled".to_string()),
|
error: Some(if ipv4_enabled {
|
||||||
|
"ipv4 disabled by upstream policy".to_string()
|
||||||
|
} else {
|
||||||
|
"ipv4 disabled".to_string()
|
||||||
|
}),
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1372,7 +1475,9 @@ impl UpstreamManager {
|
|||||||
match addr_str.parse::<SocketAddr>() {
|
match addr_str.parse::<SocketAddr>() {
|
||||||
Ok(addr) => {
|
Ok(addr) => {
|
||||||
let is_v6 = addr.is_ipv6();
|
let is_v6 = addr.is_ipv6();
|
||||||
if (is_v6 && !ipv6_enabled) || (!is_v6 && !ipv4_enabled) {
|
if (is_v6 && !upstream_ipv6_enabled)
|
||||||
|
|| (!is_v6 && !upstream_ipv4_enabled)
|
||||||
|
{
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
let result = tokio::time::timeout(
|
let result = tokio::time::timeout(
|
||||||
@@ -1614,6 +1719,8 @@ impl UpstreamManager {
|
|||||||
let u = &guard[i];
|
let u = &guard[i];
|
||||||
(u.config.clone(), u.bind_rr.clone())
|
(u.config.clone(), u.bind_rr.clone())
|
||||||
};
|
};
|
||||||
|
let (upstream_ipv4_enabled, upstream_ipv6_enabled) =
|
||||||
|
Self::resolve_probe_dc_families(&config, ipv4_enabled, ipv6_enabled);
|
||||||
|
|
||||||
let mut healthy_groups = 0usize;
|
let mut healthy_groups = 0usize;
|
||||||
let mut latency_updates: Vec<(usize, f64)> = Vec::new();
|
let mut latency_updates: Vec<(usize, f64)> = Vec::new();
|
||||||
@@ -1629,14 +1736,31 @@ impl UpstreamManager {
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
let filtered_endpoints: Vec<SocketAddr> = endpoints
|
||||||
|
.iter()
|
||||||
|
.copied()
|
||||||
|
.filter(|endpoint| {
|
||||||
|
if endpoint.is_ipv4() {
|
||||||
|
upstream_ipv4_enabled
|
||||||
|
} else {
|
||||||
|
upstream_ipv6_enabled
|
||||||
|
}
|
||||||
|
})
|
||||||
|
.collect();
|
||||||
|
|
||||||
|
if filtered_endpoints.is_empty() {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
let rotation_key = (i, group.dc_idx, is_primary);
|
let rotation_key = (i, group.dc_idx, is_primary);
|
||||||
let start_idx =
|
let start_idx =
|
||||||
*endpoint_rotation.entry(rotation_key).or_insert(0) % endpoints.len();
|
*endpoint_rotation.entry(rotation_key).or_insert(0)
|
||||||
let mut next_idx = (start_idx + 1) % endpoints.len();
|
% filtered_endpoints.len();
|
||||||
|
let mut next_idx = (start_idx + 1) % filtered_endpoints.len();
|
||||||
|
|
||||||
for step in 0..endpoints.len() {
|
for step in 0..filtered_endpoints.len() {
|
||||||
let endpoint_idx = (start_idx + step) % endpoints.len();
|
let endpoint_idx = (start_idx + step) % filtered_endpoints.len();
|
||||||
let endpoint = endpoints[endpoint_idx];
|
let endpoint = filtered_endpoints[endpoint_idx];
|
||||||
|
|
||||||
let start = Instant::now();
|
let start = Instant::now();
|
||||||
let result = tokio::time::timeout(
|
let result = tokio::time::timeout(
|
||||||
@@ -1655,7 +1779,7 @@ impl UpstreamManager {
|
|||||||
Ok(Ok(_stream)) => {
|
Ok(Ok(_stream)) => {
|
||||||
group_ok = true;
|
group_ok = true;
|
||||||
group_rtt_ms = Some(start.elapsed().as_secs_f64() * 1000.0);
|
group_rtt_ms = Some(start.elapsed().as_secs_f64() * 1000.0);
|
||||||
next_idx = (endpoint_idx + 1) % endpoints.len();
|
next_idx = (endpoint_idx + 1) % filtered_endpoints.len();
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
Ok(Err(e)) => {
|
Ok(Err(e)) => {
|
||||||
@@ -1910,6 +2034,8 @@ mod tests {
|
|||||||
enabled: true,
|
enabled: true,
|
||||||
scopes: String::new(),
|
scopes: String::new(),
|
||||||
selected_scope: String::new(),
|
selected_scope: String::new(),
|
||||||
|
ipv4: None,
|
||||||
|
ipv6: None,
|
||||||
}],
|
}],
|
||||||
1,
|
1,
|
||||||
100,
|
100,
|
||||||
|
|||||||
Reference in New Issue
Block a user