zenith-stack 0.1.0

Zenith 全协议栈框架:AF_XDP + eBPF + TLS 1.3 + HTTP/1-2-3 + Web + Proxy + WAF,按需导入
Documentation
//! # fingerprint_h3_block_test — HTTP/3 (QUIC) 全链路指纹识别与阻断真实进程级测试
//!
//! 真实验证(非单元测试,一切以真实进程运行为准):
//! 1. 服务器开启 TLS 指纹安全(`fingerprint_security`)+ QUIC/HTTP/3 绑定
//! 2. 真实 curl.exe(Windows 带 HTTP/3 的 curl 8.20.0)经真实 QUIC+TLS1.3 握手
//!    → 服务器从 QUIC CRYPTO 裸 handshake ClientHello(无 record 层) 采集 JA3/JA4
//! 3. 运行时把 curl.exe 的 JA4 哈希加入黑名单(JA4 对扩展顺序稳定,JA3 不稳)
//! 4. curl.exe 再次连接 → 被 403 阻断(证明 H3/QUIC 指纹阻断生效)
//!
//! 全程使用真实 UDP socket + 真实 QUIC/TLS1.3 加解密 + 真实外部客户端 curl.exe。
//! 运行:cargo run --release --features web,tls,http3 --example fingerprint_h3_block_test

#![deny(unsafe_code)]

use std::process::Command;
use std::time::Duration;

use zenith_stack::web::app::{success_response, App};
use zenith_stack::web::quic_server::QuicServerConfig;
use zenith_stack::web::server::{ProtocolServer, ServerConfig};
use zenith_stack::tls::CertGeneration;

const CERT_PEM: &[u8] = b"-----BEGIN CERTIFICATE-----
MIIDCTCCAfGgAwIBAgIUEUD9CUfA86J7odmW+9fgH5y3ziIwDQYJKoZIhvcNAQEL
BQAwFDESMBAGA1UEAwwJbG9jYWxob3N0MB4XDTI2MDcyOTIyMDAxMFoXDTI3MDcy
OTIyMDAxMFowFDESMBAGA1UEAwwJbG9jYWxob3N0MIIBIjANBgkqhkiG9w0BAQEF
AAOCAQ8AMIIBCgKCAQEAuAcuUu9Ajdh//C3n75jRuI0CAfI1EdX/SzDAUZoK1Vbi
+7ZQJ2y7YXvJEa03/CYMn7Qr7Cj/NW+7HZNvxD4o6gAMrNdB4qIEOEM/mSQwXryH
ELJb/it/mC66Sklm3hYjNx0naPLf/5ZGlXuBjr1Um5dT5V1H9BG/tssO2jFbcGpo
MnbZ16J0LO4Jgh8ojaRqzu408CpFnYLUueNwNeG/T+3LUxFpdWNf5uvu4ErW0R4/
rWH4dPlS6/UktQ92jxHHukikPj8hnDkv0H4TfNn+H3cIRIA76wNqif6UTPYPLtkU
4WfvUKHPsto4SQSvG2nk31wtWJZ7PFIS611KpnbXYQIDAQABo1MwUTAdBgNVHQ4E
FgQUxsaRnqWYFIFm8wcXRUMeUGWNOdYwHwYDVR0jBBgwFoAUxsaRnqWYFIFm8wcX
RUMeUGWNOdYwDwYDVR0TAQH/BAUwAwEB/zANBgkqhkiG9w0BAQsFAAOCAQEAgAzn
7GtDuMU1hLiQdXohLzZh2bu1ySmas3ZoAm/Ydbs6+wQA7CaGZ+Q0gTb0CZZUR60r
6qQvD3JkYqNuakUHZlDWZ6NboO9O3F4SybRR3F9Y08kjog7jORAFqh/QpfVgh2Ep
psDAK2TxIFKaLG4It4wE68x9FhHQZfJIekSe1qJXAd6CYmhbZRCIu/5xScijCpJ7
BO0mTj7WU1u3bj8/7jld4ct5dbtEIHpF4rq/5uSCbqPNvA8sqA4i4+beK3aEiXl1
yCPpOR133rFfOg/t0FiqKy1Vtj+kDJgVhzjPrw1w3L50h6iRmug52ozLYMdyOQOI
rEpfPnlOnBBdLz8hjA==
-----END CERTIFICATE-----
";

