Skip to main content

soth_mitm/
types.rs

1use std::net::IpAddr;
2use std::net::{SocketAddrV4, SocketAddrV6};
3use std::path::PathBuf;
4use std::sync::Arc;
5use std::time::SystemTime;
6
7use bytes::Bytes;
8use http::HeaderMap;
9use uuid::Uuid;
10
11/// Newtype wrapping a `u64` flow identifier for type-safe flow tracking.
12///
13/// # Examples
14///
15/// ```
16/// use soth_mitm::FlowId;
17///
18/// let id = FlowId(42);
19/// assert_eq!(id.as_u64(), 42);
20/// assert_eq!(format!("{id}"), "42");
21/// ```
22#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash, PartialOrd, Ord, serde::Serialize)]
23pub struct FlowId(pub u64);
24
25impl FlowId {
26    /// Returns the inner `u64` value.
27    pub fn as_u64(self) -> u64 {
28        self.0
29    }
30}
31
32impl std::fmt::Display for FlowId {
33    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
34        write!(f, "{}", self.0)
35    }
36}
37
38/// TLS protocol version.
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum TlsVersion {
41    Tls12,
42    Tls13,
43}
44
45impl TlsVersion {
46    pub fn as_str(&self) -> &'static str {
47        match self {
48            Self::Tls12 => "tls1.2",
49            Self::Tls13 => "tls1.3",
50        }
51    }
52}
53
54impl std::fmt::Display for TlsVersion {
55    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
56        f.write_str(self.as_str())
57    }
58}
59
60/// An intercepted HTTP request passed to the handler.
61#[derive(Debug, Clone, PartialEq, Eq)]
62pub struct RawRequest {
63    pub method: String,
64    pub path: String,
65    pub headers: HeaderMap,
66    pub body: Bytes,
67    pub connection_meta: Arc<ConnectionMeta>,
68}
69
70/// An intercepted HTTP response passed to the handler.
71#[derive(Debug, Clone, PartialEq, Eq)]
72pub struct RawResponse {
73    pub status: u16,
74    pub headers: HeaderMap,
75    pub body: Bytes,
76    pub connection_meta: Arc<ConnectionMeta>,
77}
78
79/// Discriminant for streaming frame types delivered via [`StreamChunk`].
80#[derive(Debug, Clone, Copy, PartialEq, Eq)]
81pub enum FrameKind {
82    SseData,
83    NdjsonLine,
84    GrpcMessage,
85    WebSocketText,
86    WebSocketBinary,
87    WebSocketClose,
88}
89
90impl FrameKind {
91    pub fn as_str(&self) -> &'static str {
92        match self {
93            Self::SseData => "sse_data",
94            Self::NdjsonLine => "ndjson_line",
95            Self::GrpcMessage => "grpc_message",
96            Self::WebSocketText => "websocket_text",
97            Self::WebSocketBinary => "websocket_binary",
98            Self::WebSocketClose => "websocket_close",
99        }
100    }
101}
102
103impl std::fmt::Display for FrameKind {
104    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
105        f.write_str(self.as_str())
106    }
107}
108
109/// Direction of a WebSocket frame.
110#[derive(Debug, Clone, Copy, PartialEq, Eq)]
111pub enum FrameDirection {
112    /// Client → server (request: model, prompt, tools).
113    ClientToServer,
114    /// Server → client (response: content deltas, usage).
115    ServerToClient,
116}
117
118/// A streaming data frame (SSE, NDJSON, gRPC, or WebSocket) delivered to the handler.
119#[derive(Debug, Clone, PartialEq, Eq)]
120pub struct StreamChunk {
121    pub connection_id: Uuid,
122    pub payload: Bytes,
123    pub sequence: u64,
124    pub frame_kind: FrameKind,
125    /// Direction for WebSocket frames. `None` for SSE/NDJSON/gRPC (always server→client).
126    pub direction: Option<FrameDirection>,
127}
128
129/// TLS metadata for the downstream connection.
130#[derive(Debug, Clone, PartialEq, Eq)]
131pub struct TlsInfo {
132    pub sni: Option<String>,
133    pub negotiated_proto: Option<String>,
134    pub ja4_hash: Option<String>,
135    pub tls_version: Option<TlsVersion>,
136}
137
138/// Metadata about the downstream connection (socket, TLS, process attribution).
139#[derive(Debug, Clone, PartialEq, Eq)]
140pub struct ConnectionMeta {
141    pub connection_id: Uuid,
142    pub socket_family: SocketFamily,
143    pub process_info: Option<ProcessInfo>,
144    pub tls_info: Option<TlsInfo>,
145    pub h2_connection_id: Option<String>,
146    pub h2_stream_id: Option<u32>,
147}
148
149/// Socket address family for the downstream connection.
150#[derive(Debug, Clone, PartialEq, Eq)]
151pub enum SocketFamily {
152    TcpV4 {
153        local: SocketAddrV4,
154        remote: SocketAddrV4,
155    },
156    TcpV6 {
157        local: SocketAddrV6,
158        remote: SocketAddrV6,
159    },
160    UnixDomain {
161        path: Option<PathBuf>,
162    },
163}
164
165impl SocketFamily {
166    pub fn as_str(&self) -> &'static str {
167        match self {
168            Self::TcpV4 { .. } => "tcp_v4",
169            Self::TcpV6 { .. } => "tcp_v6",
170            Self::UnixDomain { .. } => "unix_domain",
171        }
172    }
173}
174
175impl std::fmt::Display for SocketFamily {
176    fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
177        f.write_str(self.as_str())
178    }
179}
180
181#[derive(Debug, Clone, PartialEq, Eq)]
182pub struct ConnectionInfo {
183    pub connection_id: Uuid,
184    pub source_ip: IpAddr,
185    pub source_port: u16,
186    pub destination_host: String,
187    pub destination_port: u16,
188    pub socket_family: SocketFamily,
189    pub tls_fingerprint: Option<TlsClientFingerprint>,
190    pub alpn_protocol: Option<String>,
191    pub is_http2: bool,
192    pub process_info: Option<ProcessInfo>,
193    pub connected_at: SystemTime,
194    pub request_count: u32,
195}
196
197#[derive(Debug, Clone, PartialEq, Eq)]
198pub struct TlsClientFingerprint {
199    pub ja4: String,
200    pub ja3: String,
201    pub tls_version: TlsVersion,
202    pub cipher_suites: Vec<u16>,
203    pub extensions: Vec<u16>,
204    pub elliptic_curves: Vec<u16>,
205}
206
207/// Information about the local process that owns the downstream socket.
208#[derive(Debug, Clone, PartialEq, Eq)]
209pub struct ProcessInfo {
210    pub pid: u32,
211    pub bundle_id: Option<String>,
212    pub exe_name: Option<String>,
213    pub exe_path: Option<PathBuf>,
214    pub parent_pid: Option<u32>,
215    pub parent_process_name: Option<String>,
216}