#tls #detect #sniff #protocols #http

guess

Classify a TCP stream's first bytes as TLS / HTTP/2 / HTTP/1 / unknown

3 unstable releases

Uses new Rust 2024

0.2.1 May 9, 2026
0.2.0 Feb 8, 2026
0.1.5 Feb 7, 2026

#4 in #sniff


Used in 6 crates (2 directly)

MIT license

19KB
339 lines

Guess

Wire-protocol classifier for TCP / TLS streams. Feed the first bytes of a freshly accepted connection and get back one.

Features

  • TLS ClientHello (parsed via rustls::server::Acceptor; SNI and ALPN extracted)
  • HTTP/2 connection preface (RFC 7540 §3.5)
  • HTTP/1 (request line with HTTP/1.0 or HTTP/1.1 version marker)
  • Unknown (every detector ruled itself out — further reads will not change the outcome)

The cascade is three-state: when some detector wants more bytes before committing, classify returns detected = None so the caller knows to read more (up to MAX_PEEK_BYTES) and call again.

Example

use guess::{DetectedProtocol, MAX_PEEK_BYTES, classify};

# fn handle(stream_bytes: &[u8]) {
let result = classify(stream_bytes);
match result.detected {
    Some(DetectedProtocol::TlsClientHello) => {
        let tls = result.tls.unwrap();
        println!("SNI: {:?}, ALPN: {:?}", tls.sni, tls.alpn);
    }
    Some(DetectedProtocol::Http2Preface) => println!("h2"),
    Some(DetectedProtocol::Http1) => println!("h1"),
    Some(DetectedProtocol::Unknown) => println!("opaque L4"),
    None => println!("read more bytes (up to {MAX_PEEK_BYTES})"),
    _ => {}
}
# }

Features

  • classify (default) — pulls in rustls (for the TLS parse) and memchr (for the HTTP/1 scan), and exposes classify. Disable to get only the result types — useful when a downstream crate wants to describe a peek without performing one:
    guess = { version = "0.2", default-features = false }
    

License

Released under the MIT License © 2026 Canmi

Dependencies

~0.1–11MB
~87K SLoC