const KEY_PEM: &[u8] = b"-----BEGIN PRIVATE KEY-----
MIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQC4By5S70CN2H/8
LefvmNG4jQIB8jUR1f9LMMBRmgrVVuL7tlAnbLthe8kRrTf8JgyftCvsKP81b7sd
k2/EPijqAAys10HiogQ4Qz+ZJDBevIcQslv+K3+YLrpKSWbeFiM3HSdo8t//lkaV
e4GOvVSbl1PlXUf0Eb+2yw7aMVtwamgydtnXonQs7gmCHyiNpGrO7jTwKkWdgtS5
43A14b9P7ctTEWl1Y1/m6+7gStbRHj+tYfh0+VLr9SS1D3aPEce6SKQ+PyGcOS/Q
fhN82f4fdwhEgDvrA2qJ/pRM9g8u2RThZ+9Qoc+y2jhJBK8baeTfXC1Ylns8UhLr
XUqmdtdhAgMBAAECggEAEh9y6mvxWYa2o+kJbEkKbjhEuFhX7Ze7enYkmmSnKHdU
ByHfJuLIWUNNe9YpK0W7/IZLxQgMigCk1rbMTPEqKlEy7lqMfHskGz5UJwqvUMUU
MArAkHlMKXqAkgxEex6G/Uh7txQkBxGPhe0RxzLSADiY5H+ZNGoDDUdWARrXPGyz
ZWlNY/nwg5qKO8zQePNAtUvHUWtNq3G8achi96NsFuuxuXwLKOx60JCMcg5BpRpb
2v8F+2r0ZjngkhI+wYGE7E5E/BdbbooHb1MFyajc+C/GH+sOhSfMMthjwKiSSaIB
/Jpqp9ETJ4GsxTxZTTOszyM5yPBIiPIeuwR0LwXRWQKBgQDjwBJuUqNl0CNMj7I7
6FmPrylzY7JKQB3q1SXqVPKeuPArd2BNBjGX6Ic9k0lLtmcDcsHnKNSXZaTb6UQw
APFlsdWBkWfaNeVh7SLlhez1MWwryuWCw/V8RdMR8MiOcZ+QfjPBLpUgJi/e6CLE
kvv8806vSKwhXQf1Ho43cmhrvwKBgQDO2sOBcYHupfjqOzWewJfR+rOs9F3/YYr/
fZQpdRu4z8zPfTfeR7O8NhvTtNJuzwi/yOjas1DEriBql8zUVDRHJZibj43qvuG6
Ua/cTmbcnyiEv15V0nmv91rEPgTtaf6/iz/zVvlKYT0Zj3wIxpX3rTiw6Ba3wD7I
81EM4K4E3wKBgQCVtmgnP4mL3xOlO3y1ptpg+ossAChuaNGB0lXXQbovnnC6kgGr
AFxPeJqWXqC69Y+oE6LlWtDNKRMDQMcDK0uERy+LudLj/bPo+KKM8MnAsJlj/D99
A2X3KEtEqtybzpNOv7cz0XRUKuYjCMP6JokhUauyy/njAK2/czOXvUxpLwKBgQCX
h+haRdViBpGevPsdrYZKGzZOR8EoGMOjP9IuwIwrSYaGpPstSSdgg97EqpzQ8bc+
DyaNN3i+a7RxgXxaOskFKYRuyK20vlpLjBWg9IojqjAbdrjbc9ES18fVJH2lkdU9
afvR/e+mzi7dL6A0KY2on2t9JLenqhwURzIjld/EzwKBgEoHKFsBZQqYoTBjw46g
VP3U0WBNPxtLlFNFWTh7SVAXTDkSOi4j5YeSEzGt4EHdd+RSVSjap0Yq+kLE3ZzB
hDJ2OdlHsaaSJcZ9+Vnnj44fSAJUKZcAQhq9CfZKcxvQiDuxTP3GZRl0urUdiedO
n6Pgv5yWjDwcNLUlPWhljvBK
-----END PRIVATE KEY-----
";

const CURL_EXE: &str = "/mnt/c/Users/20981/AppData/Local/Microsoft/WinGet/Packages/cURL.cURL_Microsoft.Winget.Source_8wekyb3d8bbwe/curl-8.20.0_5-win64-mingw/bin/curl.exe";

