fix(reflectors): reject conflicting resolved endpoints
Build binaries / Build (aarch64-musl) (push) Successful in 2m16s
Build binaries / Build (arm-musleabi) (push) Successful in 2m48s
Build binaries / Build (arm-musleabihf) (push) Successful in 2m6s
Build binaries / Build (armv7-musleabi) (push) Successful in 2m8s
Build binaries / Build (armv7-musleabihf) (push) Successful in 2m29s
Build binaries / Build (i686-musl) (push) Successful in 2m34s
Build binaries / Build (mips-musl) (push) Successful in 1m57s
Build binaries / Build (mips64-muslabi64) (push) Successful in 1m58s
Build binaries / Build (mips64el-muslabi64) (push) Successful in 2m0s
Build binaries / Build (mipsel-musl) (push) Successful in 1m53s
Build binaries / Build (riscv64gc-musl) (push) Successful in 2m10s
Build binaries / Build (x86_64-musl) (push) Successful in 2m41s

Deduplicate compatible definitions and reject conflicting metadata or authentication within each list and across bootstrap and pool entries. Treat hostname aliases, protocol ordering, country-code case, and omitted default ports consistently.
This commit is contained in:
Nils Andreas Svee committed 2026-10-13 21:24:05 +07:00
1 parent a073a9d049
commit f8691c5189
1 file changed
+36 -6
+36 -6
View File
@@ -185,12 +185,44 @@ fn resolve_reflectors(
if protocol != ReflectorProtocol::Irtt && endpoint.is_ipv6() { if protocol != ReflectorProtocol::Irtt && endpoint.is_ipv6() {
continue; continue;
} }
resolved.insert(endpoint, definition.clone()); insert_reflector(&mut resolved, endpoint, definition.clone())?;
} }
} }
Ok(resolved) Ok(resolved)
} }
fn insert_reflector(
reflectors: &mut HashMap<SocketAddr, ReflectorDefinition>,
endpoint: SocketAddr,
definition: ReflectorDefinition,
) -> anyhow::Result<()> {
if let Some(existing) = reflectors.get(&endpoint) {
let same_country = match (&existing.country, &definition.country) {
(Some(a), Some(b)) => a.eq_ignore_ascii_case(b),
(None, None) => true,
_ => false,
};
let same_protocols = existing
.protocols
.iter()
.all(|p| definition.protocols.contains(p))
&& definition
.protocols
.iter()
.all(|p| existing.protocols.contains(p));
if !same_country
|| !same_protocols
|| existing.irtt_port.unwrap_or(2112) != definition.irtt_port.unwrap_or(2112)
|| existing.irtt_hmac != definition.irtt_hmac
{
anyhow::bail!("conflicting reflector definitions for resolved endpoint {endpoint}");
}
} else {
reflectors.insert(endpoint, definition);
}
Ok(())
}
fn setup_reflectors(settings: &Settings) -> anyhow::Result<ReflectorSetup> { fn setup_reflectors(settings: &Settings) -> anyhow::Result<ReflectorSetup> {
let protocol = settings.advanced_settings.measurement_type; let protocol = settings.advanced_settings.measurement_type;
let configured = resolve_reflectors( let configured = resolve_reflectors(
@@ -201,11 +233,9 @@ fn setup_reflectors(settings: &Settings) -> anyhow::Result<ReflectorSetup> {
)?; )?;
let bootstrap = resolve_reflectors(&settings.reflectors.bootstrap, protocol, &[], false)?; let bootstrap = resolve_reflectors(&settings.reflectors.bootstrap, protocol, &[], false)?;
let mut reflectors = bootstrap.clone(); let mut reflectors = bootstrap.clone();
reflectors.extend( for (endpoint, definition) in &configured {
configured insert_reflector(&mut reflectors, *endpoint, definition.clone())?;
.iter() }
.map(|(endpoint, definition)| (*endpoint, definition.clone())),
);
if reflectors.is_empty() { if reflectors.is_empty() {
anyhow::bail!("no usable reflectors for measurement protocol {protocol}"); anyhow::bail!("no usable reflectors for measurement protocol {protocol}");
} }