Skip to main content

trojan_analytics/
event.rs

1//! Connection event types.
2
3use std::net::IpAddr;
4
5use clickhouse::Row;
6use serde::{Deserialize, Serialize};
7use time::OffsetDateTime;
8
9/// A connection event representing the full lifecycle of a single connection.
10#[derive(Debug, Clone, Row, Serialize, Deserialize)]
11pub struct ConnectionEvent {
12    // === Time dimension ===
13    /// Connection start time (UTC).
14    #[serde(with = "clickhouse::serde::time::datetime64::millis")]
15    pub timestamp: OffsetDateTime,
16
17    /// Connection duration in milliseconds.
18    pub duration_ms: u64,
19
20    // === Connection identity ===
21    /// Connection ID (unique within server instance).
22    pub conn_id: u64,
23
24    /// Client IP address.
25    pub peer_ip: IpAddr,
26
27    /// Client port.
28    pub peer_port: u16,
29
30    // === User identity ===
31    /// User identifier (password hash prefix or custom ID).
32    pub user_id: String,
33
34    /// Authentication result.
35    pub auth_result: AuthResult,
36
37    // === Target information ===
38    /// Target address type.
39    pub target_type: TargetType,
40
41    /// Target host (IP or domain).
42    pub target_host: String,
43
44    /// Target port.
45    pub target_port: u16,
46
47    /// SNI (Server Name Indication), if available.
48    pub sni: String,
49
50    // === Traffic statistics ===
51    /// Bytes sent (client → server → target).
52    pub bytes_sent: u64,
53
54    /// Bytes received (target → server → client).
55    pub bytes_recv: u64,
56
57    /// Packets sent (UDP only).
58    pub packets_sent: u64,
59
60    /// Packets received (UDP only).
61    pub packets_recv: u64,
62
63    // === Connection metadata ===
64    /// Protocol type.
65    pub protocol: Protocol,
66
67    /// Transport layer.
68    pub transport: Transport,
69
70    /// Connection close reason.
71    pub close_reason: CloseReason,
72
73    /// Whether this was a fallback connection.
74    pub is_fallback: bool,
75
76    // === Server information ===
77    /// Server instance ID.
78    pub server_id: String,
79}
80
81impl ConnectionEvent {
82    /// Create a new connection event with default values.
83    pub fn new(conn_id: u64, peer_ip: IpAddr, peer_port: u16) -> Self {
84        Self {
85            timestamp: OffsetDateTime::now_utc(),
86            duration_ms: 0,
87            conn_id,
88            peer_ip,
89            peer_port,
90            user_id: String::new(),
91            auth_result: AuthResult::Skipped,
92            target_type: TargetType::Domain,
93            target_host: String::new(),
94            target_port: 0,
95            sni: String::new(),
96            bytes_sent: 0,
97            bytes_recv: 0,
98            packets_sent: 0,
99            packets_recv: 0,
100            protocol: Protocol::Tcp,
101            transport: Transport::Direct,
102            close_reason: CloseReason::Normal,
103            is_fallback: false,
104            server_id: String::new(),
105        }
106    }
107}
108
109/// Authentication result.
110#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
111#[serde(rename_all = "snake_case")]
112pub enum AuthResult {
113    /// Authentication succeeded.
114    Success,
115    /// Authentication failed.
116    Failed,
117    /// Authentication was skipped (fallback traffic).
118    Skipped,
119}
120
121impl From<AuthResult> for &'static str {
122    fn from(r: AuthResult) -> Self {
123        match r {
124            AuthResult::Success => "success",
125            AuthResult::Failed => "failed",
126            AuthResult::Skipped => "skipped",
127        }
128    }
129}
130
131/// Target address type.
132#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
133#[serde(rename_all = "snake_case")]
134pub enum TargetType {
135    /// IPv4 address.
136    Ipv4,
137    /// IPv6 address.
138    Ipv6,
139    /// Domain name.
140    Domain,
141}
142
143impl From<TargetType> for &'static str {
144    fn from(t: TargetType) -> Self {
145        match t {
146            TargetType::Ipv4 => "ipv4",
147            TargetType::Ipv6 => "ipv6",
148            TargetType::Domain => "domain",
149        }
150    }
151}
152
153/// Connection protocol.
154#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
155#[serde(rename_all = "snake_case")]
156pub enum Protocol {
157    /// TCP connection.
158    Tcp,
159    /// UDP association.
160    Udp,
161}
162
163impl From<Protocol> for &'static str {
164    fn from(p: Protocol) -> Self {
165        match p {
166            Protocol::Tcp => "tcp",
167            Protocol::Udp => "udp",
168        }
169    }
170}
171
172/// Transport layer type.
173#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
174#[serde(rename_all = "snake_case")]
175pub enum Transport {
176    /// Direct TLS connection.
177    Direct,
178    /// WebSocket transport.
179    WebSocket,
180}
181
182impl From<Transport> for &'static str {
183    fn from(t: Transport) -> Self {
184        match t {
185            Transport::Direct => "direct",
186            Transport::WebSocket => "websocket",
187        }
188    }
189}
190
191/// Connection close reason.
192#[derive(Debug, Clone, Copy, PartialEq, Eq, Serialize, Deserialize)]
193#[serde(rename_all = "snake_case")]
194pub enum CloseReason {
195    /// Normal close.
196    Normal,
197    /// Idle timeout.
198    Timeout,
199    /// Error occurred.
200    Error,
201    /// Connection reset.
202    Reset,
203    /// Server shutdown.
204    ServerShutdown,
205}
206
207impl From<CloseReason> for &'static str {
208    fn from(r: CloseReason) -> Self {
209        match r {
210            CloseReason::Normal => "normal",
211            CloseReason::Timeout => "timeout",
212            CloseReason::Error => "error",
213            CloseReason::Reset => "reset",
214            CloseReason::ServerShutdown => "shutdown",
215        }
216    }
217}