Skip to content

Commit

Permalink
httparse adapted
Browse files Browse the repository at this point in the history
  • Loading branch information
biandratti committed Dec 21, 2024
1 parent f5fe942 commit e94f4c6
Show file tree
Hide file tree
Showing 4 changed files with 54 additions and 42 deletions.
7 changes: 7 additions & 0 deletions Cargo.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ failure = "0.1.8"
log = "0.4.22"
ttl_cache = "0.5.1"
lazy_static = "1.5.0"
httparse = "1.9.5"

[dev-dependencies]
clap = { version = "4.5.23", features = ["derive"] }
Expand Down
82 changes: 43 additions & 39 deletions src/http_process.rs
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@ use pnet::packet::Packet;
use std::net::IpAddr;
use std::time::{Duration, Instant};
use ttl_cache::TtlCache;
use httparse::{Request, Response, EMPTY_HEADER};
use log::{debug, error, info};

pub type FlowKey = (IpAddr, IpAddr, u16, u16); // (Client IP, Server IP, Client Port, Server Port)

Expand Down Expand Up @@ -68,7 +70,6 @@ fn process_tcp_packet(
let flow_key = (src_ip, dst_ip, src_port, dst_port);

if tcp.get_flags() & pnet::packet::tcp::TcpFlags::SYN != 0 {
//println!("New TCP flow detected: {}:{} -> {}:{}", src_ip, src_port, dst_ip, dst_port);
let flow = TcpFlow {
client_ip: src_ip,
server_ip: dst_ip,
Expand All @@ -81,72 +82,75 @@ fn process_tcp_packet(
last_seen: Instant::now(),
};
cache.insert(flow_key, flow, Duration::new(60, 0));
return Ok(ObservableHttpPackage { http_request: None }); //TODO: WIP
return Ok(ObservableHttpPackage { http_request: None });
}

if let Some(flow) = cache.get_mut(&flow_key) {
flow.last_seen = Instant::now();

// Handle data payload
if !tcp.payload().is_empty() {
// Append the new data to the flow data
if src_ip == flow.client_ip && src_port == flow.client_port {
flow.client_data.extend_from_slice(tcp.payload());
if let Ok(request) = std::str::from_utf8(&flow.client_data) {
if request.contains("HTTP") {
println!("HTTP Request: {}", request);
}
if let Ok(request) = parse_http_request(&flow.client_data) {
info!("HTTP Request:\n{:?}", request);
}
} else {
flow.server_data.extend_from_slice(tcp.payload());
if let Ok(response) = std::str::from_utf8(&flow.server_data) {
if response.contains("HTTP") {
println!("HTTP Response: {}", response);
}
}
}
}

if let Some(flow) = cache.get_mut(&flow_key) {
flow.last_seen = Instant::now();

if !tcp.payload().is_empty() {
//TODO: Process payload here...
}

// Check for termination flags
let should_remove = tcp.get_flags()
& (pnet::packet::tcp::TcpFlags::FIN | pnet::packet::tcp::TcpFlags::RST)
!= 0;

if should_remove {
/*println!(
"TCP flow closing or reset: {}:{} -> {}:{}",
flow.client_ip, flow.client_port, flow.server_ip, flow.server_port
);*/
// Try to parse the HTTP response when enough data is accumulated
/*if let Ok(response) = parse_http_response(&flow.server_data) {
info!("HTTP Response:\n{:?}", response);
}*/
}
}

if tcp.get_flags() & (pnet::packet::tcp::TcpFlags::FIN | pnet::packet::tcp::TcpFlags::RST)
!= 0
{
debug!("Connection closed or reset");
cache.remove(&flow_key);
}
} else {
// TODO: Handle case where packet belongs to an untracked flow
/*println!(
"Untracked TCP flow: {}:{} -> {}:{}",
src_ip, src_port, dst_ip, dst_port
);*/
}
Ok(ObservableHttpPackage { http_request: None }) //TODO: WIP

Ok(ObservableHttpPackage { http_request: None })
}

fn parse_http_request(data: &[u8]) -> Result<Option<ObservableHttpRequest>, Error> {
let mut headers = [EMPTY_HEADER; 16];
let mut req = Request::new(&mut headers);

match req.parse(data) {
Ok(httparse::Status::Complete(_)) => {
let headers: Vec<_> = req.headers.iter()
.map(|h| (h.name.to_string(), String::from_utf8_lossy(h.value).to_string()))
.collect();

info!("Successfully parsed HTTP Request. Headers: {:?}", headers);

Ok(Some(ObservableHttpRequest {
lang: headers.iter().find(|(k, _)| k.eq_ignore_ascii_case("Accept-Language")).map(|(_, v)| v.clone()),
user_agent: headers.iter().find(|(k, _)| k.eq_ignore_ascii_case("User-Agent")).map(|(_, v)| v.clone()),
}))
},
Ok(httparse::Status::Partial) => {
debug!("Incomplete HTTP request data. Data: {:?}", data);
Ok(None)
},
Err(e) => {
error!("Failed to parse HTTP request. Error: {}", e);
Err(failure::err_msg(format!("Failed to parse HTTP request: {}", e)))
},
}
}

pub struct ObservableHttpPackage {
http_request: Option<ObservableHttpRequest>,
}

#[derive(Debug)]
pub struct ObservableHttpRequest {
pub lang: Option<String>,
pub user_agent: Option<String>,
pub signature: http::Signature,
//pub signature: http::Signature,
}
6 changes: 3 additions & 3 deletions src/lib.rs
Original file line number Diff line number Diff line change
Expand Up @@ -169,7 +169,7 @@ impl<'a> P0f<'a> {
freq: update.freq,
});

let http_request =
/* let http_request =
observable_package
.http_request
.map(|http_request| HttpRequestOutput {
Expand All @@ -182,9 +182,9 @@ impl<'a> P0f<'a> {
.matching_by_http_request(&http_request.signature)
.map(|(label, _)| label.clone()),
sig: http_request.signature,
});
});*/

(syn, syn_ack, mtu, uptime, http_request)
(syn, syn_ack, mtu, uptime, None)
};

P0fOutput {
Expand Down

0 comments on commit e94f4c6

Please sign in to comment.