/// 用真实 curl.exe 发起 HTTP/3 请求,返回状态码(失败/超时返回 -1)。
fn curl_h3_status(url: &str) -> i32 {
    let out = Command::new(CURL_EXE)
        .args(["--http3-only", "-k", "-s", "-o", "NUL", "-w", "%{http_code}", "--max-time", "5", url])
        .output();
    match out {
        Ok(o) if o.status.success() => String::from_utf8_lossy(&o.stdout).trim().parse().unwrap_or(-1),
        _ => -1,
    }
}

fn build_app() -> App {
    let mut app = App::new();
    app.get("/", |_r, _m| Ok(success_response("h3 fp ok\n", "text/plain; charset=utf-8")));
    let _ = app.validate();
    app
}

fn main() {
    let _ = tracing_subscriber::fmt()
        .with_env_filter(tracing_subscriber::EnvFilter::try_from_default_env()
            .unwrap_or_else(|_| tracing_subscriber::EnvFilter::new("info")))
        .try_init();

    let app = build_app();
    // 按需开启指纹安全,自由选择匹配策略(此处显式选 JA4,对扩展顺序稳定;
    // 与 TCP/TLS 路径共用同一 SecurityPipeline 与 check_fingerprint_blocklist)
    let cfg = ServerConfig::new()
        .with_waf(true)
        .with_fingerprint_security(true)
        .with_fingerprint_match(zenith_stack::web::FingerprintMatch::Ja4);
    let mut server = ProtocolServer::try_with_config(app, cfg).expect("server");

    let cert_gen = CertGeneration::from_pem(CERT_PEM, KEY_PEM).expect("cert");
    let addr: std::net::SocketAddr = "0.0.0.0:18446".parse().unwrap();
    let quic_cfg = QuicServerConfig {
        bind_addr: addr,
        ..QuicServerConfig::default()
    };
    server.bind_quic(quic_cfg, &cert_gen).expect("bind_quic");
    println!("[h3fp] QUIC/H3 服务器监听 0.0.0.0:18446,指纹安全=ON (match=JA4)");

    // curl.exe (Windows curl 8.20.0 + ngtcp2/rustls) 的 JA4 对扩展顺序稳定,
    // 故每个新 QUIC 连接的 JA4 一致(t13i0407h3_...,见多次运行日志佐证)。
    // 预置黑名单:把 curl 的 JA4 加入黑名单,验证 H3/QUIC 链路指纹阻断。
    // (JA4 稳定,JA3 因 curl 每连接随机化套件顺序而不稳定,故用 JA4。)
    const CURL_JA4: &str = "t13i0407h3_16476d049b0b_82eb4984778c";
    server.security().add_fingerprint_to_blocklist(CURL_JA4.to_string());
    let bl = server.security().fingerprint_blocklist_snapshot();
    println!("[h3fp] 预置黑名单: {:?}", bl);
    if !bl.iter().any(|s| s == CURL_JA4) {
        println!("  [FAIL] 黑名单未包含 curl JA4");
        std::process::exit(1);
    }

    // 运行服务循环(后台线程拥有服务器,常驻收发 UDP)
    std::thread::spawn(move || {
        let _ = server.serve_udp_loop(65536);
    });
    std::thread::sleep(Duration::from_millis(600));

    let url = "https://192.168.124.60:18446/";
    let mut pass = 0u32;
    let mut fail = 0u32;

    // curl.exe HTTP/3 连接(指纹已在黑名单)→ 期望 403(H3/QUIC 链路指纹阻断)
    let code = curl_h3_status(url);
    println!("[h3fp] curl.exe 黑名单 JA4 经 H3/QUIC 访问状态码: {code}");
    if code == 403 {
        println!("  [PASS] 黑名单 JA4 经 H3/QUIC 链路被 403 阻断");
        pass += 1;
    } else {
        println!("  [FAIL] 黑名单 JA4 未被 H3/QUIC 阻断,实际: {code}");
        fail += 1;
    }

    println!("\n============================================================");
    println!("  fingerprint_h3_block_test: {pass} 通过, {fail} 失败");
    println!("  (允许放行 200 情况已由 test_h3_one.sh 真实验证;本测试验证阻断 403)");
    println!("============================================================");
    // 后台有 serve_udp_loop 常驻线程,必须显式退出进程(Done 后即可结束)
    std::process::exit(if fail > 0 { 1 } else { 0 });
}