1#![forbid(unsafe_code)]
2
3pub use huginn_net_db as db;
4pub use huginn_net_db::http;
5
6pub mod http1_parser;
7pub mod http1_process;
8pub mod http2_parser;
9pub mod http2_process;
10pub mod http_common;
11pub mod http_languages;
12pub mod http_process;
13
14pub mod display;
15pub mod error;
16pub mod observable;
17pub mod output;
18pub mod process;
19pub mod signature_matcher;
20
21pub use error::*;
23pub use http_process::*;
24pub use observable::*;
25pub use output::*;
26pub use process::*;
27pub use signature_matcher::*;
28
29use pnet::datalink::{self, Channel};
30use pnet::packet::ethernet::{EtherTypes, EthernetPacket};
31use pnet::packet::ipv4::Ipv4Packet;
32use pnet::packet::ipv6::Ipv6Packet;
33use pnet::packet::Packet;
34use std::sync::atomic::{AtomicBool, Ordering};
35use std::sync::mpsc::Sender;
36use std::sync::Arc;
37use tracing::debug;
38use ttl_cache::TtlCache;
39
40pub struct HuginnNetHttp<'a> {
45 pub matcher: Option<SignatureMatcher<'a>>,
46 http_flows: TtlCache<FlowKey, TcpFlow>,
47 http_processors: HttpProcessors,
48}
49
50impl<'a> HuginnNetHttp<'a> {
51 pub fn new(
60 database: Option<&'a db::Database>,
61 max_connections: usize,
62 ) -> Result<Self, HuginnNetHttpError> {
63 let matcher = database.map(SignatureMatcher::new);
64
65 Ok(Self {
66 matcher,
67 http_flows: TtlCache::new(max_connections),
68 http_processors: HttpProcessors::new(),
69 })
70 }
71
72 pub fn analyze_network(
82 &mut self,
83 interface_name: &str,
84 sender: Sender<HttpAnalysisResult>,
85 cancel_signal: Option<Arc<AtomicBool>>,
86 ) -> Result<(), HuginnNetHttpError> {
87 let interface = datalink::interfaces()
88 .into_iter()
89 .find(|iface| iface.name == interface_name)
90 .ok_or_else(|| {
91 HuginnNetHttpError::Parse(format!("Interface {interface_name} not found"))
92 })?;
93
94 let (_, mut rx) = match datalink::channel(&interface, Default::default()) {
95 Ok(Channel::Ethernet(tx, rx)) => (tx, rx),
96 Ok(_) => {
97 return Err(HuginnNetHttpError::Parse(
98 "Unsupported channel type".to_string(),
99 ))
100 }
101 Err(e) => {
102 return Err(HuginnNetHttpError::Parse(format!(
103 "Failed to create channel: {e}"
104 )))
105 }
106 };
107
108 loop {
109 if let Some(ref signal) = cancel_signal {
110 if signal.load(Ordering::Relaxed) {
111 break;
112 }
113 }
114
115 match rx.next() {
116 Ok(packet) => {
117 match self.process_packet(packet) {
119 Ok(result) => {
120 if sender.send(result).is_err() {
121 break;
122 }
123 }
124 Err(huginn_error) => {
125 debug!("Error processing packet: {}", huginn_error);
126 }
127 }
128 }
129 Err(e) => {
130 return Err(HuginnNetHttpError::Parse(format!(
131 "Error receiving packet: {e}"
132 )));
133 }
134 }
135 }
136
137 Ok(())
138 }
139
140 fn process_packet(&mut self, packet: &[u8]) -> Result<HttpAnalysisResult, HuginnNetHttpError> {
148 let ethernet = EthernetPacket::new(packet)
149 .ok_or_else(|| HuginnNetHttpError::Parse("Invalid Ethernet packet".to_string()))?;
150
151 match ethernet.get_ethertype() {
152 EtherTypes::Ipv4 => {
153 if let Some(ipv4) = Ipv4Packet::new(ethernet.payload()) {
154 process::process_ipv4_packet(
155 &ipv4,
156 &mut self.http_flows,
157 &self.http_processors,
158 self.matcher.as_ref(),
159 )
160 } else {
161 Ok(HttpAnalysisResult {
162 http_request: None,
163 http_response: None,
164 })
165 }
166 }
167 EtherTypes::Ipv6 => {
168 if let Some(ipv6) = Ipv6Packet::new(ethernet.payload()) {
169 process::process_ipv6_packet(
170 &ipv6,
171 &mut self.http_flows,
172 &self.http_processors,
173 self.matcher.as_ref(),
174 )
175 } else {
176 Ok(HttpAnalysisResult {
177 http_request: None,
178 http_response: None,
179 })
180 }
181 }
182 _ => Ok(HttpAnalysisResult {
183 http_request: None,
184 http_response: None,
185 }),
186 }
187 }
188}