WEB Carriers Safe-matrix Refactored

Co-Authored-By: brekotis <93345790+brekotis@users.noreply.github.com>
This commit is contained in:
Alexey
2026-08-26 20:33:33 +03:00
parent 8b2b88f30c
commit 34eeb2d856
49 changed files with 3406 additions and 936 deletions
+111
View File
@@ -0,0 +1,111 @@
use std::net::{IpAddr, SocketAddr};
use hyper::Request;
use hyper::header;
use ipnetwork::IpNetwork;
use crate::config::WebClientIpSource;
/// Parses one lowercase canonical Host value restricted to the public HTTPS port.
pub(in crate::web::http) fn canonical_request_host<B>(request: &Request<B>) -> Option<&str> {
let values = request.headers().get_all(header::HOST);
let mut values = values.iter();
let value = values.next()?.to_str().ok()?;
if values.next().is_some() {
return None;
}
let authority = value.parse::<hyper::http::uri::Authority>().ok()?;
if authority.port_u16().is_some_and(|port| port != 443) {
return None;
}
let host = value.strip_suffix(":443").unwrap_or(value);
if authority.host() != host || host.bytes().any(|byte| byte.is_ascii_uppercase()) {
return None;
}
Some(host)
}
/// Accepts one forwarded client address or the direct address of a trusted peer.
pub(in crate::web::http) fn client_ip<B>(
request: &Request<B>,
peer: SocketAddr,
source: WebClientIpSource,
trusted_proxy_cidrs: &[IpNetwork],
) -> Option<IpAddr> {
if !trusted_proxy_cidrs
.iter()
.any(|network| network.contains(peer.ip()))
{
return None;
}
let header_name = match source {
WebClientIpSource::XForwardedFor => "x-forwarded-for",
};
let values = request.headers().get_all(header_name);
let mut values = values.iter();
let Some(value) = values.next() else {
return Some(peer.ip());
};
let value = value.to_str().ok()?;
if values.next().is_some() || value.trim() != value || value.contains(',') {
return None;
}
if value.is_empty() {
return Some(peer.ip());
}
value.parse::<IpAddr>().ok()
}
/// Allows IP learning only for one explicit globally routable forwarded address.
pub(in crate::web::http) fn carrier_ip_learning_eligible<B>(
request: &Request<B>,
effective_ip: IpAddr,
) -> bool {
let mut values = request.headers().get_all("x-forwarded-for").iter();
let Some(value) = values.next().and_then(|value| value.to_str().ok()) else {
return false;
};
if values.next().is_some()
|| value.trim() != value
|| value.contains(',')
|| value.parse::<IpAddr>().ok() != Some(effective_ip)
{
return false;
}
globally_routable(effective_ip)
}
fn globally_routable(address: IpAddr) -> bool {
match address {
IpAddr::V4(address) => globally_routable_v4(address),
IpAddr::V6(address) => {
if let Some(address) = address.to_ipv4_mapped() {
return globally_routable_v4(address);
}
let segments = address.segments();
!address.is_unspecified()
&& !address.is_loopback()
&& segments[0] & 0xfe00 != 0xfc00
&& segments[0] & 0xffc0 != 0xfe80
&& segments[0] & 0xff00 != 0xff00
&& !(segments[0] == 0x2001 && segments[1] == 0x0db8)
}
}
}
fn globally_routable_v4(address: std::net::Ipv4Addr) -> bool {
let [a, b, c, _] = address.octets();
!(a == 0
|| a == 10
|| a == 127
|| (a == 100 && (64..=127).contains(&b))
|| (a == 169 && b == 254)
|| (a == 172 && (16..=31).contains(&b))
|| (a == 192 && b == 0 && c == 0)
|| (a == 192 && b == 0 && c == 2)
|| (a == 192 && b == 168)
|| (a == 198 && (b == 18 || b == 19))
|| (a == 198 && b == 51 && c == 100)
|| (a == 203 && b == 0 && c == 113)
|| a >= 224)
}