Skip to main content

tls_check/
tls_check.rs

1//! Check TLS/HTTP2 fingerprint of our browser vs stock Chrome.
2
3use clawser_browser::Browser;
4
5fn load_dotenv() {
6    for path in ["clawser-browser/.env", ".env"] {
7        if let Ok(content) = std::fs::read_to_string(path) {
8            for line in content.lines() {
9                let line = line.trim();
10                if line.is_empty() || line.starts_with('#') { continue; }
11                if let Some((k, v)) = line.split_once('=') {
12                    std::env::set_var(k.trim(), v.trim());
13                }
14            }
15            break;
16        }
17    }
18}
19
20#[tokio::main]
21async fn main() {
22    load_dotenv();
23    println!("=== TLS/HTTP2 Fingerprint Check ===\n");
24
25    let browser = Browser::builder()
26        .headful()
27        .random()
28        .build()
29        .await
30        .expect("browser launch failed");
31
32    let page = browser.new_page("about:blank").await.expect("page failed");
33
34    // 1. Check TLS fingerprint
35    println!("--- tls.peet.ws ---");
36    page.goto("https://tls.peet.ws/api/all").await.expect("nav failed");
37    tokio::time::sleep(std::time::Duration::from_secs(3)).await;
38
39    let body = page.js("document.body.innerText").await.unwrap_or_default();
40    if let Ok(v) = serde_json::from_str::<serde_json::Value>(&body) {
41        // TLS
42        if let Some(tls) = v.get("tls") {
43            println!("JA3 hash:    {}", tls.get("ja3_hash").and_then(|v| v.as_str()).unwrap_or("N/A"));
44            println!("JA3:         {}", tls.get("ja3").and_then(|v| v.as_str()).unwrap_or("N/A"));
45            println!("JA4:         {}", tls.get("ja4").and_then(|v| v.as_str()).unwrap_or("N/A"));
46            println!("TLS version: {}", tls.get("version").and_then(|v| v.as_str()).unwrap_or("N/A"));
47        }
48
49        // HTTP2
50        println!();
51        if let Some(h2) = v.get("http2") {
52            println!("HTTP2 fingerprint:   {}", h2.get("akamai_fingerprint").and_then(|v| v.as_str()).unwrap_or("N/A"));
53            println!("Pseudo-header order: {}", h2.get("sent_pseudo_header_fields_order").unwrap_or(&serde_json::Value::Null));
54            if let Some(settings) = h2.get("sent_settings") {
55                println!("SETTINGS:            {}", settings);
56            }
57        }
58
59        // HTTP version used
60        println!();
61        if let Some(ip) = v.get("ip") {
62            println!("Client IP:   {}", ip.as_str().unwrap_or("N/A"));
63        }
64        if let Some(http_version) = v.get("http_version") {
65            println!("HTTP version: {}", http_version.as_str().unwrap_or("N/A"));
66        }
67
68        // User-Agent
69        println!();
70        if let Some(h1) = v.get("http1") {
71            if let Some(headers) = h1.get("headers") {
72                if let Some(arr) = headers.as_array() {
73                    for h in arr {
74                        let name = h.get("name").and_then(|n| n.as_str()).unwrap_or("");
75                        let val = h.get("value").and_then(|n| n.as_str()).unwrap_or("");
76                        if name.to_lowercase() == "user-agent" {
77                            println!("User-Agent:  {}", val);
78                        }
79                    }
80                }
81            }
82        }
83    } else {
84        println!("Failed to parse response: {}", &body[..body.len().min(500)]);
85    }
86
87    // 2. Check known Chrome 134 reference values
88    println!("\n--- Expected Chrome 134 stock values ---");
89    println!("JA3 hash:    cd49876058a26ccf43e241ccc4e4b5d3  (Chrome 134 Windows)");
90    println!("JA4:         t13d1517h2_8daaf6152771_02713d6af8  (Chrome 134 Windows)");
91    println!("HTTP2 Akamai: 1:65536;2:0;4:6291456;6:262144|15663105|0|m,a,s,p");
92
93    println!("\nBrowser still open. Press Ctrl+C to exit.");
94    loop {
95        tokio::time::sleep(std::time::Duration::from_secs(60)).await;
96    }
97}