diff --git a/Cargo.lock b/Cargo.lock index b720e2e..7e46315 100644 --- a/Cargo.lock +++ b/Cargo.lock @@ -372,7 +372,7 @@ source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "7f4c021e1093a56626774e81216a4ce732a735e5bad4868a03f3ed65ca0c3919" dependencies = [ "once_cell", - "toml_edit", + "toml_edit 0.19.15", ] [[package]] @@ -441,6 +441,35 @@ dependencies = [ "windows-sys 0.52.0", ] +[[package]] +name = "serde" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "25dd9975e68d0cb5aa1120c288333fc98731bd1dd12f561e468ea4728c042b89" +dependencies = [ + "serde_derive", +] + +[[package]] +name = "serde_derive" +version = "1.0.193" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "43576ca501357b9b071ac53cdc7da8ef0cbd9493d8df094cd821777ea6e894d3" +dependencies = [ + "proc-macro2", + "quote", + "syn 2.0.39", +] + +[[package]] +name = "serde_spanned" +version = "0.6.5" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "eb3622f419d1296904700073ea6cc23ad690adbd66f13ea683df73298736f0c1" +dependencies = [ + "serde", +] + [[package]] name = "strsim" version = "0.10.0" @@ -504,11 +533,26 @@ dependencies = [ "syn 2.0.39", ] +[[package]] +name = "toml" +version = "0.8.8" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "a1a195ec8c9da26928f773888e0742ca3ca1040c6cd859c919c9f59c1954ab35" +dependencies = [ + "serde", + "serde_spanned", + "toml_datetime", + "toml_edit 0.21.0", +] + [[package]] name = "toml_datetime" version = "0.6.5" source = "registry+https://github.com/rust-lang/crates.io-index" checksum = "3550f4e9685620ac18a50ed434eb3aec30db8ba93b0287467bca5826ea25baf1" +dependencies = [ + "serde", +] [[package]] name = "toml_edit" @@ -521,6 +565,19 @@ dependencies = [ "winnow", ] +[[package]] +name = "toml_edit" +version = "0.21.0" +source = "registry+https://github.com/rust-lang/crates.io-index" +checksum = "d34d383cd00a163b4a5b85053df514d45bc330f6de7737edfe0a93311d1eaa03" +dependencies = [ + "indexmap", + "serde", + "serde_spanned", + "toml_datetime", + "winnow", +] + [[package]] name = "unicode-ident" version = "1.0.12" @@ -593,7 +650,9 @@ dependencies = [ "libc", "log", "pcap-file", + "serde", "thiserror", + "toml", ] [[package]] diff --git a/Cargo.toml b/Cargo.toml index 10c1465..54aeec0 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -27,4 +27,7 @@ libc = "0.2.150" log = "0.4.20" pcap-file = "2.0.0" thiserror = "1.0.50" +# TODO: split into binary-only dependencies +toml = "0.8.8" +serde = { version = "1.0.193", features = ["derive"] } diff --git a/src/bin/wavehunter.rs b/src/bin/wavehunter.rs index aa81e1f..68708a6 100644 --- a/src/bin/wavehunter.rs +++ b/src/bin/wavehunter.rs @@ -1,29 +1,86 @@ -use wavehunter::diag_device::{DiagDevice, DiagResult}; +use wavehunter::diag_device::{DiagDevice, DiagDeviceError}; use wavehunter::diag_reader::DiagReader; -use wavehunter::gsmtap_parser::GsmtapParser; -use wavehunter::pcap::PcapFile; +use wavehunter::gsmtap_parser::{GsmtapParser, GsmtapParserError}; +use wavehunter::pcap::{PcapFile, PcapFileError}; use log::debug; +use thiserror::Error; +use serde::Deserialize; +use toml; -fn main() -> DiagResult<()> { +#[derive(Error, Debug)] +enum WavehunterError { + #[error("Missing config file: {0}")] + MissingConfigFile(String), + #[error("Config file parsing error: {0}")] + ConfigFileParsingError(#[from] toml::de::Error), + #[error("Pcap file initialization error: {0}")] + PcapFileInitError(PcapFileError), + #[error("Pcap file write error: {0}")] + PcapFileWriteError(PcapFileError), + #[error("Diag intialization error: {0}")] + DiagInitError(DiagDeviceError), + #[error("Diag read error: {0}")] + DiagReadError(DiagDeviceError), + #[error("GSMTAP parsing error: {0}")] + GsmtapParsingError(GsmtapParserError), +} + +#[derive(Deserialize)] +struct ConfigFile { + qmdl_path: Option, + pcap_path: Option, +} + +#[derive(Debug)] +struct Config { + qmdl_path: String, + pcap_path: String, +} + +fn parse_config

