Skip to main content

wafrift_proxy/
lib.rs

1//! Library surface for `wafrift-proxy`.
2//!
3//! The binary entry point lives in `main.rs`; this lib module exposes
4//! the building blocks downstream consumers (the bench harness,
5//! integration tests, third-party Rust code that wants the
6//! evasion proxy as a library) need.
7
8pub mod hop_by_hop;
9pub mod mitm;
10pub mod rate_limit;
11pub mod scope;
12pub mod tui;
13pub mod upstream;
14pub mod upstream_policy;
15
16/// Extract the host from a Host header, handling IPv6 bracket notation and bare IPv6 literals.
17///
18/// Returns the host component only (strips `:port`). For malformed input
19/// (e.g. unclosed brackets) returns an empty string so callers can fall
20/// back to a safe default rather than routing to garbage.
21#[allow(clippy::collapsible_if)]
22pub fn extract_host_from_header(s: &str) -> String {
23    let s = s.trim();
24    if s.is_empty() {
25        return String::new();
26    }
27    if s.starts_with('[') {
28        if let Some(end_idx) = s.find(']') {
29            if end_idx > 1 {
30                return s[1..end_idx].to_string();
31            }
32        }
33        // Malformed bracket notation — don't attempt to route it.
34        return String::new();
35    }
36    // Bare IPv6 (no brackets). Avoid `split(':')` which would truncate at the first segment.
37    if s.contains(':') {
38        if let Ok(std::net::IpAddr::V6(ip)) = s.parse() {
39            return ip.to_string();
40        }
41    }
42    s.split(':').next().unwrap_or(s).to_string()
43}
44
45#[cfg(test)]
46mod tests {
47    use super::*;
48
49    #[test]
50    fn extract_bare_ipv6() {
51        assert_eq!(extract_host_from_header("2001:db8::1"), "2001:db8::1");
52    }
53
54    #[test]
55    fn extract_bracketed_v6_with_port() {
56        assert_eq!(extract_host_from_header("[::1]:443"), "::1");
57    }
58
59    #[test]
60    fn extract_hostname_with_port() {
61        assert_eq!(extract_host_from_header("example.com:443"), "example.com");
62    }
63
64    #[test]
65    fn extract_bare_ipv4() {
66        assert_eq!(extract_host_from_header("192.168.1.1"), "192.168.1.1");
67    }
68
69    #[test]
70    fn extract_ipv4_with_port() {
71        assert_eq!(extract_host_from_header("192.168.1.1:8080"), "192.168.1.1");
72    }
73
74    #[test]
75    fn extract_bracketed_v6_no_port() {
76        assert_eq!(extract_host_from_header("[::1]"), "::1");
77        assert_eq!(extract_host_from_header("[2001:db8::1]"), "2001:db8::1");
78    }
79
80    #[test]
81    fn extract_malformed_bracket_returns_empty() {
82        // Unclosed bracket — must not crash or return garbage like "[".
83        assert_eq!(extract_host_from_header("[::1"), "");
84        assert_eq!(extract_host_from_header("["), "");
85    }
86
87    #[test]
88    fn extract_empty_returns_empty() {
89        assert_eq!(extract_host_from_header(""), "");
90        assert_eq!(extract_host_from_header("   "), "");
91    }
92}