use std::fs;
use std::io::Write;
use std::os::unix::fs::PermissionsExt;
use std::path::{Path, PathBuf};
use std::sync::{Mutex, OnceLock};
use tempfile::TempDir;
use tokio::time::{Duration, sleep};
const PROCESS_BINARY: &[u8] = include_bytes!("../../vendor/bpf/process");
const SSLSNIFF_BINARY: &[u8] = include_bytes!("../../vendor/bpf/sslsniff");
const STDIOCAP_BINARY: &[u8] = include_bytes!("../../vendor/bpf/stdiocap");
pub struct BinaryExtractor {
_temp_dir: TempDir, pub process_path: PathBuf,
pub sslsniff_path: PathBuf,
stdiocap_init_lock: Mutex<()>,
stdiocap_path: OnceLock<PathBuf>,
}
impl BinaryExtractor {
pub async fn new() -> Result<Self, Box<dyn std::error::Error + Send + Sync>> {
println!("Creating temporary directory...");
let temp_dir = TempDir::new()?;
let temp_path = temp_dir.path();
println!("Created temporary directory: {}", temp_path.display());
let process_path = temp_path.join("process");
Self::extract_binary(&process_path, PROCESS_BINARY, "process").await?;
let sslsniff_path = temp_path.join("sslsniff");
Self::extract_binary(&sslsniff_path, SSLSNIFF_BINARY, "sslsniff").await?;
sleep(Duration::from_millis(100)).await;
Ok(Self {
_temp_dir: temp_dir,
process_path,
sslsniff_path,
stdiocap_init_lock: Mutex::new(()),
stdiocap_path: OnceLock::new(),
})
}
async fn extract_binary(
path: &Path,
binary_data: &[u8],
name: &str,
) -> Result<(), Box<dyn std::error::Error + Send + Sync>> {
{
let mut file = fs::File::create(path)?;
file.write_all(binary_data)?;
file.flush()?;
}
let mut perms = fs::metadata(path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(path, perms)?;
println!("Extracted {} binary to: {}", name, path.display());
Ok(())
}
pub fn get_process_path(&self) -> &Path {
&self.process_path
}
pub fn get_sslsniff_path(&self) -> &Path {
&self.sslsniff_path
}
pub fn get_stdiocap_path(&self) -> Result<&Path, Box<dyn std::error::Error + Send + Sync>> {
if let Some(path) = self.stdiocap_path.get() {
return Ok(path.as_path());
}
let _guard = self
.stdiocap_init_lock
.lock()
.map_err(|_| std::io::Error::other("stdiocap extraction lock poisoned"))?;
if let Some(path) = self.stdiocap_path.get() {
return Ok(path.as_path());
}
let stdiocap_path = self._temp_dir.path().join("stdiocap");
{
let mut file = fs::File::create(&stdiocap_path)?;
file.write_all(STDIOCAP_BINARY)?;
file.flush()?;
}
let mut perms = fs::metadata(&stdiocap_path)?.permissions();
perms.set_mode(0o755);
fs::set_permissions(&stdiocap_path, perms)?;
println!("Extracted stdiocap binary to: {}", stdiocap_path.display());
self.stdiocap_path
.set(stdiocap_path)
.map_err(|_| std::io::Error::other("stdiocap path initialized concurrently"))?;
Ok(self
.stdiocap_path
.get()
.expect("stdiocap path should be initialized")
.as_path())
}
}