use super::common::{AnalyzerProcessor, BinaryExecutor, parse_error_event};
use super::{EventStream, Runner, RunnerError};
use crate::framework::analyzers::Analyzer;
use crate::framework::core::Event;
use async_trait::async_trait;
use futures::stream::StreamExt;
use std::path::Path;
use std::sync::{Arc, atomic::AtomicU64};
pub struct SslRunner {
analyzers: Vec<Box<dyn Analyzer>>,
executor: BinaryExecutor,
additional_args: Vec<String>,
}
impl SslRunner {
pub fn from_binary_extractor(binary_path: impl AsRef<Path>) -> Self {
let path_str = binary_path.as_ref().to_string_lossy().to_string();
Self {
analyzers: Vec::new(),
executor: BinaryExecutor::new(path_str).with_runner_name("SSL".to_string()),
additional_args: Vec::new(),
}
}
pub fn with_args<I, S>(mut self, args: I) -> Self
where
I: IntoIterator<Item = S>,
S: AsRef<str>,
{
self.additional_args = args.into_iter().map(|s| s.as_ref().to_string()).collect();
self.executor = self
.executor
.with_args(&self.additional_args)
.with_runner_name("SSL".to_string());
self
}
fn parse_ssl_event(json_value: serde_json::Value, errors: &AtomicU64) -> Event {
let Some(timestamp) = json_value.get("timestamp_ns").and_then(|v| v.as_u64()) else {
return parse_error_event("ssl", json_value, "missing timestamp_ns", errors);
};
let Some(pid) = json_value
.get("pid")
.and_then(|v| v.as_u64())
.map(|p| p as u32)
else {
return parse_error_event("ssl", json_value, "missing pid", errors);
};
let Some(comm) = json_value
.get("comm")
.and_then(|v| v.as_str())
.map(str::to_string)
else {
return parse_error_event("ssl", json_value, "missing comm", errors);
};
Event::new_with_timestamp(timestamp, "ssl".to_string(), pid, comm, json_value)
}
}
#[async_trait]
impl Runner for SslRunner {
async fn run(&mut self) -> Result<EventStream, RunnerError> {
let json_stream = self.executor.get_json_stream().await?;
let errors = Arc::new(AtomicU64::new(0));
let event_stream =
json_stream.map(move |json_value| Self::parse_ssl_event(json_value, &errors));
AnalyzerProcessor::process_through_analyzers(Box::pin(event_stream), &mut self.analyzers)
.await
}
fn add_analyzer(mut self, analyzer: Box<dyn Analyzer>) -> Self {
self.analyzers.push(analyzer);
self
}
}
#[cfg(test)]
mod tests {
use super::*;
#[tokio::test]
#[ignore = "requires real binary and may need sudo privileges"]
async fn test_ssl_runner_with_real_binary() {
use std::path::Path;
use std::time::Duration;
use tokio::time::timeout;
let _ = env_logger::Builder::from_default_env()
.filter_level(log::LevelFilter::Debug)
.is_test(true)
.try_init();
let binary_path = "../src/sslsniff";
if !Path::new(binary_path).exists() {
return;
}
let mut runner = SslRunner::from_binary_extractor(binary_path);
if let Ok(mut stream) = runner.run().await {
let _ = timeout(Duration::from_secs(30), async {
while futures::StreamExt::next(&mut stream).await.is_some() {}
})
.await;
}
}
}