SOCKS: classify mapped ME destinations as IPv4

This commit is contained in:
Alexey
2026-10-20 00:26:10 +03:00
parent 0416de3c27
commit 093932dbac
2 changed files with 75 additions and 6 deletions
+5 -5
View File
@@ -290,11 +290,11 @@ impl MePool {
None None
}; };
// BND describes the SOCKS egress to ME, whose family can differ from // BND describes the SOCKS egress to ME, whose family can differ from
// the local TCP connection to the SOCKS server. // the local TCP connection to the SOCKS server. Match KDF classification
let socks_family = if peer_addr.is_ipv4() { // so IPv4-mapped destinations require an IPv4 egress tuple.
IpFamily::V4 let socks_family = match extract_ip_material(peer_addr) {
} else { IpMaterial::V4(_) => IpFamily::V4,
IpFamily::V6 IpMaterial::V6(_) => IpFamily::V6,
}; };
let socks_bound_addr = Self::select_socks_bound_addr(socks_family, upstream_egress); let socks_bound_addr = Self::select_socks_bound_addr(socks_family, upstream_egress);
let bnd_addr_status = if !is_socks_route { let bnd_addr_status = if !is_socks_route {
@@ -149,7 +149,8 @@ async fn accept_handshake(
.write_all(&build_rpc_frame(-2, &nonce, RpcChecksumMode::Crc32)) .write_all(&build_rpc_frame(-2, &nonce, RpcChecksumMode::Crc32))
.await .await
.unwrap(); .unwrap();
let split = |ip: IpAddr| match ip { // The ME peer derives mapped destinations as IPv4 independently of client selection.
let split = |ip: IpAddr| match ip.to_canonical() {
IpAddr::V4(ip) => { IpAddr::V4(ip) => {
let mut octets = ip.octets(); let mut octets = ip.octets();
octets.reverse(); octets.reverse();
@@ -259,3 +260,71 @@ async fn strict_socks_rejects_wrong_me_family_and_incomplete_bnd() {
check_socks_handshake(transport, target, bound, MeSocksKdfPolicy::Strict, false).await; check_socks_handshake(transport, target, bound, MeSocksKdfPolicy::Strict, false).await;
} }
} }
#[tokio::test]
async fn b12_ipv4_mapped_me_strict_preserves_bnd_tuple() {
for transport in ["127.0.0.1:0", "[::1]:0"] {
check_socks_handshake(
transport,
"[::ffff:149.154.167.50]:8888",
V4_BND,
MeSocksKdfPolicy::Strict,
true,
)
.await;
}
}
#[tokio::test]
async fn b12_ipv4_mapped_me_compat_preserves_bnd_tuple() {
for transport in ["127.0.0.1:0", "[::1]:0"] {
check_socks_handshake(
transport,
"[::ffff:149.154.167.50]:8888",
V4_BND,
MeSocksKdfPolicy::Compat,
true,
)
.await;
}
}
#[tokio::test]
async fn b12_ipv4_mapped_me_strict_retains_bnd_validation() {
for transport in ["127.0.0.1:0", "[::1]:0"] {
for bound in [
V6_BND,
"10.0.0.1:45678",
"0.0.0.0:45678",
"91.108.56.1:0",
"[::ffff:91.108.56.1]:45678",
] {
check_socks_handshake(
transport,
"[::ffff:149.154.167.50]:8888",
bound,
MeSocksKdfPolicy::Strict,
false,
)
.await;
}
}
}
#[tokio::test]
async fn b12_ipv4_compatible_destination_remains_ipv6() {
// IPv4-compatible IPv6 is not IPv4-mapped and must keep native IPv6 KDF material.
for transport in ["127.0.0.1:0", "[::1]:0"] {
for policy in [MeSocksKdfPolicy::Strict, MeSocksKdfPolicy::Compat] {
check_socks_handshake(transport, "[::149.154.167.50]:8888", V6_BND, policy, true).await;
}
check_socks_handshake(
transport,
"[::149.154.167.50]:8888",
V4_BND,
MeSocksKdfPolicy::Strict,
false,
)
.await;
}
}