huginn_net_http/
process.rs

1use crate::error::HuginnNetHttpError;
2use crate::output::{
3    Browser, BrowserQualityMatched, HttpRequestOutput, HttpResponseOutput, IpPort, WebServer,
4    WebServerQualityMatched,
5};
6use crate::{http_process, HttpAnalysisResult, SignatureMatcher};
7use huginn_net_db::http::HttpDiagnosis;
8use pnet::packet::ipv4::Ipv4Packet;
9use pnet::packet::ipv6::Ipv6Packet;
10use pnet::packet::tcp::TcpPacket;
11use pnet::packet::Packet;
12use std::net::IpAddr;
13use ttl_cache::TtlCache;
14
15pub struct ObservablePackage {
16    pub source: IpPort,
17    pub destination: IpPort,
18    pub http_result: HttpAnalysisResult,
19}
20
21/// Processes an IPv4 packet for HTTP content.
22pub fn process_ipv4_packet(
23    ipv4: &Ipv4Packet,
24    http_flows: &mut TtlCache<http_process::FlowKey, http_process::TcpFlow>,
25    http_processors: &http_process::HttpProcessors,
26    matcher: Option<&SignatureMatcher>,
27) -> Result<HttpAnalysisResult, HuginnNetHttpError> {
28    let observable_package =
29        create_observable_package_ipv4(ipv4, http_flows, http_processors, matcher)?;
30    Ok(observable_package.http_result)
31}
32
33fn create_observable_package_ipv4(
34    ipv4: &Ipv4Packet,
35    http_flows: &mut TtlCache<http_process::FlowKey, http_process::TcpFlow>,
36    http_processors: &http_process::HttpProcessors,
37    matcher: Option<&SignatureMatcher>,
38) -> Result<ObservablePackage, HuginnNetHttpError> {
39    let tcp = TcpPacket::new(ipv4.payload())
40        .ok_or_else(|| HuginnNetHttpError::Parse("Invalid TCP packet".to_string()))?;
41
42    let source = IpPort {
43        ip: IpAddr::V4(ipv4.get_source()),
44        port: tcp.get_source(),
45    };
46    let destination = IpPort {
47        ip: IpAddr::V4(ipv4.get_destination()),
48        port: tcp.get_destination(),
49    };
50
51    let http_package = http_process::process_http_ipv4(ipv4, http_flows, http_processors)?;
52
53    let mut http_result = HttpAnalysisResult {
54        http_request: None,
55        http_response: None,
56    };
57
58    if let Some(http_request) = http_package.http_request {
59        let browser_quality = if let Some(matcher) = matcher {
60            if let Some((label, _signature, quality)) =
61                matcher.matching_by_http_request(&http_request)
62            {
63                BrowserQualityMatched {
64                    browser: Some(Browser::from(label)),
65                    quality: huginn_net_db::utils::MatchQualityType::Matched(quality),
66                }
67            } else {
68                BrowserQualityMatched {
69                    browser: None,
70                    quality: huginn_net_db::utils::MatchQualityType::NotMatched,
71                }
72            }
73        } else {
74            BrowserQualityMatched {
75                browser: None,
76                quality: huginn_net_db::utils::MatchQualityType::Disabled,
77            }
78        };
79
80        let user_agent = http_request.user_agent.clone();
81        let (signature_matcher, ua_matcher) = if let Some(matcher) = matcher {
82            let sig_match = matcher.matching_by_http_request(&http_request);
83            let ua_match = user_agent
84                .as_ref()
85                .and_then(|ua| matcher.matching_by_user_agent(ua.clone()));
86            (sig_match, ua_match)
87        } else {
88            (None, None)
89        };
90
91        let diagnosis = crate::http_common::get_diagnostic(
92            user_agent,
93            ua_matcher,
94            signature_matcher.map(|(label, _signature, _quality)| label),
95        );
96
97        let request_output = HttpRequestOutput {
98            source: IpPort::new(std::net::IpAddr::V4(ipv4.get_source()), tcp.get_source()),
99            destination: IpPort::new(
100                std::net::IpAddr::V4(ipv4.get_destination()),
101                tcp.get_destination(),
102            ),
103            lang: http_request.lang.clone(),
104            diagnosis,
105            browser_matched: browser_quality,
106            sig: http_request,
107        };
108        http_result.http_request = Some(request_output);
109    }
110
111    if let Some(http_response) = http_package.http_response {
112        let web_server_quality = if let Some(matcher) = matcher {
113            if let Some((label, _signature, quality)) =
114                matcher.matching_by_http_response(&http_response)
115            {
116                WebServerQualityMatched {
117                    web_server: Some(WebServer::from(label)),
118                    quality: huginn_net_db::utils::MatchQualityType::Matched(quality),
119                }
120            } else {
121                WebServerQualityMatched {
122                    web_server: None,
123                    quality: huginn_net_db::utils::MatchQualityType::NotMatched,
124                }
125            }
126        } else {
127            WebServerQualityMatched {
128                web_server: None,
129                quality: huginn_net_db::utils::MatchQualityType::Disabled,
130            }
131        };
132
133        let response_output = HttpResponseOutput {
134            source: IpPort::new(std::net::IpAddr::V4(ipv4.get_source()), tcp.get_source()),
135            destination: IpPort::new(
136                std::net::IpAddr::V4(ipv4.get_destination()),
137                tcp.get_destination(),
138            ),
139            diagnosis: HttpDiagnosis::None, // Default diagnosis for responses
140            web_server_matched: web_server_quality,
141            sig: http_response,
142        };
143        http_result.http_response = Some(response_output);
144    }
145
146    Ok(ObservablePackage {
147        source,
148        destination,
149        http_result,
150    })
151}
152
153/// Processes an IPv6 packet for HTTP content.
154pub fn process_ipv6_packet(
155    ipv6: &Ipv6Packet,
156    http_flows: &mut TtlCache<http_process::FlowKey, http_process::TcpFlow>,
157    http_processors: &http_process::HttpProcessors,
158    matcher: Option<&SignatureMatcher>,
159) -> Result<HttpAnalysisResult, HuginnNetHttpError> {
160    let observable_package =
161        create_observable_package_ipv6(ipv6, http_flows, http_processors, matcher)?;
162    Ok(observable_package.http_result)
163}
164
165fn create_observable_package_ipv6(
166    ipv6: &Ipv6Packet,
167    http_flows: &mut TtlCache<http_process::FlowKey, http_process::TcpFlow>,
168    http_processors: &http_process::HttpProcessors,
169    matcher: Option<&SignatureMatcher>,
170) -> Result<ObservablePackage, HuginnNetHttpError> {
171    // Extract TCP info for source/destination ports
172    let tcp = TcpPacket::new(ipv6.payload())
173        .ok_or_else(|| HuginnNetHttpError::Parse("Invalid TCP packet".to_string()))?;
174
175    let source = IpPort {
176        ip: IpAddr::V6(ipv6.get_source()),
177        port: tcp.get_source(),
178    };
179    let destination = IpPort {
180        ip: IpAddr::V6(ipv6.get_destination()),
181        port: tcp.get_destination(),
182    };
183
184    let http_package = http_process::process_http_ipv6(ipv6, http_flows, http_processors)?;
185
186    let mut http_result = HttpAnalysisResult {
187        http_request: None,
188        http_response: None,
189    };
190
191    // Process HTTP request
192    if let Some(http_request) = http_package.http_request {
193        let browser_quality = if let Some(matcher) = matcher {
194            if let Some((label, _signature, quality)) =
195                matcher.matching_by_http_request(&http_request)
196            {
197                BrowserQualityMatched {
198                    browser: Some(Browser::from(label)),
199                    quality: huginn_net_db::utils::MatchQualityType::Matched(quality),
200                }
201            } else {
202                BrowserQualityMatched {
203                    browser: None,
204                    quality: huginn_net_db::utils::MatchQualityType::NotMatched,
205                }
206            }
207        } else {
208            BrowserQualityMatched {
209                browser: None,
210                quality: huginn_net_db::utils::MatchQualityType::Disabled,
211            }
212        };
213
214        let user_agent = http_request.user_agent.clone();
215        let (signature_matcher, ua_matcher) = if let Some(matcher) = matcher {
216            let sig_match = matcher.matching_by_http_request(&http_request);
217            let ua_match = user_agent
218                .as_ref()
219                .and_then(|ua| matcher.matching_by_user_agent(ua.clone()));
220            (sig_match, ua_match)
221        } else {
222            (None, None)
223        };
224
225        let diagnosis = crate::http_common::get_diagnostic(
226            user_agent,
227            ua_matcher,
228            signature_matcher.map(|(label, _signature, _quality)| label),
229        );
230
231        let request_output = HttpRequestOutput {
232            source: IpPort::new(std::net::IpAddr::V6(ipv6.get_source()), tcp.get_source()),
233            destination: IpPort::new(
234                std::net::IpAddr::V6(ipv6.get_destination()),
235                tcp.get_destination(),
236            ),
237            lang: http_request.lang.clone(),
238            diagnosis,
239            browser_matched: browser_quality,
240            sig: http_request,
241        };
242        http_result.http_request = Some(request_output);
243    }
244
245    // Process HTTP response
246    if let Some(http_response) = http_package.http_response {
247        let web_server_quality = if let Some(matcher) = matcher {
248            if let Some((label, _signature, quality)) =
249                matcher.matching_by_http_response(&http_response)
250            {
251                WebServerQualityMatched {
252                    web_server: Some(WebServer::from(label)),
253                    quality: huginn_net_db::utils::MatchQualityType::Matched(quality),
254                }
255            } else {
256                WebServerQualityMatched {
257                    web_server: None,
258                    quality: huginn_net_db::utils::MatchQualityType::NotMatched,
259                }
260            }
261        } else {
262            WebServerQualityMatched {
263                web_server: None,
264                quality: huginn_net_db::utils::MatchQualityType::Disabled,
265            }
266        };
267
268        let response_output = HttpResponseOutput {
269            source: IpPort::new(std::net::IpAddr::V6(ipv6.get_source()), tcp.get_source()),
270            destination: IpPort::new(
271                std::net::IpAddr::V6(ipv6.get_destination()),
272                tcp.get_destination(),
273            ),
274            diagnosis: HttpDiagnosis::None, // Default diagnosis for responses
275            web_server_matched: web_server_quality,
276            sig: http_response,
277        };
278        http_result.http_response = Some(response_output);
279    }
280
281    Ok(ObservablePackage {
282        source,
283        destination,
284        http_result,
285    })
286}