Skip to content

Commit 4510b0d

Browse files
authored
fix(net): catch IPv4-mapped blocked ranges in is_always_blocked_net (#1032)
* fix(net): catch IPv4-mapped blocked ranges in is_always_blocked_net The IPv6 branch only checked whether the network address itself mapped to a blocked IPv4 address. A broader prefix like ::ffff:168.0.0.0/103 has a public network address but spans ::ffff:169.254.0.0, so the old code accepted it at policy load time while is_always_blocked_ip silently rejected every connection at runtime. Add three containment checks for the IPv4-mapped loopback, link-local, and unspecified representatives. The existing network-address check is kept because it handles single-host entries (/128) whose network address is already in a blocked range. Five new tests cover: single-host loopback and link-local mapped addresses, broad prefixes that span each blocked range without starting there, and a public single-host address that must not be blocked. * fix(net): address clippy warnings in is_always_blocked_net Use Ipv4Addr::LOCALHOST instead of Ipv4Addr::new(127, 0, 0, 1) and collapse the nested if let / if into is_some_and.
1 parent 20ffc72 commit 4510b0d

1 file changed

Lines changed: 53 additions & 5 deletions

File tree

  • crates/openshell-core/src

crates/openshell-core/src/net.rs

Lines changed: 53 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -97,11 +97,23 @@ pub fn is_always_blocked_net(net: ipnet::IpNet) -> bool {
9797
return true;
9898
}
9999

100-
// Check IPv4-mapped IPv6 (::ffff:127.0.0.1, ::ffff:169.254.x.x, etc.)
101-
if let Some(v4) = network.to_ipv4_mapped() {
102-
if v4.is_loopback() || v4.is_link_local() || v4.is_unspecified() {
103-
return true;
104-
}
100+
// Check IPv4-mapped IPv6 addresses. The network-address check covers
101+
// ranges whose first address is already in a blocked range
102+
// (e.g. ::ffff:127.0.0.1/128, ::ffff:169.254.0.1/128). The
103+
// containment checks below catch broader prefixes that only reach
104+
// into a blocked range further in — e.g. ::ffff:168.0.0.0/103
105+
// has a public network address but spans ::ffff:169.254.0.0.
106+
if network
107+
.to_ipv4_mapped()
108+
.is_some_and(|v4| v4.is_loopback() || v4.is_link_local() || v4.is_unspecified())
109+
{
110+
return true;
111+
}
112+
if v6net.contains(&Ipv4Addr::LOCALHOST.to_ipv6_mapped())
113+
|| v6net.contains(&Ipv4Addr::new(169, 254, 0, 0).to_ipv6_mapped())
114+
|| v6net.contains(&Ipv4Addr::UNSPECIFIED.to_ipv6_mapped())
115+
{
116+
return true;
105117
}
106118

107119
false
@@ -332,6 +344,42 @@ mod tests {
332344
assert!(is_always_blocked_net(net));
333345
}
334346

347+
#[test]
348+
fn test_always_blocked_net_v6_ipv4_mapped_loopback_single() {
349+
let net: ipnet::IpNet = "::ffff:127.0.0.1/128".parse().unwrap();
350+
assert!(is_always_blocked_net(net));
351+
}
352+
353+
#[test]
354+
fn test_always_blocked_net_v6_ipv4_mapped_link_local_single() {
355+
let net: ipnet::IpNet = "::ffff:169.254.0.1/128".parse().unwrap();
356+
assert!(is_always_blocked_net(net));
357+
}
358+
359+
#[test]
360+
fn test_always_blocked_net_v6_ipv4_mapped_broad_spans_link_local() {
361+
// ::ffff:168.0.0.0/103 has a public network address (168.0.0.0) but
362+
// the range covers 168.0.0.0–169.255.255.255, which includes the
363+
// link-local block 169.254.0.0/16.
364+
let net: ipnet::IpNet = "::ffff:168.0.0.0/103".parse().unwrap();
365+
assert!(is_always_blocked_net(net));
366+
}
367+
368+
#[test]
369+
fn test_always_blocked_net_v6_ipv4_mapped_broad_spans_loopback() {
370+
// ::ffff:64.0.0.0/98 has a public network address (64.0.0.0) but the
371+
// range covers 64.0.0.0–127.255.255.255, which includes loopback.
372+
let net: ipnet::IpNet = "::ffff:64.0.0.0/98".parse().unwrap();
373+
assert!(is_always_blocked_net(net));
374+
}
375+
376+
#[test]
377+
fn test_always_blocked_net_v6_ipv4_mapped_allows_public() {
378+
// ::ffff:8.8.8.8/128 is a public address — should not be blocked.
379+
let net: ipnet::IpNet = "::ffff:8.8.8.8/128".parse().unwrap();
380+
assert!(!is_always_blocked_net(net));
381+
}
382+
335383
// -- is_internal_ip --
336384

337385
#[test]

0 commit comments

Comments
 (0)