1use crate::error::HuginnNetError;
2use crate::parser::packet::{parse_packet, IpPacket};
3use crate::AnalysisConfig;
4use huginn_net_http::error::HuginnNetHttpError;
5use huginn_net_http::http_process::{FlowKey, HttpProcessors, ObservableHttpPackage, TcpFlow};
6#[cfg(feature = "http-p0f-request")]
7use huginn_net_http::observable::ObservableHttpRequest;
8#[cfg(feature = "http-p0f-response")]
9use huginn_net_http::observable::ObservableHttpResponse;
10use huginn_net_tcp::error::HuginnNetTcpError;
11#[cfg(feature = "tcp-mtu")]
12use huginn_net_tcp::observable::ObservableMtu;
13#[cfg(any(feature = "tcp-syn", feature = "tcp-syn-ack"))]
14use huginn_net_tcp::observable::ObservableTcp;
15#[cfg(feature = "tcp-uptime")]
16use huginn_net_tcp::observable::ObservableUptime;
17use huginn_net_tcp::tcp_process::ObservableTCPPackage;
18use huginn_net_tcp::ConnectionTracker;
19use huginn_net_tls::error::HuginnNetTlsError;
20use huginn_net_tls::{ObservableTlsClient, ObservableTlsPackage};
21use pnet::packet::ip::IpNextHeaderProtocols;
22use pnet::packet::{ipv4::Ipv4Packet, ipv6::Ipv6Packet, tcp::TcpPacket, Packet};
23use std::net::IpAddr;
24use ttl_cache::TtlCache;
25
26#[derive(Clone)]
27pub struct IpPort {
28 pub ip: IpAddr,
29 pub port: u16,
30}
31
32pub struct ObservablePackage {
33 pub source: IpPort,
34 pub destination: IpPort,
35 #[cfg(feature = "tcp-syn")]
36 pub tcp_request: Option<ObservableTcp>,
37 #[cfg(feature = "tcp-syn-ack")]
38 pub tcp_response: Option<ObservableTcp>,
39 #[cfg(feature = "tcp-mtu")]
40 pub mtu: Option<ObservableMtu>,
41 #[cfg(feature = "tcp-uptime")]
42 pub client_uptime: Option<ObservableUptime>,
43 #[cfg(feature = "tcp-uptime")]
44 pub server_uptime: Option<ObservableUptime>,
45 #[cfg(feature = "http-p0f-request")]
46 pub http_request: Option<ObservableHttpRequest>,
47 #[cfg(feature = "http-p0f-response")]
48 pub http_response: Option<ObservableHttpResponse>,
49 pub tls_client: Option<ObservableTlsClient>,
50}
51
52impl ObservablePackage {
53 pub fn extract(
54 packet: &[u8],
55 connection_tracker: &mut ConnectionTracker,
56 http_flows: &mut TtlCache<FlowKey, TcpFlow>,
57 http_processors: &HttpProcessors,
58 config: &AnalysisConfig,
59 ) -> Result<Self, HuginnNetError> {
60 match parse_packet(packet) {
61 IpPacket::Ipv4(ip_data) => {
62 if let Some(ipv4) = Ipv4Packet::new(ip_data) {
63 process_ipv4(connection_tracker, http_flows, http_processors, ipv4, config)
64 } else {
65 Err(HuginnNetError::UnexpectedPackage("Invalid IPv4 packet".to_string()))
66 }
67 }
68 IpPacket::Ipv6(ip_data) => {
69 if let Some(ipv6) = Ipv6Packet::new(ip_data) {
70 process_ipv6(connection_tracker, http_flows, http_processors, ipv6, config)
71 } else {
72 Err(HuginnNetError::UnexpectedPackage("Invalid IPv6 packet".to_string()))
73 }
74 }
75 IpPacket::None => {
76 Err(HuginnNetError::UnexpectedPackage("No valid IP packet found".to_string()))
77 }
78 }
79 }
80}
81
82trait IpPacketProcessor: Packet {
83 fn is_tcp(&self) -> bool;
84 fn get_protocol_error(&self) -> String;
85 fn get_addresses(&self) -> (IpAddr, IpAddr);
86 fn process_http_with_data(
87 data: &[u8],
88 http_flows: &mut TtlCache<FlowKey, TcpFlow>,
89 http_processors: &HttpProcessors,
90 ) -> Result<ObservableHttpPackage, HuginnNetError>;
91 fn process_tcp_with_data(
92 data: &[u8],
93 connection_tracker: &mut ConnectionTracker,
94 ) -> Result<ObservableTCPPackage, HuginnNetError>;
95 fn process_tls_with_data(data: &[u8]) -> Result<ObservableTlsPackage, HuginnNetError>;
96}
97
98impl IpPacketProcessor for Ipv4Packet<'_> {
99 fn is_tcp(&self) -> bool {
100 self.get_next_level_protocol() == IpNextHeaderProtocols::Tcp
101 }
102
103 fn get_protocol_error(&self) -> String {
104 format!(
105 "unsupported IPv4 packet with non-TCP payload: {}",
106 self.get_next_level_protocol()
107 )
108 }
109
110 fn get_addresses(&self) -> (IpAddr, IpAddr) {
111 (IpAddr::V4(self.get_source()), IpAddr::V4(self.get_destination()))
112 }
113
114 fn process_http_with_data(
115 data: &[u8],
116 http_flows: &mut TtlCache<FlowKey, TcpFlow>,
117 http_processors: &HttpProcessors,
118 ) -> Result<ObservableHttpPackage, HuginnNetError> {
119 if let Some(packet) = Ipv4Packet::new(data) {
120 huginn_net_http::http_process::process_http_ipv4(&packet, http_flows, http_processors)
121 .map_err(|e| match e {
122 HuginnNetHttpError::Parse(msg) => HuginnNetError::Parse(msg),
123 HuginnNetHttpError::UnsupportedProtocol(msg) => {
124 HuginnNetError::UnsupportedProtocol(msg)
125 }
126 HuginnNetHttpError::Misconfiguration(msg) => HuginnNetError::Parse(msg),
127 HuginnNetHttpError::NoSettingsFrame => {
128 HuginnNetError::Parse("No SETTINGS frame found".to_string())
129 }
130 HuginnNetHttpError::MalformedPseudoHeaders(msg) => HuginnNetError::Parse(msg),
131 })
132 } else {
133 Err(HuginnNetError::UnexpectedPackage("Invalid IPv4 packet data".to_string()))
134 }
135 }
136
137 fn process_tcp_with_data(
138 data: &[u8],
139 connection_tracker: &mut ConnectionTracker,
140 ) -> Result<ObservableTCPPackage, HuginnNetError> {
141 if let Some(packet) = Ipv4Packet::new(data) {
142 huginn_net_tcp::tcp_process::process_tcp_ipv4(&packet, connection_tracker).map_err(
143 |e| match e {
144 HuginnNetTcpError::Parse(msg) => HuginnNetError::Parse(msg),
145 HuginnNetTcpError::UnsupportedProtocol(msg) => {
146 HuginnNetError::UnsupportedProtocol(msg)
147 }
148 HuginnNetTcpError::InvalidTcpFlags(flags) => {
149 HuginnNetError::InvalidTcpFlags(flags)
150 }
151 HuginnNetTcpError::UnexpectedPackage(msg) => {
152 HuginnNetError::UnexpectedPackage(msg)
153 }
154 HuginnNetTcpError::Misconfiguration(msg) => HuginnNetError::Parse(msg),
155 },
156 )
157 } else {
158 Err(HuginnNetError::UnexpectedPackage("Invalid IPv4 packet data".to_string()))
159 }
160 }
161
162 fn process_tls_with_data(data: &[u8]) -> Result<ObservableTlsPackage, HuginnNetError> {
163 if let Some(packet) = Ipv4Packet::new(data) {
164 huginn_net_tls::process_tls_ipv4(&packet).map_err(|e| match e {
165 HuginnNetTlsError::Parse(msg) => HuginnNetError::Parse(msg),
166 HuginnNetTlsError::UnsupportedProtocol(msg) => {
167 HuginnNetError::UnsupportedProtocol(msg)
168 }
169 HuginnNetTlsError::Misconfiguration(msg) => HuginnNetError::Parse(msg),
170 HuginnNetTlsError::NotClientHello => {
171 HuginnNetError::Parse("TLS record is not a ClientHello".to_string())
172 }
173 HuginnNetTlsError::Unknown => {
174 HuginnNetError::Parse("Unknown TLS error".to_string())
175 }
176 })
177 } else {
178 Err(HuginnNetError::UnexpectedPackage("Invalid IPv4 packet data".to_string()))
179 }
180 }
181}
182
183impl IpPacketProcessor for Ipv6Packet<'_> {
184 fn is_tcp(&self) -> bool {
185 self.get_next_header() == IpNextHeaderProtocols::Tcp
186 }
187
188 fn get_protocol_error(&self) -> String {
189 format!("IPv6 packet with non-TCP payload: {}", self.get_next_header())
190 }
191
192 fn get_addresses(&self) -> (IpAddr, IpAddr) {
193 (IpAddr::V6(self.get_source()), IpAddr::V6(self.get_destination()))
194 }
195
196 fn process_http_with_data(
197 data: &[u8],
198 http_flows: &mut TtlCache<FlowKey, TcpFlow>,
199 http_processors: &HttpProcessors,
200 ) -> Result<ObservableHttpPackage, HuginnNetError> {
201 if let Some(packet) = Ipv6Packet::new(data) {
202 huginn_net_http::http_process::process_http_ipv6(&packet, http_flows, http_processors)
203 .map_err(|e| match e {
204 HuginnNetHttpError::Parse(msg) => HuginnNetError::Parse(msg),
205 HuginnNetHttpError::UnsupportedProtocol(msg) => {
206 HuginnNetError::UnsupportedProtocol(msg)
207 }
208 HuginnNetHttpError::Misconfiguration(msg) => HuginnNetError::Parse(msg),
209 HuginnNetHttpError::NoSettingsFrame => {
210 HuginnNetError::Parse("No SETTINGS frame found".to_string())
211 }
212 HuginnNetHttpError::MalformedPseudoHeaders(msg) => HuginnNetError::Parse(msg),
213 })
214 } else {
215 Err(HuginnNetError::UnexpectedPackage("Invalid IPv6 packet data".to_string()))
216 }
217 }
218
219 fn process_tcp_with_data(
220 data: &[u8],
221 connection_tracker: &mut ConnectionTracker,
222 ) -> Result<ObservableTCPPackage, HuginnNetError> {
223 if let Some(packet) = Ipv6Packet::new(data) {
224 huginn_net_tcp::tcp_process::process_tcp_ipv6(&packet, connection_tracker).map_err(
225 |e| match e {
226 HuginnNetTcpError::Parse(msg) => HuginnNetError::Parse(msg),
227 HuginnNetTcpError::UnsupportedProtocol(msg) => {
228 HuginnNetError::UnsupportedProtocol(msg)
229 }
230 HuginnNetTcpError::InvalidTcpFlags(flags) => {
231 HuginnNetError::InvalidTcpFlags(flags)
232 }
233 HuginnNetTcpError::UnexpectedPackage(msg) => {
234 HuginnNetError::UnexpectedPackage(msg)
235 }
236 HuginnNetTcpError::Misconfiguration(msg) => HuginnNetError::Parse(msg),
237 },
238 )
239 } else {
240 Err(HuginnNetError::UnexpectedPackage("Invalid IPv6 packet data".to_string()))
241 }
242 }
243
244 fn process_tls_with_data(data: &[u8]) -> Result<ObservableTlsPackage, HuginnNetError> {
245 if let Some(packet) = Ipv6Packet::new(data) {
246 huginn_net_tls::process_tls_ipv6(&packet).map_err(|e| match e {
247 huginn_net_tls::error::HuginnNetTlsError::Parse(msg) => HuginnNetError::Parse(msg),
248 huginn_net_tls::error::HuginnNetTlsError::UnsupportedProtocol(msg) => {
249 HuginnNetError::UnsupportedProtocol(msg)
250 }
251 huginn_net_tls::error::HuginnNetTlsError::Misconfiguration(msg) => {
252 HuginnNetError::Parse(msg)
253 }
254 huginn_net_tls::error::HuginnNetTlsError::NotClientHello => {
255 HuginnNetError::Parse("TLS record is not a ClientHello".to_string())
256 }
257 huginn_net_tls::error::HuginnNetTlsError::Unknown => {
258 HuginnNetError::Parse("Unknown TLS error".to_string())
259 }
260 })
261 } else {
262 Err(HuginnNetError::UnexpectedPackage("Invalid IPv6 packet data".to_string()))
263 }
264 }
265}
266
267fn execute_analysis<P: IpPacketProcessor>(
268 packet_data: &[u8],
269 connection_tracker: &mut ConnectionTracker,
270 http_flows: &mut TtlCache<FlowKey, TcpFlow>,
271 http_processors: &HttpProcessors,
272 config: &AnalysisConfig,
273 source: IpPort,
274 destination: IpPort,
275) -> Result<ObservablePackage, HuginnNetError> {
276 let http_response = if config.http_enabled {
277 P::process_http_with_data(packet_data, http_flows, http_processors)?
278 } else {
279 ObservableHttpPackage::empty()
280 };
281
282 let tcp_response: ObservableTCPPackage = if config.tcp_enabled {
283 P::process_tcp_with_data(packet_data, connection_tracker)?
284 } else {
285 ObservableTCPPackage::empty()
286 };
287
288 let tls_response = if config.tls_enabled {
289 P::process_tls_with_data(packet_data)?
290 } else {
291 ObservableTlsPackage { tls_client: None }
292 };
293
294 handle_http_tcp_tlc(Ok(http_response), Ok(tcp_response), Ok(tls_response), source, destination)
295}
296
297fn process_ip<P: IpPacketProcessor>(
298 connection_tracker: &mut ConnectionTracker,
299 http_flows: &mut TtlCache<FlowKey, TcpFlow>,
300 http_processors: &HttpProcessors,
301 packet: P,
302 config: &AnalysisConfig,
303) -> Result<ObservablePackage, HuginnNetError> {
304 if !packet.is_tcp() {
305 return Err(HuginnNetError::UnsupportedProtocol(packet.get_protocol_error()));
306 }
307
308 let (source_ip, destination_ip) = packet.get_addresses();
309 let tcp_ports = TcpPacket::new(packet.payload())
310 .ok_or_else(|| HuginnNetError::UnexpectedPackage("Invalid TCP packet".to_string()))?;
311
312 let source = IpPort { ip: source_ip, port: tcp_ports.get_source() };
313 let destination = IpPort { ip: destination_ip, port: tcp_ports.get_destination() };
314
315 let packet_data = packet.packet();
316
317 execute_analysis::<P>(
318 packet_data,
319 connection_tracker,
320 http_flows,
321 http_processors,
322 config,
323 source,
324 destination,
325 )
326}
327
328pub fn process_ipv4(
329 connection_tracker: &mut ConnectionTracker,
330 http_flows: &mut TtlCache<FlowKey, TcpFlow>,
331 http_processors: &HttpProcessors,
332 packet: Ipv4Packet,
333 config: &AnalysisConfig,
334) -> Result<ObservablePackage, HuginnNetError> {
335 process_ip(connection_tracker, http_flows, http_processors, packet, config)
336}
337
338pub fn process_ipv6(
339 connection_tracker: &mut ConnectionTracker,
340 http_flows: &mut TtlCache<FlowKey, TcpFlow>,
341 http_processors: &HttpProcessors,
342 packet: Ipv6Packet,
343 config: &AnalysisConfig,
344) -> Result<ObservablePackage, HuginnNetError> {
345 process_ip(connection_tracker, http_flows, http_processors, packet, config)
346}
347
348fn handle_http_tcp_tlc(
349 http_response: Result<ObservableHttpPackage, HuginnNetError>,
350 tcp_response: Result<ObservableTCPPackage, HuginnNetError>,
351 tls_response: Result<ObservableTlsPackage, HuginnNetError>,
352 source: IpPort,
353 destination: IpPort,
354) -> Result<ObservablePackage, HuginnNetError> {
355 match (http_response, tcp_response, tls_response) {
356 #[cfg_attr(
357 not(any(
358 feature = "tcp-syn",
359 feature = "tcp-syn-ack",
360 feature = "tcp-mtu",
361 feature = "tcp-uptime",
362 feature = "http-p0f-request",
363 feature = "http-p0f-response"
364 )),
365 allow(unused_variables)
366 )]
367 (Ok(http_package), Ok(tcp_package), Ok(tls_package)) => {
368 #[cfg(not(any(
369 feature = "tcp-syn",
370 feature = "tcp-syn-ack",
371 feature = "tcp-mtu",
372 feature = "tcp-uptime"
373 )))]
374 let _ = &tcp_package;
375 #[cfg(not(any(feature = "http-p0f-request", feature = "http-p0f-response")))]
376 let _ = &http_package;
377 Ok(ObservablePackage {
378 source,
379 destination,
380 #[cfg(feature = "tcp-syn")]
381 tcp_request: tcp_package.tcp_request,
382 #[cfg(feature = "tcp-syn-ack")]
383 tcp_response: tcp_package.tcp_response,
384 #[cfg(feature = "tcp-mtu")]
385 mtu: tcp_package.mtu,
386 #[cfg(feature = "tcp-uptime")]
387 client_uptime: tcp_package.client_uptime,
388 #[cfg(feature = "tcp-uptime")]
389 server_uptime: tcp_package.server_uptime,
390 #[cfg(feature = "http-p0f-request")]
391 http_request: http_package.http_request,
392 #[cfg(feature = "http-p0f-response")]
393 http_response: http_package.http_response,
394 tls_client: tls_package.tls_client,
395 })
396 }
397 (Err(http_err), _, _) => Err(http_err),
398 (_, Err(tcp_err), _) => Err(tcp_err),
399 (_, _, Err(tls_err)) => Err(tls_err),
400 }
401}