fix(server): treat a mapped wildcard bind as wildcard

This commit is contained in:
Nils Andreas Svee committed 2026-08-14 00:50:25 +07:00
1 parent 3c554a78d7
commit e154508cb1
3 files changed
+114 -3

No files matched your search

+57 -2
View File
@@ -1,6 +1,11 @@
//! Tokio UDP orchestration around [`ServerCore`](crate::ServerCore).
use std::{future::Future, io, net::SocketAddr, time::Duration};
use std::{
future::Future,
io,
net::{IpAddr, SocketAddr},
time::Duration,
};
use socket2::SockRef;
use thiserror::Error;
@@ -104,7 +109,7 @@ impl Server {
let addr = socket
.local_addr()
.map_err(|source| ServerRuntimeError::LocalAddr { source })?;
let select_reply_source = addr.ip().is_unspecified();
let select_reply_source = is_wildcard(addr);
if select_reply_source {
if !socket_io::SUPPORTED {
@@ -243,6 +248,30 @@ impl Server {
}
}
/// Whether a bound address names no particular local address, and so owes every
/// reply an explicitly selected source.
///
/// `0.0.0.0` and `[::]` are the obvious forms. The third is the IPv4-mapped
/// unspecified address, `[::ffff:0.0.0.0]`: Linux accepts it as an IPv4 wildcard
/// bind and reports it back verbatim from `getsockname`, so asking
/// `is_unspecified` alone would take a working wildcard listener for an explicit
/// one and answer its requests from whatever source the routing table picked.
/// (macOS normalizes that bind to `[::]` and never reaches the second test.)
///
/// A mapped address that names an actual host address — `[::ffff:127.0.0.1]` —
/// is explicit, and stays on the plain path.
fn is_wildcard(addr: SocketAddr) -> bool {
match addr.ip() {
IpAddr::V4(address) => address.is_unspecified(),
IpAddr::V6(address) => {
address.is_unspecified()
|| address
.to_ipv4_mapped()
.is_some_and(|mapped| mapped.is_unspecified())
}
}
}
/// Whether a reply may still be sent after its traffic class could not be
/// applied.
///
@@ -479,6 +508,32 @@ mod tests {
ServerConfig::default().with_min_send_interval(Duration::ZERO)
}
/// Which bound addresses owe their replies a selected source.
///
/// The IPv4-mapped unspecified address is the one that is not obvious, and
/// it is not hypothetical: Linux accepts `[::ffff:0.0.0.0]` as an IPv4
/// wildcard bind and reports it back unchanged, so missing it would leave a
/// working wildcard listener on the plain send path. A mapped address
/// naming a real host address is explicit and must stay there.
#[test]
fn a_bound_address_naming_no_particular_local_address_is_a_wildcard() {
for (addr, wildcard) in [
("0.0.0.0:2112", true),
("[::]:2112", true),
("[::ffff:0.0.0.0]:2112", true),
("127.0.0.1:2112", false),
("192.0.2.10:2112", false),
("[::1]:2112", false),
("[::ffff:127.0.0.1]:2112", false),
] {
assert_eq!(
is_wildcard(addr.parse().unwrap()),
wildcard,
"{addr} classified wrongly"
);
}
}
/// The rule that decides what an unappliable traffic class means, which no
/// normal interface can reach: it needs a host that refuses the socket
/// option, and the hosts these tests run on do not.