Expand description
flowscope::app_proto — identify the application protocol
riding an encrypted transport from passively-observable
signals: ALPN, SNI, and the L4 port (issue #138).
TLS and QUIC hide the application, but the handshake still leaks enough to name it without decryption:
- ALPN is authoritative —
h2/h3/dot/doq/http/1.1are negotiated in the clear. - Port 853 is the reserved DNS-over-TLS / DNS-over-QUIC port (a fallback when ALPN is absent).
- SNI disambiguates DNS-over-HTTPS: DoH looks exactly like
HTTPS on the wire (ALPN
h2/h3, port 443), so the only passive tell is the resolver’s hostname (is_known_doh_host).
This closes the “encrypted DNS is invisible” and “no HTTP/2·3 visibility” NDR gaps without a full H2/H3 parser — detection, not decode.
§Example
use flowscope::app_proto::{classify, AppProtocol, Transport};
// h2 to a normal host is HTTP/2…
let p = classify(&["h2"], Some("example.com"), Transport::Tls, 443);
assert_eq!(p, AppProtocol::Http2);
// …but h2 to a known DoH resolver is DNS-over-HTTPS.
let p = classify(&["h2"], Some("cloudflare-dns.com"), Transport::Tls, 443);
assert_eq!(p, AppProtocol::DnsOverHttps);
assert!(p.is_encrypted_dns());
// Port 853 with no ALPN still resolves to DoT / DoQ.
let none: &[&str] = &[];
assert_eq!(
classify(none, None, Transport::Quic, 853),
AppProtocol::DnsOverQuic,
);Enums§
- AppProtocol
- Identified application protocol over an encrypted transport.
- Transport
- Transport an application protocol is riding.
Functions§
- classify
- Classify from the full signal set: the ALPN list (in offer / selection order — the first recognised token wins), the SNI, the transport, and the server L4 port.
- classify_
alpn_ token - Classify a single ALPN token. Returns
Nonefor tokens that don’t map to a recognised protocol (so a caller can keep scanning a list). - is_
known_ doh_ host trueifsniis a well-known public DNS-over-HTTPS resolver hostname. DoH is wire-identical to HTTPS, so the hostname is the only passive signal; this is a curated (non-exhaustive) list of the major public resolvers. A site-specific resolver won’t match — pair with a config allowlist when it matters.