(path: P) -> Result where P: AsRef { + let config_file = std::fs::read_to_string(&path) + .map_err(|_| WavehunterError::MissingConfigFile(format!("{:?}", path.as_ref())))?; + let parsed_config: ConfigFile = toml::from_str(&config_file) + .map_err(WavehunterError::ConfigFileParsingError)?; + Ok(Config { + qmdl_path: parsed_config.qmdl_path.unwrap_or("./wavehunter.qmdl".to_string()), + pcap_path: parsed_config.pcap_path.unwrap_or("./wavehunter.pcap".to_string()), + }) +} + +fn main() -> Result<(), WavehunterError> { env_logger::init(); - let mut dev = DiagDevice::new("./wavehunter.qmdl")?; - dev.config_logs()?; + let args: Vec = std::env::args().collect(); + if args.len() != 2 { + println!("Usage: {} /path/to/config/file", args[0]); + std::process::exit(1); + } + + let config = parse_config(&args[1])?; + + let mut dev = DiagDevice::new(&config.qmdl_path) + .map_err(WavehunterError::DiagInitError)?; + dev.config_logs() + .map_err(WavehunterError::DiagInitError)?; println!("The orca is hunting for stingrays..."); let mut gsmtap_parser = GsmtapParser::new(); // We are going to want to add a timestamp to this pcap file eventually - let mut pcap_file = PcapFile::new("./wavehunter.pcap").unwrap(); - pcap_file.write_iface_header().unwrap(); + let mut pcap_file = PcapFile::new(&config.pcap_path) + .map_err(WavehunterError::PcapFileInitError)?; + pcap_file.write_iface_header() + .map_err(WavehunterError::PcapFileWriteError)?; loop { - for msg in dev.read_response()? { + for msg in dev.read_response().map_err(WavehunterError::DiagReadError)? { debug!("msg: {:?}", msg); - if let Some((timestamp, gsmtap_msg)) = gsmtap_parser.recv_message(msg).unwrap() { + if let Some((timestamp, gsmtap_msg)) = gsmtap_parser.recv_message(msg).map_err(WavehunterError::GsmtapParsingError)? { debug!("gsmtap_msg: {:?}", gsmtap_msg); - pcap_file.write_gsmtap_message(gsmtap_msg, timestamp).unwrap(); + pcap_file.write_gsmtap_message(gsmtap_msg, timestamp) + .map_err(WavehunterError::PcapFileWriteError)?; } } } diff --git a/src/bin/wavehunter_reader.rs b/src/bin/wavehunter_reader.rs index e28a2a0..6293c3d 100644 --- a/src/bin/wavehunter_reader.rs +++ b/src/bin/wavehunter_reader.rs @@ -1,12 +1,11 @@ use wavehunter::qmdl::QmdlFileReader; use wavehunter::diag_reader::DiagReader; -use wavehunter::diag_device::{DiagResult, DiagDeviceError}; use wavehunter::gsmtap_parser::GsmtapParser; use wavehunter::pcap::PcapFile; use log::{debug, error}; -fn main() -> DiagResult<()> { +fn main() { env_logger::init(); let args: Vec = std::env::args().collect(); @@ -14,7 +13,7 @@ fn main() -> DiagResult<()> { error!("Usage: {} /path/to/qmdl/file", args[0]); std::process::exit(1); } - let mut qmdl_reader = QmdlFileReader::new(&args[1])?; + let mut qmdl_reader = QmdlFileReader::new(&args[1]).unwrap(); let mut gsmtap_parser = GsmtapParser::new(); let mut pcap_file = PcapFile::new("./wavehunter.pcap").unwrap(); @@ -31,7 +30,7 @@ fn main() -> DiagResult<()> { } } }, - Err(DiagDeviceError::IO(err)) if err.kind() == std::io::ErrorKind::UnexpectedEof => { + Err(err) if err.kind() == std::io::ErrorKind::UnexpectedEof => { println!("Reached end of QMDL file, exiting..."); std::process::exit(0); }, diff --git a/src/diag_device.rs b/src/diag_device.rs index 70fa0af..c6db4e4 100644 --- a/src/diag_device.rs +++ b/src/diag_device.rs @@ -1,4 +1,4 @@ -use crate::hdlc::{hdlc_encapsulate, HdlcError}; +use crate::hdlc::hdlc_encapsulate; use crate::diag::{Message, ResponsePayload, Request, LogConfigRequest, LogConfigResponse, build_log_mask_request, RequestContainer, DataType, MessagesContainer}; use crate::diag_reader::{DiagReader, CRC_CCITT}; use crate::qmdl::QmdlFileWriter; @@ -15,20 +15,24 @@ pub type DiagResult = Result; #[derive(Error, Debug)] pub enum DiagDeviceError { - #[error("IO error {0}")] - IO(#[from] std::io::Error), #[error("Failed to initialize /dev/diag: {0}")] InitializationFailed(String), #[error("Failed to read diag device: {0}")] - DeviceReadFailed(String), + DeviceReadFailed(std::io::Error), + #[error("Failed to write diag device: {0}")] + DeviceWriteFailed(String), #[error("Nonzero status code {0} for diag request: {1:?}")] RequestFailed(u32, Request), #[error("Didn't receive response for request: {0:?}")] NoResponse(Request), - #[error("HDLC error {0}")] - HdlcError(#[from] HdlcError), - #[error("Deku error {0}")] - DekuError(#[from] DekuError), + #[error("Failed to open QMDL file: {0}")] + OpenQmdlFileError(std::io::Error), + #[error("Failed to write to QMDL file: {0}")] + QmdlFileWriteError(std::io::Error), + #[error("Failed to open diag device: {0}")] + OpenDiagDeviceError(std::io::Error), + #[error("Failed to parse MessagesContainer: {0}")] + ParseMessagesContainerError(deku::DekuError), } pub const LOG_CODES_FOR_RAW_PACKET_LOGGING: [u32; 11] = [ @@ -65,16 +69,21 @@ pub struct DiagDevice { } impl DiagReader for DiagDevice { + type Err = DiagDeviceError; + fn get_next_messages_container(&mut self) -> DiagResult { let mut bytes_read = 0; while bytes_read == 0 { - bytes_read = self.file.read(&mut self.read_buf)?; + bytes_read = self.file.read(&mut self.read_buf) + .map_err(DiagDeviceError::DeviceReadFailed)?; } - let ((leftover_bytes, _), container) = MessagesContainer::from_bytes((&self.read_buf[0..bytes_read], 0))?; + let ((leftover_bytes, _), container) = MessagesContainer::from_bytes((&self.read_buf[0..bytes_read], 0)) + .map_err(DiagDeviceError::ParseMessagesContainerError)?; if leftover_bytes.len() > 0 { warn!("warning: {} leftover bytes when parsing MessagesContainer", leftover_bytes.len()); } - self.qmdl_file.write_container(&container)?; + self.qmdl_file.write_container(&container) + .map_err(DiagDeviceError::QmdlFileWriteError)?; Ok(container) } } @@ -84,10 +93,12 @@ impl DiagDevice { let diag_file = std::fs::File::options() .read(true) .write(true) - .open("/dev/diag")?; + .open("/dev/diag") + .map_err(DiagDeviceError::OpenDiagDeviceError)?; let fd = diag_file.as_raw_fd(); - let qmdl_file = QmdlFileWriter::new(qmdl_path)?; + let qmdl_file = QmdlFileWriter::new(qmdl_path) + .map_err(DiagDeviceError::OpenQmdlFileError)?; enable_frame_readwrite(fd, MEMORY_DEVICE_MODE)?; let use_mdm = determine_use_mdm(fd)?; @@ -101,19 +112,20 @@ impl DiagDevice { } pub fn write_request(&mut self, req: &Request) -> DiagResult<()> { + let req_bytes = &req.to_bytes().expect("Failed to serialize Request"); let buf = RequestContainer { data_type: DataType::UserSpace, use_mdm: self.use_mdm > 0, mdm_field: -1, - hdlc_encapsulated_request: hdlc_encapsulate(&req.to_bytes()?, &CRC_CCITT), - }.to_bytes()?; + hdlc_encapsulated_request: hdlc_encapsulate(&req_bytes, &CRC_CCITT), + }.to_bytes().expect("Failed to serialize RequestContainer"); unsafe { let fd = self.file.as_raw_fd(); let buf_ptr = buf.as_ptr() as *const libc::c_void; let ret = libc::write(fd, buf_ptr, buf.len()); if ret < 0 { let msg = format!("write failed with error code {}", ret); - return Err(DiagDeviceError::DeviceReadFailed(msg)); + return Err(DiagDeviceError::DeviceWriteFailed(msg)); } } Ok(()) diff --git a/src/diag_reader.rs b/src/diag_reader.rs index 3a1cfaa..0b6ed3e 100644 --- a/src/diag_reader.rs +++ b/src/diag_reader.rs @@ -1,6 +1,5 @@ use crate::diag; use crate::{diag::*, hdlc::hdlc_decapsulate}; -use crate::diag_device::DiagResult; use crc::{Crc, Algorithm}; use deku::prelude::*; @@ -21,9 +20,11 @@ pub const CRC_CCITT_ALG: Algorithm = Algorithm { pub const CRC_CCITT: Crc = Crc::::new(&CRC_CCITT_ALG); pub trait DiagReader { - fn get_next_messages_container(&mut self) -> DiagResult; + type Err; - fn read_response(&mut self) -> DiagResult> { + fn get_next_messages_container(&mut self) -> Result; + + fn read_response(&mut self) -> Result, Self::Err> { loop { let container = self.get_next_messages_container()?; if container.data_type == DataType::UserSpace { @@ -34,7 +35,7 @@ pub trait DiagReader { } } - fn parse_response_container(&self, container: MessagesContainer) -> DiagResult> { + fn parse_response_container(&self, container: MessagesContainer) -> Result, Self::Err> { let mut result = Vec::new(); for msg in container.messages { for sub_msg in msg.data.split_inclusive(|&b| b == diag::MESSAGE_TERMINATOR) { diff --git a/src/qmdl.rs b/src/qmdl.rs index fdc17aa..d2e9f6e 100644 --- a/src/qmdl.rs +++ b/src/qmdl.rs @@ -2,7 +2,6 @@ //! just a series of of concatenated HDLC encapsulated diag::Message structs. use crate::diag_reader::DiagReader; -use crate::diag_device::DiagResult; use crate::diag::{MessagesContainer, MESSAGE_TERMINATOR, HdlcEncapsulatedMessage, DataType}; use std::fs::File; @@ -14,7 +13,7 @@ pub struct QmdlFileWriter { } impl QmdlFileWriter { - pub fn new

(path: P) -> DiagResult where P: AsRef { + pub fn new

(path: P) -> std::io::Result where P: AsRef { let file = std::fs::File::options() .create(true) .append(true) @@ -25,7 +24,7 @@ impl QmdlFileWriter { }) } - pub fn write_container(&mut self, container: &MessagesContainer) -> DiagResult<()> { + pub fn write_container(&mut self, container: &MessagesContainer) -> std::io::Result<()> { for msg in &container.messages { self.file.write_all(&msg.data)?; self.total_written += msg.data.len(); @@ -40,7 +39,7 @@ pub struct QmdlFileReader { } impl QmdlFileReader { - pub fn new

(path: P) -> DiagResult where P: AsRef { + pub fn new

(path: P) -> std::io::Result where P: AsRef { let file = std::fs::File::options() .read(true) .open(path)?; @@ -52,7 +51,9 @@ impl QmdlFileReader { } impl DiagReader for QmdlFileReader { - fn get_next_messages_container(&mut self) -> DiagResult { + type Err = std::io::Error; + + fn get_next_messages_container(&mut self) -> std::io::Result { let bytes_read = self.file.read_until(MESSAGE_TERMINATOR, &mut self.buf)?; // Since QMDL is just a flat list of messages, we can't actually