Check whether a host string refers to a loopback address. Covers all representations that resolve to loopback: - `127.0.0.0/8` (the entire IPv4 loopback range, not just `127.0.0.1`) - `localhost` - `::1` and long-form IPv6 loopback (`0:0:0:0:0:0:0:1`) - `::ffff:127.x.x.x` (IPv4-mapped IPv6 loopback) - Bracketed forms like `[::1]`
(host: &str)
| 1239 | /// - `::ffff:127.x.x.x` (IPv4-mapped IPv6 loopback) |
| 1240 | /// - Bracketed forms like `[::1]` |
| 1241 | fn is_loopback_host(host: &str) -> bool { |
| 1242 | // Strip brackets for IPv6 addresses like [::1] |
| 1243 | let host = host |
| 1244 | .strip_prefix('[') |
| 1245 | .and_then(|h| h.strip_suffix(']')) |
| 1246 | .unwrap_or(host); |
| 1247 | |
| 1248 | if host.eq_ignore_ascii_case("localhost") { |
| 1249 | return true; |
| 1250 | } |
| 1251 | |
| 1252 | match host.parse::<std::net::IpAddr>() { |
| 1253 | Ok(std::net::IpAddr::V4(v4)) => v4.is_loopback(), // covers all 127.x.x.x |
| 1254 | Ok(std::net::IpAddr::V6(v6)) => { |
| 1255 | if v6.is_loopback() { |
| 1256 | return true; // covers ::1 and long form |
| 1257 | } |
| 1258 | // Check IPv4-mapped IPv6 addresses like ::ffff:127.0.0.1 |
| 1259 | if let Some(v4) = v6.to_ipv4_mapped() { |
| 1260 | return v4.is_loopback(); |
| 1261 | } |
| 1262 | false |
| 1263 | } |
| 1264 | Err(_) => false, |
| 1265 | } |
| 1266 | } |
| 1267 | |
| 1268 | #[cfg(test)] |
| 1269 | #[allow( |
no outgoing calls
no test coverage detected