use std::net::IpAddr;
use serde::{Deserialize, Serialize};
use crate::model::handshake::HandshakeInfo;
use crate::model::record::RecordEvent;
pub const RECORD_LOG_CAP: usize = 64;
#[derive(Clone, Copy, Debug, Eq, Hash, PartialEq, Serialize, Deserialize)]
pub struct ConnectionKey {
pub client_ip: IpAddr,
pub client_port: u16,
pub server_ip: IpAddr,
pub server_port: u16,
}
impl ConnectionKey {
#[must_use]
pub fn canonical(a_ip: IpAddr, a_port: u16, b_ip: IpAddr, b_port: u16) -> Self {
let a_is_server = is_tls_server_port(a_port);
let b_is_server = is_tls_server_port(b_port);
let (client_ip, client_port, server_ip, server_port) = match (a_is_server, b_is_server) {
(false, true) => (a_ip, a_port, b_ip, b_port),
(true, false) => (b_ip, b_port, a_ip, a_port),
_ => {
if (a_ip, a_port) <= (b_ip, b_port) {
(a_ip, a_port, b_ip, b_port)
} else {
(b_ip, b_port, a_ip, a_port)
}
}
};
Self {
client_ip,
client_port,
server_ip,
server_port,
}
}
}
impl std::fmt::Display for ConnectionKey {
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{}:{} \u{2192} {}:{}",
self.client_ip, self.client_port, self.server_ip, self.server_port
)
}
}
#[derive(Clone, Copy, Debug, Eq, PartialEq)]
pub enum Direction {
ClientToServer,
ServerToClient,
}
#[derive(Clone, Debug, Serialize)]
pub struct ConnectionState {
pub key: ConnectionKey,
pub first_seen_ms: u64,
pub last_seen_ms: u64,
pub handshake: HandshakeInfo,
pub records: Vec<RecordEvent>,
}
const TLS_WELL_KNOWN_PORTS: &[u16] = &[443, 8443];
fn is_tls_server_port(port: u16) -> bool {
TLS_WELL_KNOWN_PORTS.contains(&port)
}
#[cfg(test)]
mod tests {
use std::net::{IpAddr, Ipv4Addr, Ipv6Addr};
use super::*;
fn v4(a: u8, b: u8, c: u8, d: u8) -> IpAddr {
IpAddr::V4(Ipv4Addr::new(a, b, c, d))
}
#[test]
fn canonicalizes_by_well_known_port() {
let ephem = 54321u16;
let k1 = ConnectionKey::canonical(v4(10, 0, 0, 1), ephem, v4(1, 1, 1, 1), 443);
let k2 = ConnectionKey::canonical(v4(1, 1, 1, 1), 443, v4(10, 0, 0, 1), ephem);
assert_eq!(k1, k2);
assert_eq!(k1.client_ip, v4(10, 0, 0, 1));
assert_eq!(k1.server_port, 443);
}
#[test]
fn canonicalizes_by_lexicographic_when_ambiguous() {
let k1 = ConnectionKey::canonical(v4(10, 0, 0, 2), 5000, v4(10, 0, 0, 1), 6000);
let k2 = ConnectionKey::canonical(v4(10, 0, 0, 1), 6000, v4(10, 0, 0, 2), 5000);
assert_eq!(k1, k2);
assert_eq!(k1.client_ip, v4(10, 0, 0, 1));
assert_eq!(k1.client_port, 6000);
}
#[test]
fn works_across_ipv4_and_ipv6() {
let k = ConnectionKey::canonical(
IpAddr::V6(Ipv6Addr::LOCALHOST),
54000,
IpAddr::V4(Ipv4Addr::new(1, 1, 1, 1)),
443,
);
assert_eq!(k.server_port, 443);
}
}