Skip to main content

retina_core/subscription/
tls_handshake.rs

1//! TLS handshakes.
2//!
3//! This is a session-level subscription that delivers parsed TLS handshakes and associated
4//! connection metadata. Only the first TLS handshake in a connection is parsed; subsequent
5//! encrypted messages are dropped.
6//!
7//! ## Example
8//! Prints the chosen cipher suite of TLS handshakes with `calendar.google.com`.
9//! ```
10//! #[filter("tls.sni = 'calendar.google.com'")]
11//! fn main() {
12//!     let config = default_config();
13//!     let cb = |tls: TlsHandshake| {
14//!         println!("{}", tls.data.cipher());
15//!     };
16//!     let mut runtime = Runtime::new(config, filter, cb).unwrap();
17//!     runtime.run();
18//! }
19
20use crate::conntrack::conn_id::FiveTuple;
21use crate::conntrack::pdu::{L4Context, L4Pdu};
22use crate::conntrack::ConnTracker;
23use crate::filter::FilterResult;
24use crate::memory::mbuf::Mbuf;
25use crate::protocols::stream::tls::{parser::TlsParser, Tls};
26use crate::protocols::stream::{ConnParser, Session, SessionData};
27use crate::subscription::{Level, Subscribable, Subscription, Trackable};
28
29use serde::Serialize;
30
31use std::net::SocketAddr;
32
33/// A parsed TLS handshake and connection metadata.
34#[derive(Debug, Serialize)]
35pub struct TlsHandshake {
36    /// Connection 5-tuple.
37    pub five_tuple: FiveTuple,
38    /// Parsed TLS handshake data.
39    pub data: Tls,
40}
41
42impl TlsHandshake {
43    /// Returns the client's socket address.
44    #[inline]
45    pub fn client(&self) -> SocketAddr {
46        self.five_tuple.orig
47    }
48
49    /// Returns the server's socket address.
50    #[inline]
51    pub fn server(&self) -> SocketAddr {
52        self.five_tuple.resp
53    }
54}
55
56impl Subscribable for TlsHandshake {
57    type Tracked = TrackedTls;
58
59    fn level() -> Level {
60        Level::Session
61    }
62
63    fn parsers() -> Vec<ConnParser> {
64        vec![ConnParser::Tls(TlsParser::default())]
65    }
66
67    fn process_packet(
68        mbuf: Mbuf,
69        subscription: &Subscription<Self>,
70        conn_tracker: &mut ConnTracker<Self::Tracked>,
71    ) {
72        match subscription.filter_packet(&mbuf) {
73            FilterResult::MatchTerminal(idx) | FilterResult::MatchNonTerminal(idx) => {
74                if let Ok(ctxt) = L4Context::new(&mbuf, idx) {
75                    conn_tracker.process(mbuf, ctxt, subscription);
76                }
77            }
78            FilterResult::NoMatch => drop(mbuf),
79        }
80    }
81}
82
83/// Represents TLS connection's state during the connection lifetime.
84///
85/// ## Remarks
86/// Retina uses an internal parser to track and filter application-layer protocols, and transfers
87/// session ownership to the subscription to invoke the callback on a filter match. This is an
88/// optimization to avoid double-parsing: once for the filter and once for the subscription data.
89/// This is why most `Trackable` trait methods for this type are unimplemented.
90///
91/// ## Note
92/// Internal connection state is an associated type of a `pub` trait, and therefore must also be
93/// public. Documentation is hidden by default to avoid confusing users.
94#[doc(hidden)]
95pub struct TrackedTls {
96    five_tuple: FiveTuple,
97}
98
99impl Trackable for TrackedTls {
100    type Subscribed = TlsHandshake;
101
102    fn new(five_tuple: FiveTuple) -> Self {
103        TrackedTls { five_tuple }
104    }
105
106    fn pre_match(&mut self, _pdu: L4Pdu, _session_id: Option<usize>) {}
107
108    fn on_match(&mut self, session: Session, subscription: &Subscription<Self::Subscribed>) {
109        if let SessionData::Tls(tls) = session.data {
110            subscription.invoke(TlsHandshake {
111                five_tuple: self.five_tuple,
112                data: *tls,
113            });
114        }
115    }
116
117    fn post_match(&mut self, _pdu: L4Pdu, _subscription: &Subscription<Self::Subscribed>) {}
118
119    fn on_terminate(&mut self, _subscription: &Subscription<Self::Subscribed>) {}
120}