Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
77 changes: 50 additions & 27 deletions Cargo.lock

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

15 changes: 5 additions & 10 deletions check/src/main.rs
Original file line number Diff line number Diff line change
Expand Up @@ -113,12 +113,8 @@ async fn analyze_pcap(pcap_path: &str, show_skipped: bool) {
async fn analyze_qmdl(qmdl_path: &str, show_skipped: bool) {
let mut harness = Harness::new_with_config(&AnalyzerConfig::default());
let qmdl_file = &mut File::open(&qmdl_path).await.expect("failed to open file");
let file_size = qmdl_file
.metadata()
.await
.expect("failed to get QMDL file metadata")
.len();
let mut qmdl_reader = QmdlReader::new(qmdl_file, Some(file_size as usize));
let compressed = qmdl_path.ends_with(".gz");
Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I believe it would be easier to sniff gzip magic bytes. Then the distinction between gzip vs non-gzip can happen within the reader entirely, it doesn't need extra params, and no other code needs to be touched.

let qmdl_reader = QmdlReader::new(qmdl_file, compressed, None);
let mut qmdl_stream = pin!(
qmdl_reader
.as_stream()
Expand All @@ -141,8 +137,9 @@ async fn pcapify(qmdl_path: &PathBuf) {
let qmdl_file = &mut File::open(&qmdl_path)
.await
.expect("failed to open qmdl file");
let compressed = qmdl_path.ends_with(".gz");
let qmdl_file_size = qmdl_file.metadata().await.unwrap().len();
let mut qmdl_reader = QmdlReader::new(qmdl_file, Some(qmdl_file_size as usize));
let mut qmdl_reader = QmdlReader::new(qmdl_file, compressed, Some(qmdl_file_size as usize));
Comment on lines +140 to +142
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the qmdl_file_size value won't work here if compressed is true right? it should probably be omitted like it was above

let mut pcap_path = qmdl_path.clone();
pcap_path.set_extension("pcapng");
let pcap_file = &mut File::create(&pcap_path)
Expand Down Expand Up @@ -197,9 +194,7 @@ async fn main() {
let name_str = name.to_str().unwrap();
let path = entry.path();
let path_str = path.to_str().unwrap();
// instead of relying on the QMDL extension, can we check if a file is
// QMDL by inspecting the contents?
if name_str.ends_with(".qmdl") {
if name_str.ends_with(".qmdl") || name_str.ends_with(".qmdl.gz") {
info!("**** Beginning analysis of {name_str}");
analyze_qmdl(path_str, args.show_skipped).await;
if args.pcapify {
Expand Down
2 changes: 1 addition & 1 deletion daemon/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,7 @@ futures-macro = "0.3.30"
include_dir = "0.7.3"
chrono = { version = "0.4.31", features = ["serde"] }
tokio-stream = { version = "0.1.14", default-features = false, features = ["io-util"] }
futures = { version = "0.3.30", default-features = false }
futures = { version = "0.3.32", default-features = false, features = ["std"] }
serde_json = "1.0.114"
image = { version = "0.25.1", default-features = false, features = ["png", "gif"] }
tempfile = "3.10.2"
Expand Down
13 changes: 3 additions & 10 deletions daemon/src/analysis.rs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,6 @@ use futures::TryStreamExt;
use log::{error, info};
use rayhunter::analysis::analyzer::{AnalyzerConfig, EventType, Harness};
use rayhunter::diag::{DataType, MessagesContainer};
use rayhunter::qmdl::QmdlReader;
use serde::Serialize;
use tokio::fs::File;
use tokio::io::{AsyncWriteExt, BufWriter};
Expand Down Expand Up @@ -135,7 +134,7 @@ async fn perform_analysis(
analyzer_config: &AnalyzerConfig,
) -> Result<(), String> {
info!("Opening QMDL and analysis file for {name}...");
let (analysis_file, qmdl_file) = {
let (analysis_file, qmdl_reader) = {
let mut qmdl_store = qmdl_store_lock.write().await;
let (entry_index, _) = qmdl_store
.entry_for_name(name)
Expand All @@ -144,23 +143,17 @@ async fn perform_analysis(
.clear_and_open_entry_analysis(entry_index)
.await
.map_err(|e| format!("{e:?}"))?;
let qmdl_file = qmdl_store
let qmdl_reader = qmdl_store
.open_entry_qmdl(entry_index)
.await
.map_err(|e| format!("{e:?}"))?;

(analysis_file, qmdl_file)
(analysis_file, qmdl_reader)
};

let mut analysis_writer = AnalysisWriter::new(analysis_file, analyzer_config)
.await
.map_err(|e| format!("{e:?}"))?;
let file_size = qmdl_file
.metadata()
.await
.expect("failed to get QMDL file metadata")
.len();
let mut qmdl_reader = QmdlReader::new(qmdl_file, Some(file_size as usize));
let mut qmdl_stream = pin::pin!(
qmdl_reader
.as_stream()
Expand Down
30 changes: 20 additions & 10 deletions daemon/src/diag.rs
Original file line number Diff line number Diff line change
Expand Up @@ -63,7 +63,7 @@ pub struct DiagTask {

enum DiagState {
Recording {
qmdl_writer: QmdlWriter<File>,
qmdl_writer: Box<QmdlWriter<File>>,
analysis_writer: Box<AnalysisWriter>,
},
Stopped,
Expand Down Expand Up @@ -143,7 +143,7 @@ impl DiagTask {
DiskSpaceCheck::Failed => {}
}

let (qmdl_file, analysis_file) = match qmdl_store.new_entry().await {
let (qmdl_gz_file, analysis_file) = match qmdl_store.new_entry().await {
Ok(files) => files,
Err(e) => {
let msg = format!("failed creating QMDL file entry: {e}");
Expand All @@ -152,7 +152,7 @@ impl DiagTask {
}
};
self.stop_current_recording().await;
let qmdl_writer = QmdlWriter::new(qmdl_file);
let qmdl_writer = Box::new(QmdlWriter::new(qmdl_gz_file));
let analysis_writer = match AnalysisWriter::new(analysis_file, &self.analyzer_config).await
{
Ok(writer) => Box::new(writer),
Expand Down Expand Up @@ -237,13 +237,23 @@ impl DiagTask {
let mut state = DiagState::Stopped;
std::mem::swap(&mut self.state, &mut state);
if let DiagState::Recording {
analysis_writer, ..
qmdl_writer,
analysis_writer,
..
} = state
{
analysis_writer
.close()
.await
.expect("failed to close analysis writer");
match (qmdl_writer.close().await, analysis_writer.close().await) {
(Ok(()), Ok(())) => {}
(qmdl_result, analysis_result) => {
if let Err(err) = qmdl_result {
error!("failed to close QmdlWriter: {:?}", err);
}
if let Err(err) = analysis_result {
error!("failed to close AnalysisWriter: {:?}", err);
}
panic!();
}
}
}
}

Expand Down Expand Up @@ -315,13 +325,13 @@ impl DiagTask {
}
debug!(
"total QMDL bytes written: {}, updating manifest...",
qmdl_writer.total_written
qmdl_writer.total_uncompressed_bytes
);
let index = qmdl_store
.current_entry
.expect("DiagDevice had qmdl_writer, but QmdlStore didn't have current entry???");
if let Err(e) = qmdl_store
.update_entry_qmdl_size(index, qmdl_writer.total_written)
.update_entry_qmdl_size(index, qmdl_writer.total_uncompressed_bytes)
.await
{
let reason = format!("failed to update manifest (disk full?): {e}");
Expand Down
16 changes: 4 additions & 12 deletions daemon/src/pcap.rs
Original file line number Diff line number Diff line change
Expand Up @@ -45,23 +45,20 @@ pub async fn get_pcap(
StatusCode::NOT_FOUND,
format!("couldn't find manifest entry with name {qmdl_name}"),
))?;
if entry.qmdl_size_bytes == 0 {
if entry.uncompressed_qmdl_size_bytes == 0 {
return Err((
StatusCode::SERVICE_UNAVAILABLE,
"QMDL file is empty, try again in a bit!".to_string(),
));
}
let qmdl_size_bytes = entry.qmdl_size_bytes;
let qmdl_file = qmdl_store
let qmdl_reader = qmdl_store
.open_entry_qmdl(entry_index)
.await
.map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{e:?}")))?;
// the QMDL reader should stop at the last successfully written data chunk
// (entry.size_bytes)
let (reader, writer) = duplex(1024);

tokio::spawn(async move {
if let Err(e) = generate_pcap_data(writer, qmdl_file, qmdl_size_bytes).await {
if let Err(e) = generate_pcap_data(writer, qmdl_reader).await {
error!("failed to generate PCAP: {e:?}");
}
});
Expand All @@ -71,19 +68,14 @@ pub async fn get_pcap(
Ok((headers, body).into_response())
}

pub async fn generate_pcap_data<R, W>(
writer: W,
qmdl_file: R,
qmdl_size_bytes: usize,
) -> Result<(), Error>
pub async fn generate_pcap_data<R, W>(writer: W, mut reader: QmdlReader<R>) -> Result<(), Error>
where
W: AsyncWrite + Unpin + Send,
R: AsyncRead + Unpin,
{
let mut pcap_writer = GsmtapPcapWriter::new(writer).await?;
pcap_writer.write_iface_header().await?;

let mut reader = QmdlReader::new(qmdl_file, Some(qmdl_size_bytes));
while let Some(container) = reader.get_next_messages_container().await? {
if container.data_type != DataType::UserSpace {
continue;
Expand Down
Loading
Loading