mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-04-30 09:29:58 -07:00
Refactor error types and add config file parsing
1. Main binary now parses a config toml and uses its paths for the output pcap and qmdl files 2. Previously we were relying on DiagDeviceError for several things that aren't a diag device. This modularizes that error in a way that both improves the error descriptions, and also allows for better separation of concerns.
This commit is contained in:
@@ -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<String>,
|
||||
pcap_path: Option<String>,
|
||||
}
|
||||
|
||||
#[derive(Debug)]
|
||||
struct Config {
|
||||
qmdl_path: String,
|
||||
pcap_path: String,
|
||||
}
|
||||
|
||||
fn parse_config<P>(path: P) -> Result<Config, WavehunterError> where P: AsRef<std::path::Path> {
|
||||
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<String> = 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)?;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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<String> = 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);
|
||||
},
|
||||
|
||||
@@ -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<T> = Result<T, DiagDeviceError>;
|
||||
|
||||
#[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<MessagesContainer> {
|
||||
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(())
|
||||
|
||||
@@ -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<u16> = Algorithm {
|
||||
pub const CRC_CCITT: Crc<u16> = Crc::<u16>::new(&CRC_CCITT_ALG);
|
||||
|
||||
pub trait DiagReader {
|
||||
fn get_next_messages_container(&mut self) -> DiagResult<MessagesContainer>;
|
||||
type Err;
|
||||
|
||||
fn read_response(&mut self) -> DiagResult<Vec<Message>> {
|
||||
fn get_next_messages_container(&mut self) -> Result<MessagesContainer, Self::Err>;
|
||||
|
||||
fn read_response(&mut self) -> Result<Vec<Message>, 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<Vec<Message>> {
|
||||
fn parse_response_container(&self, container: MessagesContainer) -> Result<Vec<Message>, 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) {
|
||||
|
||||
11
src/qmdl.rs
11
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<P>(path: P) -> DiagResult<Self> where P: AsRef<std::path::Path> {
|
||||
pub fn new<P>(path: P) -> std::io::Result<Self> where P: AsRef<std::path::Path> {
|
||||
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<P>(path: P) -> DiagResult<Self> where P: AsRef<std::path::Path> {
|
||||
pub fn new<P>(path: P) -> std::io::Result<Self> where P: AsRef<std::path::Path> {
|
||||
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<MessagesContainer> {
|
||||
type Err = std::io::Error;
|
||||
|
||||
fn get_next_messages_container(&mut self) -> std::io::Result<MessagesContainer> {
|
||||
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
|
||||
|
||||
Reference in New Issue
Block a user