use crate::{
packet::Packet,
xml::{ParserResult, RTSharkParser},
};
use quick_xml::Reader;
#[cfg(target_family = "unix")]
use std::os::unix::process::ExitStatusExt;
use tokio::{
io::{AsyncBufRead, AsyncBufReadExt},
process::Child,
};
pub struct RTSharkAsync {
process: Option<tokio::process::Child>,
parser: quick_xml::Reader<tokio::io::BufReader<tokio::process::ChildStdout>>,
stderr: tokio::io::BufReader<tokio::process::ChildStderr>,
blacklist: Vec<String>,
whitelist: Vec<String>,
}
impl RTSharkAsync {
pub(crate) fn new(
mut process: tokio::process::Child,
blacklist: Vec<String>,
whitelist: Vec<String>,
) -> Self {
let buf_reader = tokio::io::BufReader::new(process.stdout.take().unwrap());
let stderr = tokio::io::BufReader::new(process.stderr.take().unwrap());
let parser = quick_xml::Reader::from_reader(buf_reader);
RTSharkAsync {
process: Some(process),
parser,
stderr,
blacklist,
whitelist,
}
}
pub async fn read(&mut self) -> std::io::Result<Option<Packet>> {
let ret = RTSharkAsync::parse(&mut self.parser, &self.blacklist, &self.whitelist).await?;
if ret.is_none() {
self.on_eof().await?;
}
Ok(ret)
}
async fn on_eof(&mut self) -> std::io::Result<()> {
let done = match self.process {
Some(ref mut process) => RTSharkAsync::try_wait_has_exited(process),
_ => true,
};
if done {
self.process = None;
let mut line = String::new();
let size = self.stderr.read_line(&mut line).await?;
if size != 0 {
return Err(std::io::Error::new(std::io::ErrorKind::InvalidInput, line));
}
}
Ok(())
}
pub(crate) async fn parse<B: AsyncBufRead + Unpin>(
reader: &mut Reader<B>,
blacklist: &[String],
whitelist: &[String],
) -> std::io::Result<Option<Packet>> {
let mut parser = RTSharkParser::new();
let mut buf = vec![];
loop {
let event = reader.read_event_into_async(&mut buf).await.map_err(|e| {
std::io::Error::new(
std::io::ErrorKind::InvalidInput,
format!("cant parse xml: {e}"),
)
})?;
match parser.parse(event, blacklist, whitelist)? {
ParserResult::Continue => (),
ParserResult::Packet(packet) => return Ok(Some(packet)),
ParserResult::Eof => return Ok(None),
}
}
}
pub async fn kill(&mut self) {
if let Some(ref mut process) = self.process {
let done = match process.try_wait() {
Ok(maybe) => match maybe {
None => false,
Some(_exitcode) => true,
},
Err(e) => {
eprintln!("Error while killing rtshark: wait: {e}");
false
}
};
if !done {
match process.kill().await {
Ok(()) => (),
Err(e) => eprintln!("Error while killing rtshark: kill: {e}"),
}
if let Err(e) = process.wait().await {
eprintln!("Error while killing rtshark: wait: {e}");
}
}
self.process = None;
}
}
pub fn pid(&self) -> Option<u32> {
self.process.as_ref().map(|p| p.id())?
}
fn try_wait_has_exited(child: &mut Child) -> bool {
let mut count = 3;
while count != 0 {
#[cfg(target_family = "unix")]
if let Ok(Some(s)) = child.try_wait() {
return s.code().is_some() || s.signal().is_some();
}
#[cfg(target_family = "windows")]
if let Ok(Some(s)) = child.try_wait() {
return s.code().is_some();
}
std::thread::sleep(std::time::Duration::from_millis(100));
count -= 1;
}
false
}
}
#[cfg(test)]
mod tests {
use std::io::Write;
use serial_test::serial;
use tokio::runtime::Runtime;
use crate::RTSharkBuilder;
#[tokio::test]
async fn test_async_read() {
let pcap_path = std::path::Path::new(env!("CARGO_MANIFEST_DIR"))
.join("assets")
.join("test_tls.pcap");
println!("{pcap_path:?}");
assert!(pcap_path.exists());
let mut rtshark = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.capture_filter("tcp")
.spawn_async()
.unwrap();
let mut tls_counter = 0;
let mut time_counter = 0;
let mut running = true;
while running {
tokio::join!(
async {
match rtshark.read().await {
Ok(Some(packet)) => {
if let Some(_tls) = packet.layer_name("tls") {
tls_counter += 1;
println!("TLS packet count: {tls_counter}");
}
}
Ok(None) => {
println!("End of capture stream");
running = false;
}
Err(e) => {
eprintln!("Error parsing tshark output: {e}");
running = false;
}
}
},
async {
tokio::time::sleep(tokio::time::Duration::from_millis(50)).await;
time_counter += 1;
println!("Time elapsed: {time_counter} seconds");
}
);
}
assert_eq!(tls_counter, 27);
assert!(time_counter >= 49);
}
#[test]
fn test_rtshark_input_pcap() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder().input_path(pcap_path.to_str().unwrap());
Runtime::new().unwrap().block_on(async {
let mut rtshark = builder.spawn_async().unwrap();
match rtshark.read().await.unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
loop {
match rtshark.read().await.unwrap() {
None => break,
Some(_) => todo!(),
}
}
rtshark.kill().await;
assert!(rtshark.pid().is_none());
});
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_input_pcap_decode_as() {
let pcap = include_bytes!("../assets/rtp.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("rtp.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder().input_path(pcap_path.to_str().unwrap());
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("rtp").is_none()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
assert!(rtshark.pid().is_none());
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.decode_as("udp.port==6000,rtp");
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("rtp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
assert!(rtshark.pid().is_none());
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_input_pcap_display_filter() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.display_filter("udp.port == 53");
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.display_filter("tcp.port == 80");
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
None => (),
_ => panic!("invalid Output type"),
}
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_input_pcap_blacklist() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.metadata_blacklist("ip.src");
let mut rtshark = builder.spawn().unwrap();
let pkt = match rtshark.read().unwrap() {
Some(p) => p,
_ => panic!("invalid Output type"),
};
let ip = pkt.layer_name("ip").unwrap();
assert!(ip.metadata("ip.src").is_none());
assert!(ip.metadata("ip.dst").unwrap().value().eq("127.0.0.1"));
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_input_pcap_whitelist() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.metadata_whitelist("ip.dst");
let mut rtshark = builder.spawn().unwrap();
let pkt = match rtshark.read().unwrap() {
Some(p) => p,
_ => panic!("invalid Output type"),
};
let ip = pkt.layer_name("ip").unwrap();
assert!(ip.metadata("ip.src").is_none());
assert!(ip.metadata("ip.dst").unwrap().value().eq("127.0.0.1"));
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_input_pcap_multiple_whitelist() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.metadata_whitelist("ip.src")
.metadata_whitelist("ip.dst");
let mut rtshark = builder.spawn().unwrap();
let pkt = match rtshark.read().unwrap() {
Some(p) => p,
_ => panic!("invalid Output type"),
};
let ip = pkt.layer_name("ip").unwrap();
assert!(ip.metadata("ip.src").unwrap().value().eq("127.0.0.1"));
assert!(ip.metadata("ip.dst").unwrap().value().eq("127.0.0.1"));
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_input_pcap_whitelist_multiple_layer() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.metadata_whitelist("ip.src")
.metadata_whitelist("udp.dstport");
let mut rtshark = builder.spawn().unwrap();
let pkt = match rtshark.read().unwrap() {
Some(p) => p,
_ => panic!("invalid Output type"),
};
let ip = pkt.layer_name("ip").unwrap();
assert!(ip.metadata("ip.src").unwrap().value().eq("127.0.0.1"));
let ip = pkt.layer_name("udp").unwrap();
assert!(ip.metadata("udp.dstport").unwrap().value().eq("53"));
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_input_pcap_whitelist_missing_attr() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.metadata_whitelist("nosuchproto.nosuchmetadata");
let mut rtshark = builder.spawn().unwrap();
let ret = rtshark.read();
assert!(ret.is_err());
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_input_fifo() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_fifo").unwrap();
let fifo_path = tmp_dir.path().join("pcap.pipe");
nix::unistd::mkfifo(&fifo_path, nix::sys::stat::Mode::S_IRWXU)
.expect("Error creating fifo");
let builder = RTSharkBuilder::builder()
.input_path(fifo_path.to_str().unwrap())
.live_capture();
let mut rtshark = builder.spawn().unwrap();
let mut output = std::fs::OpenOptions::new()
.write(true)
.open(&fifo_path)
.expect("unable to open fifo");
output.write_all(pcap).expect("unable to write in fifo");
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
assert!(rtshark.pid().is_none());
tmp_dir.close().expect("Error deleting fifo dir");
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_input_multiple_fifo() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_fifo").unwrap();
let fifo_path1 = tmp_dir.path().join("pcap1.pipe");
let fifo_path2 = tmp_dir.path().join("pcap2.pipe");
nix::unistd::mkfifo(&fifo_path1, nix::sys::stat::Mode::S_IRWXU)
.expect("Error creating fifo");
nix::unistd::mkfifo(&fifo_path2, nix::sys::stat::Mode::S_IRWXU)
.expect("Error creating fifo");
let builder = RTSharkBuilder::builder()
.input_path(fifo_path1.to_str().unwrap())
.input_path(fifo_path2.to_str().unwrap())
.live_capture();
let mut rtshark = builder.spawn().unwrap();
let mut output = std::fs::OpenOptions::new()
.write(true)
.open(&fifo_path1)
.expect("unable to open fifo");
output.write_all(pcap).expect("unable to write in fifo");
let mut output = std::fs::OpenOptions::new()
.write(true)
.open(&fifo_path2)
.expect("unable to open fifo");
output.write_all(pcap).expect("unable to write in fifo");
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
assert!(rtshark.pid().is_none());
tmp_dir.close().expect("Error deleting fifo dir");
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_input_pcap_filter_pcap() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_fifo").unwrap();
let fifo_path = tmp_dir.path().join("pcap.pipe");
nix::unistd::mkfifo(&fifo_path, nix::sys::stat::Mode::S_IRWXU)
.expect("Error creating fifo");
let builder = RTSharkBuilder::builder()
.input_path(fifo_path.to_str().unwrap())
.live_capture()
.capture_filter("port 53");
let mut rtshark = builder.spawn().unwrap();
let mut output = std::fs::OpenOptions::new()
.write(true)
.open(&fifo_path)
.expect("unable to open fifo");
output.write_all(pcap).expect("unable to write in fifo");
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
assert!(rtshark.pid().is_none());
tmp_dir.close().expect("Error deleting fifo dir");
}
#[cfg(all(target_family = "unix", not(target_os = "macos")))]
#[test]
fn test_rtshark_drop() {
let tmp_dir = tempdir::TempDir::new("test_fifo").unwrap();
let fifo_path = tmp_dir.path().join("pcap.pipe");
nix::unistd::mkfifo(&fifo_path, nix::sys::stat::Mode::S_IRWXU)
.expect("Error creating fifo");
let builder = RTSharkBuilder::builder()
.input_path(fifo_path.to_str().unwrap())
.live_capture();
let pid = {
let rtshark = builder.spawn().unwrap();
let pid = rtshark.pid().unwrap();
assert!(std::path::Path::new(&format!("/proc/{pid}")).exists());
pid
};
assert!(!std::path::Path::new(&format!("/proc/{pid}")).exists());
tmp_dir.close().expect("Error deleting fifo dir");
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_killed() {
let tmp_dir = tempdir::TempDir::new("test_fifo").unwrap();
let fifo_path = tmp_dir.path().join("pcap.pipe");
nix::unistd::mkfifo(&fifo_path, nix::sys::stat::Mode::S_IRWXU)
.expect("Error creating fifo");
let builder = RTSharkBuilder::builder()
.input_path(fifo_path.to_str().unwrap())
.live_capture();
let mut rtshark = builder.spawn().unwrap();
nix::sys::signal::kill(
nix::unistd::Pid::from_raw(rtshark.pid().unwrap() as libc::pid_t),
nix::sys::signal::Signal::SIGKILL,
)
.unwrap();
match rtshark.read().unwrap() {
None => (),
_ => panic!("invalid Output type"),
}
tmp_dir.close().expect("Error deleting fifo dir");
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_fifo_lost() {
let tmp_dir = tempdir::TempDir::new("test_fifo").unwrap();
let fifo_path = tmp_dir.path().join("pcap.pipe");
nix::unistd::mkfifo(&fifo_path, nix::sys::stat::Mode::S_IRWXU)
.expect("Error creating fifo");
let builder = RTSharkBuilder::builder()
.input_path(fifo_path.to_str().unwrap())
.live_capture();
let mut rtshark = builder.spawn().unwrap();
tmp_dir.close().expect("Error deleting fifo dir");
loop {
match rtshark.read() {
Ok(e) if e.is_some() => panic!("invalid Output type"),
Ok(e) if e.is_none() => break,
_ => (),
}
}
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_fifo_opened_then_closed() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_fifo").unwrap();
let fifo_path = tmp_dir.path().join("pcap.pipe");
nix::unistd::mkfifo(&fifo_path, nix::sys::stat::Mode::S_IRWXU)
.expect("Error creating fifo");
let builder = RTSharkBuilder::builder()
.input_path(fifo_path.to_str().unwrap())
.live_capture();
let mut rtshark = builder.spawn().unwrap();
{
let mut output = std::fs::OpenOptions::new()
.write(true)
.open(&fifo_path)
.expect("unable to open fifo");
output.write_all(pcap).expect("unable to write in fifo");
}
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_file_missing() {
let builder = RTSharkBuilder::builder().input_path("/missing/rtshark/fifo");
let ret = builder.spawn();
match ret {
Ok(_) => panic!("We can't start if file is missing"),
Err(e) => eprintln!("{e}"),
}
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_set_options() {
let pcap = include_bytes!("../assets/tcp_fragmentation.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.option("tcp.relative_sequence_numbers:true");
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => {
let tcp = p.layer_name("tcp").expect("tcp layer");
if !tcp.iter().any(|md| {
if let Some(display) = md.display() {
display.contains("relative sequence number")
} else {
false
}
}) {
panic!("expected relative sequence number")
}
}
e => panic!("invalid Output type: {e:?}"),
}
rtshark.kill();
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.option("tcp.relative_sequence_numbers:false");
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => {
let tcp = p.layer_name("tcp").expect("tcp layer");
if tcp.iter().any(|md| {
if let Some(display) = md.display() {
display.contains("relative sequence number")
} else {
false
}
}) {
panic!("expected no relative sequence numbers")
}
}
e => panic!("invalid Output type: {e:?}"),
}
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_set_disabled_protocols() {
let pcap = include_bytes!("../assets/tcp_fragmentation.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.disable_protocol("tcp")
.disable_protocol("sip");
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => {
assert!(p.layer_name("tcp").is_none());
assert!(p.layer_name("sip").is_none());
}
e => panic!("invalid Output type: {e:?}"),
}
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_set_enabled_protocols() {
let pcap = include_bytes!("../assets/tcp_fragmentation.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.disable_protocol("ALL")
.enable_protocol("eth")
.enable_protocol("ip");
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => {
assert!(p.layer_name("tcp").is_none());
assert!(p.layer_name("sip").is_none());
assert!(p.layer_name("ip").is_some());
}
e => panic!("invalid Output type: {e:?}"),
}
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
#[serial] fn test_rtshark_tshark_missing() {
let path = match std::env::var("PATH") {
Ok(v) => {
std::env::remove_var("PATH");
Some(v)
}
Err(_) => None,
};
let builder = RTSharkBuilder::builder()
.input_path("/missing/rtshark/fifo")
.live_capture()
.env_path("/invalid/path");
let ret = builder.spawn();
if let Some(v) = path {
std::env::set_var("PATH", v);
}
match ret {
Ok(_) => panic!("We can't start if tshark is missing"),
Err(e) => eprintln!("{e}"),
}
}
#[test]
fn test_rtshark_input_pcap_output_pcap() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let in_path = tmp_dir.path().join("in.pcap");
let mut output = std::fs::File::create(&in_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let out_path = tmp_dir.path().join("out.pcap");
let builder = RTSharkBuilder::builder()
.input_path(in_path.to_str().unwrap())
.output_path(out_path.to_str().unwrap());
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
loop {
match rtshark.read().unwrap() {
None => break,
Some(_) => todo!(),
}
}
rtshark.kill();
assert!(rtshark.pid().is_none());
let mut rtshark = RTSharkBuilder::builder()
.input_path(out_path.to_str().unwrap())
.spawn()
.unwrap();
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[cfg(target_family = "unix")]
#[test]
fn test_rtshark_input_fifo_output_pcap() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_fifo").unwrap();
let fifo_path = tmp_dir.path().join("pcap.pipe");
nix::unistd::mkfifo(&fifo_path, nix::sys::stat::Mode::S_IRWXU)
.expect("Error creating fifo");
let out_path = tmp_dir.path().join("out.pcap");
let builder = RTSharkBuilder::builder()
.input_path(fifo_path.to_str().unwrap())
.output_path(out_path.to_str().unwrap())
.live_capture();
let mut rtshark = builder.spawn().unwrap();
let mut output = std::fs::OpenOptions::new()
.write(true)
.open(&fifo_path)
.expect("unable to open fifo");
output.write_all(pcap).expect("unable to write in fifo");
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
assert!(rtshark.pid().is_none());
let mut rtshark = RTSharkBuilder::builder()
.input_path(out_path.to_str().unwrap())
.spawn()
.unwrap();
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
#[serial] fn test_rtshark_multiple_spawn_pcap() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let in_path = tmp_dir.path().join("in.pcap");
let mut output = std::fs::File::create(&in_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let out_path = tmp_dir.path().join("out.pcap");
let builder = RTSharkBuilder::builder()
.input_path(in_path.to_str().unwrap())
.output_path(out_path.to_str().unwrap());
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => assert!(p.layer_name("udp").is_some()),
_ => panic!("invalid Output type"),
}
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_timestamp_micros() {
let pcap = include_bytes!("../assets/test.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let in_path = tmp_dir.path().join("in.pcap");
let mut output = std::fs::File::create(&in_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let out_path = tmp_dir.path().join("out.pcap");
let builder = RTSharkBuilder::builder()
.input_path(in_path.to_str().unwrap())
.output_path(out_path.to_str().unwrap());
let mut rtshark = builder.spawn().unwrap();
match rtshark.read().unwrap() {
Some(p) => assert_eq!(p.timestamp_micros(), Some(1652011560275852)),
_ => panic!("invalid Output type"),
}
rtshark.kill();
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_rtshark_tls_keylogfile_pcap() {
let pcap = include_bytes!("../assets/test_tls.pcap");
let keylog = include_bytes!("../assets/test_tlskeylogfile.txt");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder().input_path(pcap_path.to_str().unwrap());
let mut rtshark = builder.spawn().unwrap();
loop {
match rtshark.read().unwrap() {
None => break,
Some(p) => {
assert!(p.layer_name("tcp").is_some());
assert!(p.layer_name("http2").is_none())
}
}
}
rtshark.kill();
let keylog_path = tmp_dir.path().join("keylogfile.txt");
let mut output = std::fs::File::create(&keylog_path).expect("unable to open file");
output.write_all(keylog).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.keylog_file(keylog_path.as_os_str().to_str().unwrap());
let mut rtshark = builder.spawn().unwrap();
let mut http2_found = false;
loop {
match rtshark.read().unwrap() {
None => break,
Some(p) => {
assert!(p.layer_name("tcp").is_some());
if p.layer_name("http2").is_some() {
http2_found = true;
}
}
}
}
assert!(http2_found);
rtshark.kill();
assert!(rtshark.pid().is_none());
tmp_dir.close().expect("Error deleting fifo dir");
}
#[test]
fn test_reassembled_tcp() {
let pcap = include_bytes!("../assets/tcp_fragmentation.pcap");
let tmp_dir = tempdir::TempDir::new("test_pcap").unwrap();
let pcap_path = tmp_dir.path().join("file.pcap");
let mut output = std::fs::File::create(&pcap_path).expect("unable to open file");
output.write_all(pcap).expect("unable to write pcap");
output.flush().expect("unable to flush");
let builder = RTSharkBuilder::builder()
.input_path(pcap_path.to_str().unwrap())
.display_filter("tls.handshake.type == 1");
let mut rtshark = builder.spawn().unwrap();
loop {
match rtshark.read().unwrap() {
None => break,
Some(p) => {
let tcp = p.layer_name("tcp").expect("Missing tcp layer");
tcp.metadata("tcp.reassembled.data")
.expect("Missing metadata");
}
}
}
rtshark.kill();
assert!(rtshark.pid().is_none());
tmp_dir.close().expect("Error deleting fifo dir");
}
}