diff --git a/bin/src/analysis.rs b/bin/src/analysis.rs index 62c2a12..a61a5d4 100644 --- a/bin/src/analysis.rs +++ b/bin/src/analysis.rs @@ -18,9 +18,9 @@ use tokio::sync::mpsc::Receiver; use tokio::sync::{RwLock, RwLockWriteGuard}; use tokio_util::task::TaskTracker; +use crate::dummy_analyzer::TestAnalyzer; use crate::qmdl_store::RecordingStore; use crate::server::ServerState; -use crate::dummy_analyzer::TestAnalyzer; pub struct AnalysisWriter { writer: BufWriter, @@ -53,7 +53,10 @@ impl AnalysisWriter { // Runs the analysis harness on the given container, serializing the results // to the analysis file and returning the file's new length. - pub async fn analyze(&mut self, container: MessagesContainer) -> Result<(usize, bool), std::io::Error> { + pub async fn analyze( + &mut self, + container: MessagesContainer, + ) -> Result<(usize, bool), std::io::Error> { let row = self.harness.analyze_qmdl_messages(container); if !row.is_empty() { self.write(&row).await?; @@ -182,7 +185,10 @@ pub fn run_analysis_thread( let count = queued_len(analysis_status_lock.clone()).await; for _ in 0..count { let name = dequeue_to_running(analysis_status_lock.clone()).await; - if let Err(err) = perform_analysis(&name, qmdl_store_lock.clone(), enable_dummy_analyzer).await { + if let Err(err) = + perform_analysis(&name, qmdl_store_lock.clone(), enable_dummy_analyzer) + .await + { error!("failed to analyze {}: {}", name, err); } clear_running(analysis_status_lock.clone()).await; diff --git a/bin/src/check.rs b/bin/src/check.rs index 1a2b106..9f1aac4 100644 --- a/bin/src/check.rs +++ b/bin/src/check.rs @@ -1,9 +1,15 @@ -use std::{collections::HashMap, future, path::PathBuf, pin::pin}; -use log::{info, warn}; -use rayhunter::{analysis::analyzer::{EventType, Harness}, diag::DataType, gsmtap_parser, pcap::GsmtapPcapWriter, qmdl::QmdlReader}; -use tokio::fs::{metadata, read_dir, File}; use clap::Parser; use futures::TryStreamExt; +use log::{info, warn}; +use rayhunter::{ + analysis::analyzer::{EventType, Harness}, + diag::DataType, + gsmtap_parser, + pcap::GsmtapPcapWriter, + qmdl::QmdlReader, +}; +use std::{collections::HashMap, future, path::PathBuf, pin::pin}; +use tokio::fs::{metadata, read_dir, File}; mod dummy_analyzer; @@ -28,15 +34,24 @@ struct Args { async fn analyze_file(harness: &mut Harness, qmdl_path: &str, show_skipped: bool) { 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 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!(qmdl_reader.as_stream() + let mut qmdl_stream = pin!(qmdl_reader + .as_stream() .try_filter(|container| future::ready(container.data_type == DataType::UserSpace))); let mut skipped_reasons: HashMap = HashMap::new(); let mut total_messages = 0; let mut warnings = 0; let mut skipped = 0; - while let Some(container) = qmdl_stream.try_next().await.expect("failed getting QMDL container") { + while let Some(container) = qmdl_stream + .try_next() + .await + .expect("failed getting QMDL container") + { let row = harness.analyze_qmdl_messages(container); total_messages += 1; for reason in row.skipped_message_reasons { @@ -50,18 +65,13 @@ async fn analyze_file(harness: &mut Harness, qmdl_path: &str, show_skipped: bool EventType::Informational => { info!( "{}: INFO - {} {}", - qmdl_path, - analysis.timestamp, - event.message, + qmdl_path, analysis.timestamp, event.message, ); } EventType::QualitativeWarning { severity } => { warn!( "{}: WARNING (Severity: {:?}) - {} {}", - qmdl_path, - severity, - analysis.timestamp, - event.message, + qmdl_path, severity, analysis.timestamp, event.message, ); warnings += 1; } @@ -75,22 +85,36 @@ async fn analyze_file(harness: &mut Harness, qmdl_path: &str, show_skipped: bool info!(" - {}: \"{}\"", count, reason); } } - info!("{}: {} messages analyzed, {} warnings, {} messages skipped", qmdl_path, total_messages, warnings, skipped); + info!( + "{}: {} messages analyzed, {} warnings, {} messages skipped", + qmdl_path, total_messages, warnings, skipped + ); } async fn pcapify(qmdl_path: &PathBuf) { - let qmdl_file = &mut File::open(&qmdl_path).await.expect("failed to open qmdl file"); + let qmdl_file = &mut File::open(&qmdl_path) + .await + .expect("failed to open qmdl file"); 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 pcap_path = qmdl_path.clone(); pcap_path.set_extension("pcap"); - let pcap_file = &mut File::create(&pcap_path).await.expect("failed to open pcap file"); + let pcap_file = &mut File::create(&pcap_path) + .await + .expect("failed to open pcap file"); let mut pcap_writer = GsmtapPcapWriter::new(pcap_file).await.unwrap(); pcap_writer.write_iface_header().await.unwrap(); - while let Some(container) = qmdl_reader.get_next_messages_container().await.expect("failed to get container") { + while let Some(container) = qmdl_reader + .get_next_messages_container() + .await + .expect("failed to get container") + { for msg in container.into_messages().into_iter().flatten() { if let Ok(Some((timestamp, parsed))) = gsmtap_parser::parse(msg) { - pcap_writer.write_gsmtap_message(parsed, timestamp).await.expect("failed to write"); + pcap_writer + .write_gsmtap_message(parsed, timestamp) + .await + .expect("failed to write"); } } } @@ -109,7 +133,8 @@ async fn main() { .with_colors(true) .without_timestamps() .with_level(level) - .init().unwrap(); + .init() + .unwrap(); let mut harness = Harness::new_with_all_analyzers(); if args.enable_dummy_analyzer { @@ -120,7 +145,9 @@ async fn main() { info!(" - {}: {}", analyzer.name, analyzer.description); } - let metadata = metadata(&args.qmdl_path).await.expect("failed to get metadata"); + let metadata = metadata(&args.qmdl_path) + .await + .expect("failed to get metadata"); if metadata.is_dir() { let mut dir = read_dir(&args.qmdl_path).await.expect("failed to read dir"); while let Some(entry) = dir.next_entry().await.expect("failed to get entry") { diff --git a/bin/src/config.rs b/bin/src/config.rs index fd0c49c..27d36fb 100644 --- a/bin/src/config.rs +++ b/bin/src/config.rs @@ -2,8 +2,7 @@ use crate::error::RayhunterError; use serde::Deserialize; -#[derive(Debug)] -#[derive(Deserialize)] +#[derive(Debug, Deserialize)] #[serde(default)] pub struct Config { pub qmdl_store_path: String, @@ -27,7 +26,10 @@ impl Default for Config { } } -pub fn parse_config

(path: P) -> Result where P: AsRef { +pub fn parse_config

(path: P) -> Result +where + P: AsRef, +{ if let Ok(config_file) = std::fs::read_to_string(&path) { Ok(toml::from_str(&config_file).map_err(RayhunterError::ConfigFileParsingError)?) } else { diff --git a/bin/src/daemon.rs b/bin/src/daemon.rs index caddfe5..ca65bf6 100644 --- a/bin/src/daemon.rs +++ b/bin/src/daemon.rs @@ -1,38 +1,43 @@ mod analysis; mod config; -mod error; -mod pcap; -mod server; -mod stats; -mod qmdl_store; mod diag; mod display; mod dummy_analyzer; +mod error; +mod pcap; +mod qmdl_store; +mod server; +mod stats; -use crate::config::{parse_config, parse_args}; +use crate::config::{parse_args, parse_config}; use crate::diag::run_diag_read_thread; -use crate::qmdl_store::RecordingStore; -use crate::server::{ServerState, get_qmdl, serve_static}; -use crate::pcap::get_pcap; -use crate::stats::get_system_stats; use crate::error::RayhunterError; +use crate::pcap::get_pcap; +use crate::qmdl_store::RecordingStore; +use crate::server::{get_qmdl, serve_static, ServerState}; +use crate::stats::get_system_stats; -use analysis::{get_analysis_status, run_analysis_thread, start_analysis, AnalysisCtrlMessage, AnalysisStatus}; +use analysis::{ + get_analysis_status, run_analysis_thread, start_analysis, AnalysisCtrlMessage, AnalysisStatus, +}; use axum::response::Redirect; -use diag::{delete_all_recordings, delete_recording, get_analysis_report, start_recording, stop_recording, DiagDeviceCtrlMessage}; -use log::{info, error}; -use qmdl_store::RecordingStoreError; -use rayhunter::diag_device::DiagDevice; use axum::routing::{get, post}; use axum::Router; +use diag::{ + delete_all_recordings, delete_recording, get_analysis_report, start_recording, stop_recording, + DiagDeviceCtrlMessage, +}; +use log::{error, info}; +use qmdl_store::RecordingStoreError; +use rayhunter::diag_device::DiagDevice; use stats::get_qmdl_manifest; +use std::net::SocketAddr; +use std::sync::Arc; +use tokio::net::TcpListener; use tokio::sync::mpsc::{self, Sender}; +use tokio::sync::{oneshot, RwLock}; use tokio::task::JoinHandle; use tokio_util::task::TaskTracker; -use std::net::SocketAddr; -use tokio::net::TcpListener; -use tokio::sync::{RwLock, oneshot}; -use std::sync::Arc; type AppRouter = Router>; @@ -70,7 +75,8 @@ async fn run_server( info!("The orca is hunting for stingrays..."); axum::serve(listener, app) .with_graceful_shutdown(server_shutdown_signal(server_shutdown_rx)) - .await.unwrap(); + .await + .unwrap(); }) } @@ -88,7 +94,9 @@ async fn init_qmdl_store(config: &config::Config) -> Result Result Err(err.into()), } } else { @@ -126,18 +134,24 @@ fn run_ctrl_c_thread( info!("Done!"); } - server_shutdown_tx.send(()) + server_shutdown_tx + .send(()) .expect("couldn't send server shutdown signal"); info!("sending UI shutdown"); if let Some(ui_shutdown_tx) = maybe_ui_shutdown_tx { - ui_shutdown_tx.send(()) + ui_shutdown_tx + .send(()) .expect("couldn't send ui shutdown signal"); } - diag_device_sender.send(DiagDeviceCtrlMessage::Exit).await + diag_device_sender + .send(DiagDeviceCtrlMessage::Exit) + .await .expect("couldn't send Exit message to diag thread"); - analysis_tx.send(AnalysisCtrlMessage::Exit).await + analysis_tx + .send(AnalysisCtrlMessage::Exit) + .await .expect("couldn't send Exit message to analysis thread"); - }, + } Err(err) => { error!("Unable to listen for shutdown signal: {}", err); } @@ -146,7 +160,6 @@ fn run_ctrl_c_thread( }) } - #[tokio::main] async fn main() -> Result<(), RayhunterError> { env_logger::init(); @@ -167,21 +180,43 @@ async fn main() -> Result<(), RayhunterError> { if !config.debug_mode { let (ui_shutdown_tx, ui_shutdown_rx) = oneshot::channel(); maybe_ui_shutdown_tx = Some(ui_shutdown_tx); - let mut dev = DiagDevice::new().await + let mut dev = DiagDevice::new() + .await .map_err(RayhunterError::DiagInitError)?; - dev.config_logs().await + dev.config_logs() + .await .map_err(RayhunterError::DiagInitError)?; info!("Starting Diag Thread"); - run_diag_read_thread(&task_tracker, dev, rx, ui_update_tx.clone(), qmdl_store_lock.clone(), config.enable_dummy_analyzer); + run_diag_read_thread( + &task_tracker, + dev, + rx, + ui_update_tx.clone(), + qmdl_store_lock.clone(), + config.enable_dummy_analyzer, + ); info!("Starting UI"); display::update_ui(&task_tracker, &config, ui_shutdown_rx, ui_update_rx); } let (server_shutdown_tx, server_shutdown_rx) = oneshot::channel::<()>(); info!("create shutdown thread"); let analysis_status_lock = Arc::new(RwLock::new(AnalysisStatus::default())); - run_analysis_thread(&task_tracker, analysis_rx, qmdl_store_lock.clone(), analysis_status_lock.clone(), config.enable_dummy_analyzer); - run_ctrl_c_thread(&task_tracker, tx.clone(), server_shutdown_tx, maybe_ui_shutdown_tx, qmdl_store_lock.clone(), analysis_tx.clone()); + run_analysis_thread( + &task_tracker, + analysis_rx, + qmdl_store_lock.clone(), + analysis_status_lock.clone(), + config.enable_dummy_analyzer, + ); + run_ctrl_c_thread( + &task_tracker, + tx.clone(), + server_shutdown_tx, + maybe_ui_shutdown_tx, + qmdl_store_lock.clone(), + analysis_tx.clone(), + ); let state = Arc::new(ServerState { qmdl_store_lock: qmdl_store_lock.clone(), diag_device_ctrl_sender: tx, diff --git a/bin/src/diag.rs b/bin/src/diag.rs index 1009c66..12f6561 100644 --- a/bin/src/diag.rs +++ b/bin/src/diag.rs @@ -6,21 +6,21 @@ use axum::extract::{Path, State}; use axum::http::header::CONTENT_TYPE; use axum::http::StatusCode; use axum::response::{IntoResponse, Response}; +use futures::{StreamExt, TryStreamExt}; +use log::{debug, error, info}; use rayhunter::diag::DataType; use rayhunter::diag_device::DiagDevice; -use tokio::sync::RwLock; -use tokio::sync::mpsc::{Receiver, Sender}; use rayhunter::qmdl::QmdlWriter; -use log::{debug, error, info}; use tokio::fs::File; +use tokio::sync::mpsc::{Receiver, Sender}; +use tokio::sync::RwLock; use tokio_util::io::ReaderStream; use tokio_util::task::TaskTracker; -use futures::{StreamExt, TryStreamExt}; +use crate::analysis::AnalysisWriter; use crate::display; use crate::qmdl_store::{RecordingStore, RecordingStoreError}; use crate::server::ServerState; -use crate::analysis::AnalysisWriter; pub enum DiagDeviceCtrlMessage { StopRecording, @@ -119,35 +119,82 @@ pub fn run_diag_read_thread( }); } -pub async fn start_recording(State(state): State>) -> Result<(StatusCode, String), (StatusCode, String)> { +pub async fn start_recording( + State(state): State>, +) -> Result<(StatusCode, String), (StatusCode, String)> { if state.debug_mode { return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string())); } let mut qmdl_store = state.qmdl_store_lock.write().await; - let (qmdl_file, analysis_file) = qmdl_store.new_entry().await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't create new qmdl entry: {}", e)))?; + let (qmdl_file, analysis_file) = qmdl_store.new_entry().await.map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't create new qmdl entry: {}", e), + ) + })?; let qmdl_writer = QmdlWriter::new(qmdl_file); - state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StartRecording((qmdl_writer, analysis_file))).await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?; + state + .diag_device_ctrl_sender + .send(DiagDeviceCtrlMessage::StartRecording(( + qmdl_writer, + analysis_file, + ))) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't send stop recording message: {}", e), + ) + })?; let display_state = display::DisplayState::Recording; - state.ui_update_sender.send(display_state).await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send ui update message: {}", e)))?; + state + .ui_update_sender + .send(display_state) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't send ui update message: {}", e), + ) + })?; Ok((StatusCode::ACCEPTED, "ok".to_string())) } -pub async fn stop_recording(State(state): State>) -> Result<(StatusCode, String), (StatusCode, String)> { +pub async fn stop_recording( + State(state): State>, +) -> Result<(StatusCode, String), (StatusCode, String)> { if state.debug_mode { return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string())); } let mut qmdl_store = state.qmdl_store_lock.write().await; - qmdl_store.close_current_entry().await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't close current qmdl entry: {}", e)))?; - state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StopRecording).await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?; - state.ui_update_sender.send(display::DisplayState::Paused).await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send ui update message: {}", e)))?; + qmdl_store.close_current_entry().await.map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't close current qmdl entry: {}", e), + ) + })?; + state + .diag_device_ctrl_sender + .send(DiagDeviceCtrlMessage::StopRecording) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't send stop recording message: {}", e), + ) + })?; + state + .ui_update_sender + .send(display::DisplayState::Paused) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't send ui update message: {}", e), + ) + })?; Ok((StatusCode::ACCEPTED, "ok".to_string())) } @@ -160,45 +207,98 @@ pub async fn delete_recording( } let mut qmdl_store = state.qmdl_store_lock.write().await; match qmdl_store.delete_entry(&qmdl_name).await { - Err(RecordingStoreError::NoSuchEntryError) => return Err((StatusCode::BAD_REQUEST, format!("no recording with name {qmdl_name}"))), - Err(e) => return Err((StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't delete recording: {e}"))), - Ok(_) => {}, + Err(RecordingStoreError::NoSuchEntryError) => { + return Err(( + StatusCode::BAD_REQUEST, + format!("no recording with name {qmdl_name}"), + )) + } + Err(e) => { + return Err(( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't delete recording: {e}"), + )) + } + Ok(_) => {} } - state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StopRecording).await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?; - state.ui_update_sender.send(display::DisplayState::Paused).await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send ui update message: {}", e)))?; + state + .diag_device_ctrl_sender + .send(DiagDeviceCtrlMessage::StopRecording) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't send stop recording message: {}", e), + ) + })?; + state + .ui_update_sender + .send(display::DisplayState::Paused) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't send ui update message: {}", e), + ) + })?; Ok((StatusCode::ACCEPTED, "ok".to_string())) } -pub async fn delete_all_recordings(State(state): State>) -> Result<(StatusCode, String), (StatusCode, String)> { +pub async fn delete_all_recordings( + State(state): State>, +) -> Result<(StatusCode, String), (StatusCode, String)> { if state.debug_mode { return Err((StatusCode::FORBIDDEN, "server is in debug mode".to_string())); } let mut qmdl_store = state.qmdl_store_lock.write().await; - qmdl_store.delete_all_entries().await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't delete all recordings: {}", e)))?; - state.diag_device_ctrl_sender.send(DiagDeviceCtrlMessage::StopRecording).await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send stop recording message: {}", e)))?; - state.ui_update_sender.send(display::DisplayState::Paused).await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("couldn't send ui update message: {}", e)))?; + qmdl_store.delete_all_entries().await.map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't delete all recordings: {}", e), + ) + })?; + state + .diag_device_ctrl_sender + .send(DiagDeviceCtrlMessage::StopRecording) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't send stop recording message: {}", e), + ) + })?; + state + .ui_update_sender + .send(display::DisplayState::Paused) + .await + .map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("couldn't send ui update message: {}", e), + ) + })?; Ok((StatusCode::ACCEPTED, "ok".to_string())) } -pub async fn get_analysis_report(State(state): State>, Path(qmdl_name): Path) -> Result { +pub async fn get_analysis_report( + State(state): State>, + Path(qmdl_name): Path, +) -> Result { let qmdl_store = state.qmdl_store_lock.read().await; let (entry_index, _) = if qmdl_name == "live" { qmdl_store.get_current_entry().ok_or(( StatusCode::SERVICE_UNAVAILABLE, - "No QMDL data's being recorded to analyze, try starting a new recording!".to_string() + "No QMDL data's being recorded to analyze, try starting a new recording!".to_string(), ))? } else { qmdl_store.entry_for_name(&qmdl_name).ok_or(( StatusCode::NOT_FOUND, - format!("Couldn't find QMDL entry with name \"{}\"", qmdl_name) + format!("Couldn't find QMDL entry with name \"{}\"", qmdl_name), ))? }; - let analysis_file = qmdl_store.open_entry_analysis(entry_index).await + let analysis_file = qmdl_store + .open_entry_analysis(entry_index) + .await .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("{:?}", e)))?; let analysis_stream = ReaderStream::new(analysis_file); diff --git a/bin/src/display/orbic.rs b/bin/src/display/orbic.rs index 84ab399..474ece5 100644 --- a/bin/src/display/orbic.rs +++ b/bin/src/display/orbic.rs @@ -1,9 +1,9 @@ use crate::config; +use crate::display::generic_framebuffer::{self, Dimensions, GenericFramebuffer}; use crate::display::DisplayState; -use crate::display::generic_framebuffer::{self, GenericFramebuffer, Dimensions}; -use tokio::sync::oneshot; use tokio::sync::mpsc::Receiver; +use tokio::sync::oneshot; use tokio_util::task::TaskTracker; const FB_PATH: &str = "/dev/fb0"; @@ -20,10 +20,7 @@ impl GenericFramebuffer for Framebuffer { } } - fn write_buffer( - &mut self, - buffer: &[(u8, u8, u8)], - ) { + fn write_buffer(&mut self, buffer: &[(u8, u8, u8)]) { let mut raw_buffer = Vec::new(); for (r, g, b) in buffer { let mut rgb565: u16 = (*r as u16 & 0b11111000) << 8; @@ -40,7 +37,7 @@ pub fn update_ui( task_tracker: &TaskTracker, config: &config::Config, ui_shutdown_rx: oneshot::Receiver<()>, - ui_update_rx: Receiver + ui_update_rx: Receiver, ) { generic_framebuffer::update_ui( task_tracker, diff --git a/bin/src/display/tplink.rs b/bin/src/display/tplink.rs index 5216f31..74945e8 100644 --- a/bin/src/display/tplink.rs +++ b/bin/src/display/tplink.rs @@ -4,7 +4,7 @@ use tokio::sync::oneshot; use tokio_util::task::TaskTracker; use crate::config; -use crate::display::{DisplayState, tplink_onebit, tplink_framebuffer}; +use crate::display::{tplink_framebuffer, tplink_onebit, DisplayState}; use std::fs; @@ -21,19 +21,9 @@ pub fn update_ui( if fs::exists(tplink_onebit::OLED_PATH).unwrap_or_default() { info!("detected one-bit display"); - tplink_onebit::update_ui( - task_tracker, - config, - ui_shutdown_rx, - ui_update_rx - ) + tplink_onebit::update_ui(task_tracker, config, ui_shutdown_rx, ui_update_rx) } else { info!("fallback to framebuffer"); - tplink_framebuffer::update_ui( - task_tracker, - config, - ui_shutdown_rx, - ui_update_rx - ) + tplink_framebuffer::update_ui(task_tracker, config, ui_shutdown_rx, ui_update_rx) } } diff --git a/bin/src/display/tplink_framebuffer.rs b/bin/src/display/tplink_framebuffer.rs index ea3badd..af52b10 100644 --- a/bin/src/display/tplink_framebuffer.rs +++ b/bin/src/display/tplink_framebuffer.rs @@ -3,11 +3,11 @@ use std::io::Write; use std::os::fd::AsRawFd; use crate::config; +use crate::display::generic_framebuffer::{self, Dimensions, GenericFramebuffer}; use crate::display::DisplayState; -use crate::display::generic_framebuffer::{self, GenericFramebuffer, Dimensions}; -use tokio::sync::oneshot; use tokio::sync::mpsc::Receiver; +use tokio::sync::oneshot; use tokio_util::task::TaskTracker; const FB_PATH: &str = "/dev/fb0"; @@ -33,10 +33,7 @@ impl GenericFramebuffer for Framebuffer { } } - fn write_buffer( - &mut self, - buffer: &[(u8, u8, u8)], - ) { + fn write_buffer(&mut self, buffer: &[(u8, u8, u8)]) { // for how to write to the buffer, consult M7350v5_en_gpl/bootable/recovery/recovery_color_oled.c let dimensions = self.dimensions(); let width = dimensions.width; @@ -65,7 +62,7 @@ impl GenericFramebuffer for Framebuffer { unsafe { let res = libc::ioctl( f.as_raw_fd(), - 0x4619, // FBIORECT_DISPLAY + 0x4619, // FBIORECT_DISPLAY &mut arg as *mut _, std::mem::size_of::(), ); @@ -81,7 +78,7 @@ pub fn update_ui( task_tracker: &TaskTracker, config: &config::Config, ui_shutdown_rx: oneshot::Receiver<()>, - ui_update_rx: Receiver + ui_update_rx: Receiver, ) { generic_framebuffer::update_ui( task_tracker, diff --git a/bin/src/dummy_analyzer.rs b/bin/src/dummy_analyzer.rs index 1aa50b7..66876b7 100644 --- a/bin/src/dummy_analyzer.rs +++ b/bin/src/dummy_analyzer.rs @@ -5,11 +5,11 @@ use rayhunter::telcom_parser::lte_rrc::{PCCH_MessageType, PCCH_MessageType_c1, P use rayhunter::analysis::analyzer::{Analyzer, Event, EventType, Severity}; use rayhunter::analysis::information_element::{InformationElement, LteInformationElement}; -pub struct TestAnalyzer{ +pub struct TestAnalyzer { pub count: i32, } -impl Analyzer for TestAnalyzer{ +impl Analyzer for TestAnalyzer { fn get_name(&self) -> Cow { Cow::from("Example Analyzer") } @@ -22,15 +22,15 @@ impl Analyzer for TestAnalyzer{ self.count += 1; if self.count % 100 == 0 { return Some(Event { - event_type: EventType::Informational , + event_type: EventType::Informational, message: "multiple of 100 events processed".to_string(), - }) + }); } let pcch_msg = match ie { - InformationElement::LTE(lte_ie) => match &** lte_ie { + InformationElement::LTE(lte_ie) => match &**lte_ie { LteInformationElement::PCCH(pcch_msg) => pcch_msg, _ => return None, - } + }, _ => return None, }; let PCCH_MessageType::C1(PCCH_MessageType_c1::Paging(paging)) = &pcch_msg.message else { @@ -39,9 +39,11 @@ impl Analyzer for TestAnalyzer{ for record in &paging.paging_record_list.as_ref()?.0 { if let PagingUE_Identity::S_TMSI(_) = record.ue_identity { return Some(Event { - event_type: EventType::QualitativeWarning { severity: Severity::Low }, + event_type: EventType::QualitativeWarning { + severity: Severity::Low, + }, message: "TMSI was provided to cell".to_string(), - }) + }); } } None diff --git a/bin/src/error.rs b/bin/src/error.rs index 2983256..9016a20 100644 --- a/bin/src/error.rs +++ b/bin/src/error.rs @@ -1,10 +1,10 @@ -use thiserror::Error; use rayhunter::diag_device::DiagDeviceError; +use thiserror::Error; use crate::qmdl_store::RecordingStoreError; #[derive(Error, Debug)] -pub enum RayhunterError{ +pub enum RayhunterError { #[error("Config file parsing error: {0}")] ConfigFileParsingError(#[from] toml::de::Error), #[error("Diag intialization error: {0}")] diff --git a/bin/src/pcap.rs b/bin/src/pcap.rs index 4f7395c..ac94095 100644 --- a/bin/src/pcap.rs +++ b/bin/src/pcap.rs @@ -1,36 +1,43 @@ use crate::ServerState; +use axum::body::Body; +use axum::extract::{Path, State}; +use axum::http::header::CONTENT_TYPE; +use axum::http::StatusCode; +use axum::response::{IntoResponse, Response}; +use futures::TryStreamExt; +use log::error; use rayhunter::diag::DataType; use rayhunter::gsmtap_parser; use rayhunter::pcap::GsmtapPcapWriter; use rayhunter::qmdl::QmdlReader; -use axum::body::Body; -use axum::http::header::CONTENT_TYPE; -use axum::extract::{State, Path}; -use axum::http::StatusCode; -use axum::response::{Response, IntoResponse}; +use std::sync::Arc; +use std::{future, pin::pin}; use tokio::io::duplex; use tokio_util::io::ReaderStream; -use std::{future, pin::pin}; -use std::sync::Arc; -use log::error; -use futures::TryStreamExt; // Streams a pcap file chunk-by-chunk to the client by reading the QMDL data // written so far. This is done by spawning a thread which streams chunks of // pcap data to a channel that's piped to the client. -pub async fn get_pcap(State(state): State>, Path(qmdl_name): Path) -> Result { +pub async fn get_pcap( + State(state): State>, + Path(qmdl_name): Path, +) -> Result { let qmdl_store = state.qmdl_store_lock.read().await; - let (entry_index, entry) = qmdl_store.entry_for_name(&qmdl_name) - .ok_or((StatusCode::NOT_FOUND, format!("couldn't find qmdl file with name {}", qmdl_name)))?; + let (entry_index, entry) = qmdl_store.entry_for_name(&qmdl_name).ok_or(( + StatusCode::NOT_FOUND, + format!("couldn't find qmdl file with name {}", qmdl_name), + ))?; if entry.qmdl_size_bytes == 0 { return Err(( StatusCode::SERVICE_UNAVAILABLE, - "QMDL file is empty, try again in a bit!".to_string() + "QMDL file is empty, try again in a bit!".to_string(), )); } let qmdl_size_bytes = entry.qmdl_size_bytes; - let qmdl_file = qmdl_store.open_entry_qmdl(entry_index).await + let qmdl_file = 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) @@ -40,20 +47,27 @@ pub async fn get_pcap(State(state): State>, Path(qmdl_name): Pa tokio::spawn(async move { let mut reader = QmdlReader::new(qmdl_file, Some(qmdl_size_bytes)); - let mut messages_stream = pin!(reader.as_stream() + let mut messages_stream = pin!(reader + .as_stream() .try_filter(|container| future::ready(container.data_type == DataType::UserSpace))); - while let Some(container) = messages_stream.try_next().await.expect("failed getting QMDL container") { + while let Some(container) = messages_stream + .try_next() + .await + .expect("failed getting QMDL container") + { for maybe_msg in container.into_messages() { match maybe_msg { Ok(msg) => { - let maybe_gsmtap_msg = gsmtap_parser::parse(msg) - .expect("error parsing gsmtap message"); + let maybe_gsmtap_msg = + gsmtap_parser::parse(msg).expect("error parsing gsmtap message"); if let Some((timestamp, gsmtap_msg)) = maybe_gsmtap_msg { - pcap_writer.write_gsmtap_message(gsmtap_msg, timestamp).await + pcap_writer + .write_gsmtap_message(gsmtap_msg, timestamp) + .await .expect("error writing pcap packet"); } - }, + } Err(e) => error!("error parsing message: {:?}", e), } } diff --git a/bin/src/qmdl_store.rs b/bin/src/qmdl_store.rs index 20bb946..66a3e31 100644 --- a/bin/src/qmdl_store.rs +++ b/bin/src/qmdl_store.rs @@ -1,11 +1,11 @@ -use rayhunter::util::RuntimeMetadata; use chrono::{DateTime, Local}; +use rayhunter::util::RuntimeMetadata; use serde::{Deserialize, Serialize}; use std::path::{Path, PathBuf}; use thiserror::Error; use tokio::{ fs::{self, try_exists, File, OpenOptions}, - io::AsyncWriteExt + io::AsyncWriteExt, }; #[derive(Debug, Error)] @@ -127,7 +127,7 @@ impl RecordingStore { let mut store = RecordingStore { path: path.as_ref().to_owned(), manifest: Manifest { - entries: Vec::new() + entries: Vec::new(), }, current_entry: None, }; @@ -171,10 +171,7 @@ impl RecordingStore { } // Returns the corresponding QMDL file for a given entry - pub async fn open_entry_qmdl( - &self, - entry_index: usize, - ) -> Result { + pub async fn open_entry_qmdl(&self, entry_index: usize) -> Result { let entry = &self.manifest.entries[entry_index]; File::open(entry.get_qmdl_filepath(&self.path)) .await @@ -203,8 +200,7 @@ impl RecordingStore { .open(entry.get_analysis_filepath(&self.path)) .await .map_err(RecordingStoreError::ReadFileError)?; - self.update_entry_analysis_size(entry_index, 0) - .await?; + self.update_entry_analysis_size(entry_index, 0).await?; Ok(file) } @@ -253,7 +249,8 @@ impl RecordingStore { .await .map_err(RecordingStoreError::WriteManifestError)?; - fs::rename(tmp_path, self.path.join("manifest.toml")).await + fs::rename(tmp_path, self.path.join("manifest.toml")) + .await .map_err(RecordingStoreError::WriteManifestError)?; Ok(()) @@ -261,7 +258,8 @@ impl RecordingStore { // Finds an entry by filename pub fn entry_for_name(&self, name: &str) -> Option<(usize, &ManifestEntry)> { - let entry_index = self.manifest + let entry_index = self + .manifest .entries .iter() .position(|entry| entry.name == name)?; @@ -274,7 +272,8 @@ impl RecordingStore { } pub async fn delete_entry(&mut self, name: &str) -> Result { - let entry_to_delete_idx = self.manifest + let entry_to_delete_idx = self + .manifest .entries .iter() .position(|entry| entry.name == name) diff --git a/bin/src/server.rs b/bin/src/server.rs index cc3a1bd..72596b4 100644 --- a/bin/src/server.rs +++ b/bin/src/server.rs @@ -1,20 +1,20 @@ use axum::body::Body; -use axum::http::header::{self, CONTENT_LENGTH, CONTENT_TYPE}; -use axum::extract::State; -use axum::http::{StatusCode, HeaderValue}; -use axum::response::{Response, IntoResponse}; use axum::extract::Path; +use axum::extract::State; +use axum::http::header::{self, CONTENT_LENGTH, CONTENT_TYPE}; +use axum::http::{HeaderValue, StatusCode}; +use axum::response::{IntoResponse, Response}; +use include_dir::{include_dir, Dir}; +use std::sync::Arc; use tokio::fs::File; use tokio::io::AsyncReadExt; use tokio::sync::mpsc::Sender; -use std::sync::Arc; use tokio::sync::RwLock; use tokio_util::io::ReaderStream; -use include_dir::{include_dir, Dir}; -use crate::{display, DiagDeviceCtrlMessage}; use crate::analysis::{AnalysisCtrlMessage, AnalysisStatus}; use crate::qmdl_store::RecordingStore; +use crate::{display, DiagDeviceCtrlMessage}; pub struct ServerState { pub qmdl_store_lock: Arc>, @@ -25,13 +25,22 @@ pub struct ServerState { pub debug_mode: bool, } -pub async fn get_qmdl(State(state): State>, Path(qmdl_name): Path) -> Result { +pub async fn get_qmdl( + State(state): State>, + Path(qmdl_name): Path, +) -> Result { let qmdl_idx = qmdl_name.trim_end_matches(".qmdl"); let qmdl_store = state.qmdl_store_lock.read().await; - let (entry_index, entry) = qmdl_store.entry_for_name(qmdl_idx) - .ok_or((StatusCode::NOT_FOUND, format!("couldn't find qmdl file with name {}", qmdl_idx)))?; - let qmdl_file = qmdl_store.open_entry_qmdl(entry_index).await - .map_err(|e| (StatusCode::INTERNAL_SERVER_ERROR, format!("error opening QMDL file: {}", e)))?; + let (entry_index, entry) = qmdl_store.entry_for_name(qmdl_idx).ok_or(( + StatusCode::NOT_FOUND, + format!("couldn't find qmdl file with name {}", qmdl_idx), + ))?; + let qmdl_file = qmdl_store.open_entry_qmdl(entry_index).await.map_err(|e| { + ( + StatusCode::INTERNAL_SERVER_ERROR, + format!("error opening QMDL file: {}", e), + ) + })?; let limited_qmdl_file = qmdl_file.take(entry.qmdl_size_bytes as u64); let qmdl_stream = ReaderStream::new(limited_qmdl_file); @@ -46,7 +55,10 @@ pub async fn get_qmdl(State(state): State>, Path(qmdl_name): Pa // Bundles the server's static files (html/css/js) into the binary for easy distribution static STATIC_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static"); -pub async fn serve_static(State(state): State>, Path(path): Path) -> impl IntoResponse { +pub async fn serve_static( + State(state): State>, + Path(path): Path, +) -> impl IntoResponse { let path = path.trim_start_matches('/'); let mime_type = mime_guess::from_path(path).first_or_text_plain(); @@ -62,7 +74,9 @@ pub async fn serve_static(State(state): State>, Path(path): Pat return match File::open(build_path).await { Ok(mut file) => { let mut body = String::new(); - file.read_to_string(&mut body).await.expect("failed to read file"); + file.read_to_string(&mut body) + .await + .expect("failed to read file"); Response::builder() .status(StatusCode::OK) .header( @@ -71,11 +85,11 @@ pub async fn serve_static(State(state): State>, Path(path): Pat ) .body(Body::from(body)) .unwrap() - }, + } Err(_) => Response::builder() .status(StatusCode::NOT_FOUND) .body(Body::empty()) - .unwrap() + .unwrap(), }; } diff --git a/bin/src/stats.rs b/bin/src/stats.rs index 28d5a81..9759167 100644 --- a/bin/src/stats.rs +++ b/bin/src/stats.rs @@ -3,9 +3,9 @@ use std::sync::Arc; use crate::qmdl_store::ManifestEntry; use crate::server::ServerState; -use axum::Json; use axum::extract::State; use axum::http::StatusCode; +use axum::Json; use log::error; use serde::Serialize; use tokio::process::Command; @@ -65,10 +65,16 @@ pub struct MemoryStats { // runs the given command and returns its stdout as a string async fn get_cmd_output(mut cmd: Command) -> Result { let cmd_str = format!("{:?}", &cmd); - let output = cmd.output().await + let output = cmd + .output() + .await .map_err(|e| format!("error running command {}: {}", &cmd_str, e))?; if !output.status.success() { - return Err(format!("command {} failed with exit code {}", &cmd_str, output.status.code().unwrap())); + return Err(format!( + "command {} failed with exit code {}", + &cmd_str, + output.status.code().unwrap() + )); } Ok(String::from_utf8_lossy(&output.stdout).to_string()) } @@ -79,7 +85,8 @@ impl MemoryStats { let mut free_cmd = Command::new("free"); free_cmd.arg("-k"); let stdout = get_cmd_output(free_cmd).await?; - let mut numbers = stdout.split_whitespace() + let mut numbers = stdout + .split_whitespace() .flat_map(|part| part.parse::()); Ok(Self { total: humanize_kb(numbers.next().ok_or("error parsing free output")?), @@ -91,13 +98,15 @@ impl MemoryStats { // turns a number of kilobytes (like 28293) into a human-readable string (like "28.3M") fn humanize_kb(kb: usize) -> String { - if kb < 1000{ + if kb < 1000 { return format!("{}K", kb); } format!("{:.1}M", kb as f64 / 1024.0) } -pub async fn get_system_stats(State(state): State>) -> Result, (StatusCode, String)> { +pub async fn get_system_stats( + State(state): State>, +) -> Result, (StatusCode, String)> { let qmdl_store = state.qmdl_store_lock.read().await; match SystemStats::new(qmdl_store.path.to_str().unwrap()).await { Ok(stats) => Ok(Json(stats)), @@ -105,9 +114,9 @@ pub async fn get_system_stats(State(state): State>) -> Result, } -pub async fn get_qmdl_manifest(State(state): State>) -> Result, (StatusCode, String)> { +pub async fn get_qmdl_manifest( + State(state): State>, +) -> Result, (StatusCode, String)> { let qmdl_store = state.qmdl_store_lock.read().await; let mut entries = qmdl_store.manifest.entries.clone(); let current_entry = qmdl_store.current_entry.map(|index| entries.remove(index)); diff --git a/lib/src/analysis/analyzer.rs b/lib/src/analysis/analyzer.rs index 08a35a2..255a24d 100644 --- a/lib/src/analysis/analyzer.rs +++ b/lib/src/analysis/analyzer.rs @@ -1,14 +1,13 @@ -use std::borrow::Cow; use chrono::{DateTime, FixedOffset}; use serde::Serialize; +use std::borrow::Cow; -use crate::{diag::MessagesContainer, gsmtap_parser}; use crate::util::RuntimeMetadata; +use crate::{diag::MessagesContainer, gsmtap_parser}; use super::{ - imsi_requested::ImsiRequestedAnalyzer, - information_element::InformationElement, connection_redirect_downgrade::ConnectionRedirect2GDowngradeAnalyzer, + imsi_requested::ImsiRequestedAnalyzer, information_element::InformationElement, priority_2g_downgrade::LteSib6And7DowngradeAnalyzer, }; @@ -118,14 +117,16 @@ impl Default for Harness { impl Harness { pub fn new() -> Self { - Self { analyzers: Vec::new() } + Self { + analyzers: Vec::new(), + } } pub fn new_with_all_analyzers() -> Self { let mut harness = Harness::new(); harness.add_analyzer(Box::new(ImsiRequestedAnalyzer::new())); - harness.add_analyzer(Box::new(ConnectionRedirect2GDowngradeAnalyzer{})); - harness.add_analyzer(Box::new(LteSib6And7DowngradeAnalyzer{})); + harness.add_analyzer(Box::new(ConnectionRedirect2GDowngradeAnalyzer {})); + harness.add_analyzer(Box::new(LteSib6And7DowngradeAnalyzer {})); // FIXME: our RRC parser is reporting false positives for this due to an // upstream hampi bug (https://github.com/ystero-dev/hampi/issues/133). @@ -186,19 +187,22 @@ impl Harness { } fn analyze_information_element(&mut self, ie: &InformationElement) -> Vec> { - self.analyzers.iter_mut() + self.analyzers + .iter_mut() .map(|analyzer| analyzer.analyze_information_element(ie)) .collect() } pub fn get_names(&self) -> Vec> { - self.analyzers.iter() + self.analyzers + .iter() .map(|analyzer| analyzer.get_name()) .collect() } pub fn get_descriptions(&self) -> Vec> { - self.analyzers.iter() + self.analyzers + .iter() .map(|analyzer| analyzer.get_description()) .collect() } diff --git a/lib/src/analysis/connection_redirect_downgrade.rs b/lib/src/analysis/connection_redirect_downgrade.rs index 6ed5dad..8c6e56f 100644 --- a/lib/src/analysis/connection_redirect_downgrade.rs +++ b/lib/src/analysis/connection_redirect_downgrade.rs @@ -2,13 +2,15 @@ use std::borrow::Cow; use super::analyzer::{Analyzer, Event, EventType, Severity}; use super::information_element::{InformationElement, LteInformationElement}; -use telcom_parser::lte_rrc::{DL_DCCH_MessageType, DL_DCCH_MessageType_c1, RRCConnectionReleaseCriticalExtensions, RRCConnectionReleaseCriticalExtensions_c1, RedirectedCarrierInfo}; use super::util::unpack; +use telcom_parser::lte_rrc::{ + DL_DCCH_MessageType, DL_DCCH_MessageType_c1, RRCConnectionReleaseCriticalExtensions, + RRCConnectionReleaseCriticalExtensions_c1, RedirectedCarrierInfo, +}; // Based on HITBSecConf presentation "Forcing a targeted LTE cellphone into an // eavesdropping network" by Lin Huang -pub struct ConnectionRedirect2GDowngradeAnalyzer { -} +pub struct ConnectionRedirect2GDowngradeAnalyzer {} // TODO: keep track of SIB state to compare LTE reselection blocks w/ 2g/3g ones impl Analyzer for ConnectionRedirect2GDowngradeAnalyzer { @@ -33,7 +35,9 @@ impl Analyzer for ConnectionRedirect2GDowngradeAnalyzer { unpack!(Some(carrier_info) = &r8_ies.redirected_carrier_info); match carrier_info { RedirectedCarrierInfo::Geran(_carrier_freqs_geran) => Some(Event { - event_type: EventType::QualitativeWarning { severity: Severity::High }, + event_type: EventType::QualitativeWarning { + severity: Severity::High, + }, message: "Detected 2G downgrade".to_owned(), }), _ => Some(Event { diff --git a/lib/src/analysis/imsi_provided.rs b/lib/src/analysis/imsi_provided.rs index 927ebe4..05723ab 100644 --- a/lib/src/analysis/imsi_provided.rs +++ b/lib/src/analysis/imsi_provided.rs @@ -5,8 +5,7 @@ use telcom_parser::lte_rrc::{PCCH_MessageType, PCCH_MessageType_c1, PagingUE_Ide use super::analyzer::{Analyzer, Event, EventType, Severity}; use super::information_element::{InformationElement, LteInformationElement}; -pub struct ImsiProvidedAnalyzer { -} +pub struct ImsiProvidedAnalyzer {} impl Analyzer for ImsiProvidedAnalyzer { fn get_name(&self) -> Cow { @@ -19,10 +18,10 @@ impl Analyzer for ImsiProvidedAnalyzer { fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { let pcch_msg = match ie { - InformationElement::LTE(lte_ie) => match &** lte_ie { + InformationElement::LTE(lte_ie) => match &**lte_ie { LteInformationElement::PCCH(pcch_msg) => pcch_msg, _ => return None, - } + }, _ => return None, }; let PCCH_MessageType::C1(PCCH_MessageType_c1::Paging(paging)) = &pcch_msg.message else { @@ -31,9 +30,11 @@ impl Analyzer for ImsiProvidedAnalyzer { for record in &paging.paging_record_list.as_ref()?.0 { if let PagingUE_Identity::Imsi(_) = record.ue_identity { return Some(Event { - event_type: EventType::QualitativeWarning { severity: Severity::High }, + event_type: EventType::QualitativeWarning { + severity: Severity::High, + }, message: "IMSI was provided to cell".to_string(), - }) + }); } } None diff --git a/lib/src/analysis/imsi_requested.rs b/lib/src/analysis/imsi_requested.rs index 323f926..3eafbb2 100644 --- a/lib/src/analysis/imsi_requested.rs +++ b/lib/src/analysis/imsi_requested.rs @@ -36,7 +36,7 @@ impl Analyzer for ImsiRequestedAnalyzer { InformationElement::LTE(inner) => match &**inner { LteInformationElement::NAS(payload) => payload, _ => return None, - } + }, _ => return None, }; @@ -45,7 +45,7 @@ impl Analyzer for ImsiRequestedAnalyzer { if self.packet_num < PACKET_THRESHHOLD { return Some(Event { event_type: EventType::QualitativeWarning { - severity: Severity::Medium + severity: Severity::Medium, }, message: format!( "NAS IMSI identity request detected, however it was within \ @@ -53,15 +53,15 @@ impl Analyzer for ImsiRequestedAnalyzer { turned your device on, this is likely a \ false-positive.", PACKET_THRESHHOLD - ) - }) + ), + }); } else { return Some(Event { event_type: EventType::QualitativeWarning { - severity: Severity::High + severity: Severity::High, }, message: "NAS IMSI identity request detected".to_owned(), - }) + }); } } None diff --git a/lib/src/analysis/information_element.rs b/lib/src/analysis/information_element.rs index 161ab36..9286755 100644 --- a/lib/src/analysis/information_element.rs +++ b/lib/src/analysis/information_element.rs @@ -3,9 +3,9 @@ //! the term to refer to a structured, fully parsed message in any telcom //! standard. +use crate::gsmtap::{GsmtapMessage, GsmtapType, LteNasSubtype, LteRrcSubtype}; use telcom_parser::{decode, lte_rrc}; use thiserror::Error; -use crate::gsmtap::{GsmtapMessage, GsmtapType, LteNasSubtype, LteRrcSubtype}; #[derive(Error, Debug)] pub enum InformationElementError { @@ -46,7 +46,6 @@ pub enum LteInformationElement { // FIXME: actually parse NAS messages NAS(Vec), - // FIXME: unclear which message these "NB" types map to //DlCcchNb(), //DlDcchNb(), @@ -65,8 +64,8 @@ impl TryFrom<&GsmtapMessage> for InformationElement { fn try_from(gsmtap_msg: &GsmtapMessage) -> Result { match gsmtap_msg.header.gsmtap_type { GsmtapType::LteRrc(lte_rrc_subtype) => { - use LteRrcSubtype as L; use LteInformationElement as R; + use LteRrcSubtype as L; let lte = match lte_rrc_subtype { L::DlCcch => R::DlCcch(decode(&gsmtap_msg.payload)?), L::DlDcch => R::DlDcch(Box::new(decode(&gsmtap_msg.payload)?)), @@ -82,14 +81,20 @@ impl TryFrom<&GsmtapMessage> for InformationElement { L::BcchDlSchMbms => R::BcchDlSchMbms(decode(&gsmtap_msg.payload)?), L::SbcchSlBch => R::SbcchSlBch(decode(&gsmtap_msg.payload)?), L::SbcchSlBchV2x => R::SbcchSlBchV2x(decode(&gsmtap_msg.payload)?), - _ => return Err(InformationElementError::UnsupportedGsmtapType(gsmtap_msg.header.gsmtap_type)), + _ => { + return Err(InformationElementError::UnsupportedGsmtapType( + gsmtap_msg.header.gsmtap_type, + )) + } }; Ok(InformationElement::LTE(Box::new(lte))) - }, - GsmtapType::LteNas(LteNasSubtype::Plain) => { - Ok(InformationElement::LTE(Box::new(LteInformationElement::NAS(gsmtap_msg.payload.clone())))) - }, - _ => Err(InformationElementError::UnsupportedGsmtapType(gsmtap_msg.header.gsmtap_type)), + } + GsmtapType::LteNas(LteNasSubtype::Plain) => Ok(InformationElement::LTE(Box::new( + LteInformationElement::NAS(gsmtap_msg.payload.clone()), + ))), + _ => Err(InformationElementError::UnsupportedGsmtapType( + gsmtap_msg.header.gsmtap_type, + )), } } } diff --git a/lib/src/analysis/mod.rs b/lib/src/analysis/mod.rs index 755172d..cb733e2 100644 --- a/lib/src/analysis/mod.rs +++ b/lib/src/analysis/mod.rs @@ -1,8 +1,8 @@ pub mod analyzer; -pub mod information_element; -pub mod priority_2g_downgrade; pub mod connection_redirect_downgrade; pub mod imsi_provided; pub mod imsi_requested; +pub mod information_element; pub mod null_cipher; +pub mod priority_2g_downgrade; pub mod util; diff --git a/lib/src/analysis/null_cipher.rs b/lib/src/analysis/null_cipher.rs index 095324c..d778cdf 100644 --- a/lib/src/analysis/null_cipher.rs +++ b/lib/src/analysis/null_cipher.rs @@ -1,25 +1,41 @@ use std::borrow::Cow; -use telcom_parser::lte_rrc::{CipheringAlgorithm_r12, DL_DCCH_MessageType, DL_DCCH_MessageType_c1, RRCConnectionReconfiguration, RRCConnectionReconfigurationCriticalExtensions, RRCConnectionReconfigurationCriticalExtensions_c1, SCG_Configuration_r12, SecurityConfigHO_v1530HandoverType_v1530, SecurityModeCommand, SecurityModeCommandCriticalExtensions, SecurityModeCommandCriticalExtensions_c1}; +use telcom_parser::lte_rrc::{ + CipheringAlgorithm_r12, DL_DCCH_MessageType, DL_DCCH_MessageType_c1, + RRCConnectionReconfiguration, RRCConnectionReconfigurationCriticalExtensions, + RRCConnectionReconfigurationCriticalExtensions_c1, SCG_Configuration_r12, + SecurityConfigHO_v1530HandoverType_v1530, SecurityModeCommand, + SecurityModeCommandCriticalExtensions, SecurityModeCommandCriticalExtensions_c1, +}; use super::analyzer::{Analyzer, Event, EventType, Severity}; use super::information_element::{InformationElement, LteInformationElement}; -pub struct NullCipherAnalyzer { -} +pub struct NullCipherAnalyzer {} impl NullCipherAnalyzer { - fn check_rrc_connection_reconfiguration_cipher(&self, reconfiguration: &RRCConnectionReconfiguration) -> bool { - let RRCConnectionReconfigurationCriticalExtensions::C1(c1) = &reconfiguration.critical_extensions else { + fn check_rrc_connection_reconfiguration_cipher( + &self, + reconfiguration: &RRCConnectionReconfiguration, + ) -> bool { + let RRCConnectionReconfigurationCriticalExtensions::C1(c1) = + &reconfiguration.critical_extensions + else { return false; }; - let RRCConnectionReconfigurationCriticalExtensions_c1::RrcConnectionReconfiguration_r8(c1) = c1 else { + let RRCConnectionReconfigurationCriticalExtensions_c1::RrcConnectionReconfiguration_r8(c1) = + c1 + else { return false; }; if let Some(handover) = &c1.security_config_ho { let maybe_security_config = match &handover.handover_type { - telcom_parser::lte_rrc::SecurityConfigHOHandoverType::IntraLTE(lte) => lte.security_algorithm_config.as_ref(), - telcom_parser::lte_rrc::SecurityConfigHOHandoverType::InterRAT(rat) => Some(&rat.security_algorithm_config), + telcom_parser::lte_rrc::SecurityConfigHOHandoverType::IntraLTE(lte) => { + lte.security_algorithm_config.as_ref() + } + telcom_parser::lte_rrc::SecurityConfigHOHandoverType::InterRAT(rat) => { + Some(&rat.security_algorithm_config) + } }; if let Some(security_config) = maybe_security_config { if security_config.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 { @@ -28,7 +44,9 @@ impl NullCipherAnalyzer { } } // Use map/flatten to dig into a long chain of nested Option types - let maybe_v1250 = c1.non_critical_extension.as_ref() + let maybe_v1250 = c1 + .non_critical_extension + .as_ref() .and_then(|v890| v890.non_critical_extension.as_ref()) .and_then(|v920| v920.non_critical_extension.as_ref()) .and_then(|v1020| v1020.non_critical_extension.as_ref()) @@ -37,8 +55,11 @@ impl NullCipherAnalyzer { return false; }; - if let Some(SCG_Configuration_r12::Setup(scg_setup)) = v1250.scg_configuration_r12.as_ref() { - let maybe_cipher = scg_setup.scg_config_part_scg_r12.as_ref() + if let Some(SCG_Configuration_r12::Setup(scg_setup)) = v1250.scg_configuration_r12.as_ref() + { + let maybe_cipher = scg_setup + .scg_config_part_scg_r12 + .as_ref() .and_then(|scg| scg.mobility_control_info_scg_r12.as_ref()) .and_then(|mci| mci.ciphering_algorithm_scg_r12.as_ref()); if let Some(cipher) = maybe_cipher { @@ -48,7 +69,9 @@ impl NullCipherAnalyzer { } } - let maybe_v1530_security_config = v1250.non_critical_extension.as_ref() + let maybe_v1530_security_config = v1250 + .non_critical_extension + .as_ref() .and_then(|v1310| v1310.non_critical_extension.as_ref()) .and_then(|v1430| v1430.non_critical_extension.as_ref()) .and_then(|v1510| v1510.non_critical_extension.as_ref()) @@ -57,9 +80,15 @@ impl NullCipherAnalyzer { return false; }; let maybe_security_algorithm = match &v1530_security_config.handover_type_v1530 { - SecurityConfigHO_v1530HandoverType_v1530::Intra5GC(intra_5gc) => intra_5gc.security_algorithm_config_r15.as_ref(), - SecurityConfigHO_v1530HandoverType_v1530::Fivegc_ToEPC(to_epc) => Some(&to_epc.security_algorithm_config_r15), - SecurityConfigHO_v1530HandoverType_v1530::Epc_To5GC(to_5gc) => Some(&to_5gc.security_algorithm_config_r15), + SecurityConfigHO_v1530HandoverType_v1530::Intra5GC(intra_5gc) => { + intra_5gc.security_algorithm_config_r15.as_ref() + } + SecurityConfigHO_v1530HandoverType_v1530::Fivegc_ToEPC(to_epc) => { + Some(&to_epc.security_algorithm_config_r15) + } + SecurityConfigHO_v1530HandoverType_v1530::Epc_To5GC(to_5gc) => { + Some(&to_5gc.security_algorithm_config_r15) + } }; if let Some(security_algorithm) = maybe_security_algorithm { if security_algorithm.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 { @@ -76,7 +105,13 @@ impl NullCipherAnalyzer { let SecurityModeCommandCriticalExtensions_c1::SecurityModeCommand_r8(r8) = &c1 else { return false; }; - if r8.security_config_smc.security_algorithm_config.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 { + if r8 + .security_config_smc + .security_algorithm_config + .ciphering_algorithm + .0 + == CipheringAlgorithm_r12::EEA0 + { return true; } false @@ -94,23 +129,29 @@ impl Analyzer for NullCipherAnalyzer { fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { let dcch_msg = match ie { - InformationElement::LTE(lte_ie) => match &** lte_ie { + InformationElement::LTE(lte_ie) => match &**lte_ie { LteInformationElement::DlDcch(dcch_msg) => dcch_msg, _ => return None, - } + }, _ => return None, }; let DL_DCCH_MessageType::C1(c1) = &dcch_msg.message else { return None; }; let null_cipher_detected = match c1 { - DL_DCCH_MessageType_c1::RrcConnectionReconfiguration(reconfiguration) => self.check_rrc_connection_reconfiguration_cipher(reconfiguration), - DL_DCCH_MessageType_c1::SecurityModeCommand(command) => self.check_security_mode_command_cipher(command), + DL_DCCH_MessageType_c1::RrcConnectionReconfiguration(reconfiguration) => { + self.check_rrc_connection_reconfiguration_cipher(reconfiguration) + } + DL_DCCH_MessageType_c1::SecurityModeCommand(command) => { + self.check_security_mode_command_cipher(command) + } _ => return None, }; if null_cipher_detected { return Some(Event { - event_type: EventType::QualitativeWarning { severity: Severity::High }, + event_type: EventType::QualitativeWarning { + severity: Severity::High, + }, message: "Cell suggested use of null cipher".to_string(), }); } diff --git a/lib/src/analysis/priority_2g_downgrade.rs b/lib/src/analysis/priority_2g_downgrade.rs index ea7ef70..b408a53 100644 --- a/lib/src/analysis/priority_2g_downgrade.rs +++ b/lib/src/analysis/priority_2g_downgrade.rs @@ -2,18 +2,29 @@ use std::borrow::Cow; use super::analyzer::{Analyzer, Event, EventType, Severity}; use super::information_element::{InformationElement, LteInformationElement}; -use telcom_parser::lte_rrc::{BCCH_DL_SCH_MessageType, BCCH_DL_SCH_MessageType_c1, CellReselectionPriority, SystemInformationBlockType7, SystemInformationCriticalExtensions, SystemInformation_r8_IEsSib_TypeAndInfo, SystemInformation_r8_IEsSib_TypeAndInfo_Entry}; +use telcom_parser::lte_rrc::{ + BCCH_DL_SCH_MessageType, BCCH_DL_SCH_MessageType_c1, CellReselectionPriority, + SystemInformationBlockType7, SystemInformationCriticalExtensions, + SystemInformation_r8_IEsSib_TypeAndInfo, SystemInformation_r8_IEsSib_TypeAndInfo_Entry, +}; /// Based on heuristic T7 from Shinjo Park's "Why We Cannot Win". -pub struct LteSib6And7DowngradeAnalyzer { -} +pub struct LteSib6And7DowngradeAnalyzer {} impl LteSib6And7DowngradeAnalyzer { - fn unpack_system_information<'a>(&self, ie: &'a InformationElement) -> Option<&'a SystemInformation_r8_IEsSib_TypeAndInfo> { + fn unpack_system_information<'a>( + &self, + ie: &'a InformationElement, + ) -> Option<&'a SystemInformation_r8_IEsSib_TypeAndInfo> { if let InformationElement::LTE(lte_ie) = ie { if let LteInformationElement::BcchDlSch(bcch_dl_sch_message) = &**lte_ie { - if let BCCH_DL_SCH_MessageType::C1(BCCH_DL_SCH_MessageType_c1::SystemInformation(system_information)) = &bcch_dl_sch_message.message { - if let SystemInformationCriticalExtensions::SystemInformation_r8(sib) = &system_information.critical_extensions { + if let BCCH_DL_SCH_MessageType::C1(BCCH_DL_SCH_MessageType_c1::SystemInformation( + system_information, + )) = &bcch_dl_sch_message.message + { + if let SystemInformationCriticalExtensions::SystemInformation_r8(sib) = + &system_information.critical_extensions + { return Some(&sib.sib_type_and_info); } } @@ -33,14 +44,19 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer { Cow::from("Tests for LTE cells broadcasting a SIB type 6 and 7 which include 2G/3G frequencies with higher priorities.") } - fn analyze_information_element(&mut self, ie: &InformationElement) -> Option { + fn analyze_information_element( + &mut self, + ie: &InformationElement, + ) -> Option { let sibs = &self.unpack_system_information(ie)?.0; for sib in sibs { match sib { SystemInformation_r8_IEsSib_TypeAndInfo_Entry::Sib6(sib6) => { if let Some(carrier_info_list) = sib6.carrier_freq_list_utra_fdd.as_ref() { for carrier_info in &carrier_info_list.0 { - if let Some(CellReselectionPriority(p)) = carrier_info.cell_reselection_priority { + if let Some(CellReselectionPriority(p)) = + carrier_info.cell_reselection_priority + { if p == 0 { return Some(Event { event_type: EventType::QualitativeWarning { severity: Severity::High }, @@ -52,7 +68,9 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer { } if let Some(carrier_info_list) = sib6.carrier_freq_list_utra_tdd.as_ref() { for carrier_info in &carrier_info_list.0 { - if let Some(CellReselectionPriority(p)) = carrier_info.cell_reselection_priority { + if let Some(CellReselectionPriority(p)) = + carrier_info.cell_reselection_priority + { if p == 0 { return Some(Event { event_type: EventType::QualitativeWarning { severity: Severity::High }, @@ -62,20 +80,31 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer { } } } - }, - SystemInformation_r8_IEsSib_TypeAndInfo_Entry::Sib7(SystemInformationBlockType7 { carrier_freqs_info_list: Some(carrier_info_list), .. }) => { + } + SystemInformation_r8_IEsSib_TypeAndInfo_Entry::Sib7( + SystemInformationBlockType7 { + carrier_freqs_info_list: Some(carrier_info_list), + .. + }, + ) => { for carrier_info in &carrier_info_list.0 { - if let Some(CellReselectionPriority(p)) = carrier_info.common_info.cell_reselection_priority { + if let Some(CellReselectionPriority(p)) = + carrier_info.common_info.cell_reselection_priority + { if p == 0 { return Some(Event { - event_type: EventType::QualitativeWarning { severity: Severity::High }, - message: "LTE cell advertised a 2G cell for priority 0 reselection".to_string(), + event_type: EventType::QualitativeWarning { + severity: Severity::High, + }, + message: + "LTE cell advertised a 2G cell for priority 0 reselection" + .to_string(), }); } } } - }, - _ => {}, + } + _ => {} } } None diff --git a/lib/src/analysis/util.rs b/lib/src/analysis/util.rs index 2076c61..ac128e4 100644 --- a/lib/src/analysis/util.rs +++ b/lib/src/analysis/util.rs @@ -1,4 +1,3 @@ - // Unpacks a pattern, or returns None. // // # Examples @@ -24,7 +23,9 @@ // macro_rules! unpack { ($pat:pat = $val:expr) => { - let $pat = $val else { return None; }; + let $pat = $val else { + return None; + }; }; } diff --git a/lib/src/diag.rs b/lib/src/diag.rs index 70cba6a..baa765e 100644 --- a/lib/src/diag.rs +++ b/lib/src/diag.rs @@ -5,7 +5,7 @@ use crc::{Algorithm, Crc}; use deku::prelude::*; use crate::hdlc::{self, hdlc_decapsulate}; -use log::{warn, error}; +use log::{error, warn}; use thiserror::Error; pub const MESSAGE_TERMINATOR: u8 = 0x7e; @@ -42,7 +42,7 @@ pub enum LogConfigRequest { log_type: u32, log_mask_bitsize: u32, log_mask: Vec, - } + }, } #[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)] @@ -93,13 +93,19 @@ impl MessagesContainer { Ok(data) => match Message::from_bytes((&data, 0)) { Ok(((leftover_bytes, _), res)) => { if !leftover_bytes.is_empty() { - warn!("warning: {} leftover bytes when parsing Message", leftover_bytes.len()); + warn!( + "warning: {} leftover bytes when parsing Message", + leftover_bytes.len() + ); } result.push(Ok(res)); - }, + } Err(e) => result.push(Err(DiagParsingError::MessageParsingError(e, data))), }, - Err(err) => result.push(Err(DiagParsingError::HdlcDecapsulationError(err, sub_msg.to_vec()))), + Err(err) => result.push(Err(DiagParsingError::HdlcDecapsulationError( + err, + sub_msg.to_vec(), + ))), } } } @@ -171,7 +177,7 @@ pub enum LogBody { msg: Vec, }, #[deku(id = "0xb0c0")] - LteRrcOtaMessage{ + LteRrcOtaMessage { ext_header_version: u8, #[deku(ctx = "*ext_header_version")] packet: LteRrcOtaPacket, @@ -210,7 +216,7 @@ pub enum LogBody { NrRrcOtaMessage { #[deku(count = "hdr_len")] msg: Vec, - } + }, } #[derive(Debug, Clone, PartialEq, DekuRead, DekuWrite)] @@ -364,15 +370,17 @@ pub enum ResponsePayload { #[deku(ctx = "subopcode: u32", id = "subopcode")] pub enum LogConfigResponse { #[deku(id = "1")] - RetrieveIdRanges { - log_mask_sizes: [u32; 16], - }, + RetrieveIdRanges { log_mask_sizes: [u32; 16] }, #[deku(id = "3")] SetMask, } -pub fn build_log_mask_request(log_type: u32, log_mask_bitsize: u32, accepted_log_codes: &[u32]) -> Request { +pub fn build_log_mask_request( + log_type: u32, + log_mask_bitsize: u32, + accepted_log_codes: &[u32], +) -> Request { let mut current_byte: u8 = 0; let mut num_bits_written: u8 = 0; let mut log_mask: Vec = vec![]; @@ -413,31 +421,35 @@ mod test { log_mask_bitsize: 0, log_mask: vec![], }); - assert_eq!(req.to_bytes().unwrap(), vec![ - 115, 0, 0, 0, - 3, 0, 0, 0, - 0, 0, 0, 0, - 0, 0, 0, 0, - ]); + assert_eq!( + req.to_bytes().unwrap(), + vec![115, 0, 0, 0, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,] + ); } #[test] fn test_build_log_mask_request() { let log_type = 11; let bitsize = 513; - let req = build_log_mask_request(log_type, bitsize, &crate::diag_device::LOG_CODES_FOR_RAW_PACKET_LOGGING); - assert_eq!(req, Request::LogConfig(LogConfigRequest::SetMask { + let req = build_log_mask_request( log_type, - log_mask_bitsize: bitsize, - log_mask: vec![ - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, - 0x0, 0x0, 0xc, 0x30, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, - ], - })); + bitsize, + &crate::diag_device::LOG_CODES_FOR_RAW_PACKET_LOGGING, + ); + assert_eq!( + req, + Request::LogConfig(LogConfigRequest::SetMask { + log_type, + log_mask_bitsize: bitsize, + log_mask: vec![ + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1, 0x0, 0x0, 0x0, 0xc, 0x30, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, + 0x0, 0x0, + ], + }) + ); } #[test] @@ -448,53 +460,53 @@ mod test { mdm_field: -1, hdlc_encapsulated_request: vec![1, 2, 3, 4], }; - assert_eq!(req.to_bytes().unwrap(), vec![ - 32, 0, 0, 0, - 1, 2, 3, 4, - ]); + assert_eq!(req.to_bytes().unwrap(), vec![32, 0, 0, 0, 1, 2, 3, 4,]); let req = RequestContainer { data_type: DataType::UserSpace, use_mdm: true, mdm_field: -1, hdlc_encapsulated_request: vec![1, 2, 3, 4], }; - assert_eq!(req.to_bytes().unwrap(), vec![ - 32, 0, 0, 0, - 255, 255, 255, 255, - 1, 2, 3, 4, - ]); + assert_eq!( + req.to_bytes().unwrap(), + vec![32, 0, 0, 0, 255, 255, 255, 255, 1, 2, 3, 4,] + ); } #[test] fn test_logs() { let data = vec![ - 16, 0, 38, 0, 38, 0, 192, 176, 26, 165, 245, 135, 118, 35, 2, 1, 20, - 14, 48, 0, 160, 0, 2, 8, 0, 0, 217, 15, 5, 0, 0, 0, 0, 7, 0, 64, 1, - 238, 173, 213, 77, 208 + 16, 0, 38, 0, 38, 0, 192, 176, 26, 165, 245, 135, 118, 35, 2, 1, 20, 14, 48, 0, 160, 0, + 2, 8, 0, 0, 217, 15, 5, 0, 0, 0, 0, 7, 0, 64, 1, 238, 173, 213, 77, 208, ]; let msg = Message::from_bytes((&data, 0)).unwrap().1; - assert_eq!(msg, Message::Log { - pending_msgs: 0, - outer_length: 38, - inner_length: 38, - log_type: 0xb0c0, - timestamp: Timestamp { ts: 72659535985485082 }, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 20, - packet: LteRrcOtaPacket::V8 { - rrc_rel_maj: 14, - rrc_rel_min: 48, - bearer_id: 0, - phy_cell_id: 160, - earfcn: 2050, - sfn_subfn: 4057, - pdu_num: 5, - sib_mask: 0, - len: 7, - packet: vec![0x40, 0x1, 0xee, 0xad, 0xd5, 0x4d, 0xd0], + assert_eq!( + msg, + Message::Log { + pending_msgs: 0, + outer_length: 38, + inner_length: 38, + log_type: 0xb0c0, + timestamp: Timestamp { + ts: 72659535985485082 }, - }, - }); + body: LogBody::LteRrcOtaMessage { + ext_header_version: 20, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 14, + rrc_rel_min: 48, + bearer_id: 0, + phy_cell_id: 160, + earfcn: 2050, + sfn_subfn: 4057, + pdu_num: 5, + sib_mask: 0, + len: 7, + packet: vec![0x40, 0x1, 0xee, 0xad, 0xd5, 0x4d, 0xd0], + }, + }, + } + ); } fn make_container(data_type: DataType, message: HdlcEncapsulatedMessage) -> MessagesContainer { @@ -515,7 +527,9 @@ mod test { outer_length: length_with_payload, inner_length: length_with_payload, log_type: 0xb0c0, - timestamp: Timestamp { ts: 72659535985485082 }, + timestamp: Timestamp { + ts: 72659535985485082, + }, body: LogBody::LteRrcOtaMessage { ext_header_version: 20, packet: LteRrcOtaPacket::V8 { @@ -532,7 +546,9 @@ mod test { }, }, }; - let serialized = message.to_bytes().expect("failed to serialize test message"); + let serialized = message + .to_bytes() + .expect("failed to serialize test message"); let encapsulated_data = hdlc::hdlc_encapsulate(&serialized, &CRC_CCITT); let encapsulated = HdlcEncapsulatedMessage { len: encapsulated_data.len() as u32, @@ -574,7 +590,10 @@ mod test { container.num_messages += 1; let result = container.into_messages(); assert_eq!(result[0], Ok(message1)); - assert!(matches!(result[1], Err(DiagParsingError::MessageParsingError(_, _)))); + assert!(matches!( + result[1], + Err(DiagParsingError::MessageParsingError(_, _)) + )); } #[test] @@ -589,6 +608,9 @@ mod test { container.num_messages += 1; let result = container.into_messages(); assert_eq!(result[0], Ok(message1)); - assert!(matches!(result[1], Err(DiagParsingError::HdlcDecapsulationError(_, _)))); + assert!(matches!( + result[1], + Err(DiagParsingError::HdlcDecapsulationError(_, _)) + )); } } diff --git a/lib/src/diag_device.rs b/lib/src/diag_device.rs index 8d51e97..7dd68f0 100644 --- a/lib/src/diag_device.rs +++ b/lib/src/diag_device.rs @@ -1,13 +1,16 @@ +use crate::diag::{ + build_log_mask_request, DataType, DiagParsingError, LogConfigRequest, LogConfigResponse, + Message, MessagesContainer, Request, RequestContainer, ResponsePayload, CRC_CCITT, +}; use crate::hdlc::hdlc_encapsulate; -use crate::diag::{build_log_mask_request, DataType, DiagParsingError, LogConfigRequest, LogConfigResponse, Message, MessagesContainer, Request, RequestContainer, ResponsePayload, CRC_CCITT}; use crate::log_codes; +use deku::prelude::*; +use futures_core::TryStream; +use log::{error, info}; use std::io::ErrorKind; use std::os::fd::AsRawFd; -use futures_core::TryStream; use thiserror::Error; -use log::{info, error}; -use deku::prelude::*; use tokio::fs::File; use tokio::io::{AsyncReadExt, AsyncWriteExt}; @@ -38,22 +41,19 @@ pub enum DiagDeviceError { pub const LOG_CODES_FOR_RAW_PACKET_LOGGING: [u32; 11] = [ // Layer 2: log_codes::LOG_GPRS_MAC_SIGNALLING_MESSAGE_C, // 0x5226 - // Layer 3: log_codes::LOG_GSM_RR_SIGNALING_MESSAGE_C, // 0x512f - log_codes::WCDMA_SIGNALLING_MESSAGE, // 0x412f - log_codes::LOG_LTE_RRC_OTA_MSG_LOG_C, // 0xb0c0 - log_codes::LOG_NR_RRC_OTA_MSG_LOG_C, // 0xb821 - + log_codes::WCDMA_SIGNALLING_MESSAGE, // 0x412f + log_codes::LOG_LTE_RRC_OTA_MSG_LOG_C, // 0xb0c0 + log_codes::LOG_NR_RRC_OTA_MSG_LOG_C, // 0xb821 // NAS: log_codes::LOG_UMTS_NAS_OTA_MESSAGE_LOG_PACKET_C, // 0x713a - log_codes::LOG_LTE_NAS_ESM_OTA_IN_MSG_LOG_C, // 0xb0e2 - log_codes::LOG_LTE_NAS_ESM_OTA_OUT_MSG_LOG_C, // 0xb0e3 - log_codes::LOG_LTE_NAS_EMM_OTA_IN_MSG_LOG_C, // 0xb0ec - log_codes::LOG_LTE_NAS_EMM_OTA_OUT_MSG_LOG_C, // 0xb0ed - + log_codes::LOG_LTE_NAS_ESM_OTA_IN_MSG_LOG_C, // 0xb0e2 + log_codes::LOG_LTE_NAS_ESM_OTA_OUT_MSG_LOG_C, // 0xb0e3 + log_codes::LOG_LTE_NAS_EMM_OTA_IN_MSG_LOG_C, // 0xb0ec + log_codes::LOG_LTE_NAS_EMM_OTA_OUT_MSG_LOG_C, // 0xb0ed // User IP traffic: - log_codes::LOG_DATA_PROTOCOL_LOGGING_C // 0x11eb + log_codes::LOG_DATA_PROTOCOL_LOGGING_C, // 0x11eb ]; const BUFFER_LEN: usize = 1024 * 1024 * 10; @@ -68,9 +68,9 @@ const DIAG_IOCTL_REMOTE_DEV: u64 = 32; #[cfg(target_arch = "arm")] const DIAG_IOCTL_SWITCH_LOGGING: u32 = 7; -#[cfg(target_arch = "x86_64")] +#[cfg(target_arch = "x86_64")] const DIAG_IOCTL_SWITCH_LOGGING: u64 = 7; -#[cfg(target_arch = "aarch64")] +#[cfg(target_arch = "aarch64")] const DIAG_IOCTL_SWITCH_LOGGING: u64 = 7; pub struct DiagDevice { @@ -99,7 +99,9 @@ impl DiagDevice { }) } - pub fn as_stream(&mut self) -> impl TryStream + '_ { + pub fn as_stream( + &mut self, + ) -> impl TryStream + '_ { futures::stream::try_unfold(self, |dev| async { let container = dev.get_next_messages_container().await?; Ok(Some((container, dev))) @@ -110,11 +112,18 @@ impl DiagDevice { let mut bytes_read = 0; // TP-Link M7350 sometimes sends too small messages, we need to be able to deal with short reads. while bytes_read <= 8 { - bytes_read = self.file.read(&mut self.read_buf).await + bytes_read = self + .file + .read(&mut self.read_buf) + .await .map_err(DiagDeviceError::DeviceReadFailed)?; } - info!("Parsing messages container size = {:?} [{:?}]", bytes_read, &self.read_buf[0..bytes_read]); + info!( + "Parsing messages container size = {:?} [{:?}]", + bytes_read, + &self.read_buf[0..bytes_read] + ); match MessagesContainer::from_bytes((&self.read_buf[0..bytes_read], 0)) { Ok((_, container)) => return Ok(container), @@ -129,7 +138,9 @@ impl DiagDevice { use_mdm: self.use_mdm > 0, mdm_field: -1, hdlc_encapsulated_request: hdlc_encapsulate(req_bytes, &CRC_CCITT), - }.to_bytes().expect("Failed to serialize RequestContainer"); + } + .to_bytes() + .expect("Failed to serialize RequestContainer"); if let Err(err) = self.file.write(&buf).await { // For reasons I don't entirely understand, calls to write(2) on // /dev/diag always return 0 bytes written, though the written @@ -164,13 +175,17 @@ impl DiagDevice { for msg in self.read_response().await? { match msg { Ok(Message::Log { .. }) => info!("skipping log response..."), - Ok(Message::Response { payload, status, .. }) => match payload { - ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { log_mask_sizes }) => { + Ok(Message::Response { + payload, status, .. + }) => match payload { + ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { + log_mask_sizes, + }) => { if status != 0 { return Err(DiagDeviceError::RequestFailed(status, req)); } return Ok(log_mask_sizes); - }, + } _ => info!("skipping non-LogConfigResponse response..."), }, Err(e) => error!("error parsing message: {:?}", e), @@ -181,20 +196,26 @@ impl DiagDevice { } async fn set_log_mask(&mut self, log_type: u32, log_mask_bitsize: u32) -> DiagResult<()> { - let req = build_log_mask_request(log_type, log_mask_bitsize, &LOG_CODES_FOR_RAW_PACKET_LOGGING); + let req = build_log_mask_request( + log_type, + log_mask_bitsize, + &LOG_CODES_FOR_RAW_PACKET_LOGGING, + ); self.write_request(&req).await?; for msg in self.read_response().await? { match msg { Ok(Message::Log { .. }) => info!("skipping log response..."), - Ok(Message::Response { payload, status, .. }) => { + Ok(Message::Response { + payload, status, .. + }) => { if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = payload { if status != 0 { return Err(DiagDeviceError::RequestFailed(status, req)); } return Ok(()); } - }, + } Err(e) => error!("error parsing message: {:?}", e), } } @@ -229,7 +250,7 @@ impl DiagDevice { struct diag_logging_mode_param_t { req_mode: u32, peripheral_mask: u32, - mode_param: u8 + mode_param: u8, } // Triggers the diag device's debug logging mode @@ -254,11 +275,18 @@ fn enable_frame_readwrite(fd: i32, mode: u32) -> DiagResult<()> { fd, DIAG_IOCTL_SWITCH_LOGGING, &mut params as *mut _, - std::mem::size_of::(), 0, 0, 0, 0 + std::mem::size_of::(), + 0, + 0, + 0, + 0, ); if ret < 0 { - let msg = format!("DIAG_IOCTL_SWITCH_LOGGING ioctl failed with error code {}", ret); - return Err(DiagDeviceError::InitializationFailed(msg)) + let msg = format!( + "DIAG_IOCTL_SWITCH_LOGGING ioctl failed with error code {}", + ret + ); + return Err(DiagDeviceError::InitializationFailed(msg)); } } } @@ -272,7 +300,7 @@ fn determine_use_mdm(fd: i32) -> DiagResult { unsafe { if libc::ioctl(fd, DIAG_IOCTL_REMOTE_DEV, &use_mdm as *const i32) < 0 { let msg = format!("DIAG_IOCTL_REMOTE_DEV ioctl failed with error code {}", 0); - return Err(DiagDeviceError::InitializationFailed(msg)) + return Err(DiagDeviceError::InitializationFailed(msg)); } } Ok(use_mdm) diff --git a/lib/src/gsmtap.rs b/lib/src/gsmtap.rs index 337c72e..15ea2a7 100644 --- a/lib/src/gsmtap.rs +++ b/lib/src/gsmtap.rs @@ -6,24 +6,24 @@ use deku::prelude::*; pub enum GsmtapType { Um(UmSubtype), Abis, - UmBurst, /* raw burst bits */ - SIM, /* ISO 7816 smart card interface */ - TetraI1, /* tetra air interface */ + UmBurst, /* raw burst bits */ + SIM, /* ISO 7816 smart card interface */ + TetraI1, /* tetra air interface */ TetraI1Burst, /* tetra air interface */ - WmxBurst, /* WiMAX burst */ - GbLlc, /* GPRS Gb interface: LLC */ - GbSndcp, /* GPRS Gb interface: SNDCP */ - Gmr1Um, /* GMR-1 L2 packets */ + WmxBurst, /* WiMAX burst */ + GbLlc, /* GPRS Gb interface: LLC */ + GbSndcp, /* GPRS Gb interface: SNDCP */ + Gmr1Um, /* GMR-1 L2 packets */ UmtsRlcMac, UmtsRrc(UmtsRrcSubtype), LteRrc(LteRrcSubtype), /* LTE interface */ - LteMac, /* LTE MAC interface */ - LteMacFramed, /* LTE MAC with context hdr */ - OsmocoreLog, /* libosmocore logging */ - QcDiag, /* Qualcomm DIAG frame */ + LteMac, /* LTE MAC interface */ + LteMacFramed, /* LTE MAC with context hdr */ + OsmocoreLog, /* libosmocore logging */ + QcDiag, /* Qualcomm DIAG frame */ LteNas(LteNasSubtype), /* LTE Non-Access Stratum */ - E1T1, /* E1/T1 Lines */ - GsmRlp, /* GSM RLP frames as per 3GPP TS 24.022 */ + E1T1, /* E1/T1 Lines */ + GsmRlp, /* GSM RLP frames as per 3GPP TS 24.022 */ } // based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/src/scat/parsers/qualcomm/diagltelogparser.py#L1337 @@ -119,7 +119,7 @@ pub enum UmtsRrcSubtype { SysInfoTypeSB1 = 58, SysInfoTypeSB2 = 59, ToTargetRNCContainer = 60, - TargetRNCToSourceRNCContainer = 61 + TargetRNCToSourceRNCContainer = 61, } #[repr(u8)] @@ -218,9 +218,7 @@ pub struct GsmtapHeader { } impl GsmtapHeader { - pub fn new( - gsmtap_type: GsmtapType, - ) -> Self { + pub fn new(gsmtap_type: GsmtapType) -> Self { GsmtapHeader { gsmtap_type, version: 2, diff --git a/lib/src/gsmtap_parser.rs b/lib/src/gsmtap_parser.rs index 70c17e8..b1f89e5 100644 --- a/lib/src/gsmtap_parser.rs +++ b/lib/src/gsmtap_parser.rs @@ -13,7 +13,10 @@ pub enum GsmtapParserError { } pub fn parse(msg: Message) -> Result, GsmtapParserError> { - if let Message::Log { timestamp, body, .. } = msg { + if let Message::Log { + timestamp, body, .. + } = msg + { match log_to_gsmtap(body)? { Some(msg) => Ok(Some((timestamp, msg))), None => Ok(None), @@ -25,9 +28,13 @@ pub fn parse(msg: Message) -> Result, GsmtapP fn log_to_gsmtap(value: LogBody) -> Result, GsmtapParserError> { match value { - LogBody::LteRrcOtaMessage { ext_header_version, packet } => { + LogBody::LteRrcOtaMessage { + ext_header_version, + packet, + } => { let gsmtap_type = match ext_header_version { - 0x02 | 0x03 | 0x04 | 0x06 | 0x07 | 0x08 | 0x0d | 0x16 => match packet.get_pdu_num() { + 0x02 | 0x03 | 0x04 | 0x06 | 0x07 | 0x08 | 0x0d | 0x16 => match packet.get_pdu_num() + { 1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch), 2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch), 3 => GsmtapType::LteRrc(LteRrcSubtype::MCCH), @@ -36,7 +43,12 @@ fn log_to_gsmtap(value: LogBody) -> Result, GsmtapParserEr 6 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch), 7 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch), 8 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch), - pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)), + pdu => { + return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum( + ext_header_version, + pdu, + )) + } }, 0x09 | 0x0c => match packet.get_pdu_num() { 8 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch), @@ -47,7 +59,12 @@ fn log_to_gsmtap(value: LogBody) -> Result, GsmtapParserEr 13 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch), 14 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch), 15 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch), - pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)), + pdu => { + return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum( + ext_header_version, + pdu, + )) + } }, 0x0e..=0x10 => match packet.get_pdu_num() { 1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch), @@ -58,7 +75,12 @@ fn log_to_gsmtap(value: LogBody) -> Result, GsmtapParserEr 7 => GsmtapType::LteRrc(LteRrcSubtype::DlDcch), 8 => GsmtapType::LteRrc(LteRrcSubtype::UlCcch), 9 => GsmtapType::LteRrc(LteRrcSubtype::UlDcch), - pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)), + pdu => { + return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum( + ext_header_version, + pdu, + )) + } }, 0x13 | 0x1a | 0x1b => match packet.get_pdu_num() { 1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch), @@ -76,8 +98,13 @@ fn log_to_gsmtap(value: LogBody) -> Result, GsmtapParserEr 49 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb), 50 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb), 52 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb), - pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)), - } + pdu => { + return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum( + ext_header_version, + pdu, + )) + } + }, 0x14 | 0x18 | 0x19 => match packet.get_pdu_num() { 1 => GsmtapType::LteRrc(LteRrcSubtype::BcchBch), 2 => GsmtapType::LteRrc(LteRrcSubtype::BcchDlSch), @@ -94,9 +121,18 @@ fn log_to_gsmtap(value: LogBody) -> Result, GsmtapParserEr 58 => GsmtapType::LteRrc(LteRrcSubtype::DlDcchNb), 59 => GsmtapType::LteRrc(LteRrcSubtype::UlCcchNb), 61 => GsmtapType::LteRrc(LteRrcSubtype::UlDcchNb), - pdu => return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum(ext_header_version, pdu)), + pdu => { + return Err(GsmtapParserError::InvalidLteRrcOtaHeaderPduNum( + ext_header_version, + pdu, + )) + } }, - _ => return Err(GsmtapParserError::InvalidLteRrcOtaExtHeaderVersion(ext_header_version)), + _ => { + return Err(GsmtapParserError::InvalidLteRrcOtaExtHeaderVersion( + ext_header_version, + )) + } }; let mut header = GsmtapHeader::new(gsmtap_type); header.arfcn = packet.get_earfcn().try_into().unwrap_or(0); @@ -106,19 +142,19 @@ fn log_to_gsmtap(value: LogBody) -> Result, GsmtapParserEr header, payload: packet.take_payload(), })) - }, + } LogBody::Nas4GMessage { msg, direction, .. } => { // currently we only handle "plain" (i.e. non-secure) NAS messages let mut header = GsmtapHeader::new(GsmtapType::LteNas(LteNasSubtype::Plain)); - header.uplink = matches!(direction, Nas4GMessageDirection::Uplink); + header.uplink = matches!(direction, Nas4GMessageDirection::Uplink); Ok(Some(GsmtapMessage { header, payload: msg, })) - }, + } _ => { error!("gsmtap_sink: ignoring unhandled log type: {:?}", value); Ok(None) - }, + } } } diff --git a/lib/src/hdlc.rs b/lib/src/hdlc.rs index 22456bd..6ffa8f4 100644 --- a/lib/src/hdlc.rs +++ b/lib/src/hdlc.rs @@ -3,11 +3,14 @@ //! here: //! https://github.com/P1sec/QCSuper/blob/master/docs/The%20Diag%20protocol.md#the-diag-protocol-over-usb -use crc::Crc; use bytes::Buf; +use crc::Crc; use thiserror::Error; -use crate::diag::{MESSAGE_ESCAPE_CHAR, MESSAGE_TERMINATOR, ESCAPED_MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR}; +use crate::diag::{ + ESCAPED_MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR, MESSAGE_ESCAPE_CHAR, + MESSAGE_TERMINATOR, +}; #[derive(Debug, Clone, Error, PartialEq)] pub enum HdlcError { @@ -29,7 +32,9 @@ pub fn hdlc_encapsulate(data: &[u8], crc: &Crc) -> Vec { for &b in data { match b { MESSAGE_TERMINATOR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR]), - MESSAGE_ESCAPE_CHAR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR]), + MESSAGE_ESCAPE_CHAR => { + result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR]) + } _ => result.push(b), } } @@ -37,7 +42,9 @@ pub fn hdlc_encapsulate(data: &[u8], crc: &Crc) -> Vec { for b in crc.checksum(data).to_le_bytes() { match b { MESSAGE_TERMINATOR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_TERMINATOR]), - MESSAGE_ESCAPE_CHAR => result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR]), + MESSAGE_ESCAPE_CHAR => { + result.extend([MESSAGE_ESCAPE_CHAR, ESCAPED_MESSAGE_ESCAPE_CHAR]) + } _ => result.push(b), } } @@ -77,7 +84,10 @@ pub fn hdlc_decapsulate(data: &[u8], crc: &Crc) -> Result, HdlcErro let checksum_lo = unescaped.pop().ok_or(HdlcError::MissingChecksum)?; let checksum = [checksum_lo, checksum_hi].as_slice().get_u16_le(); if checksum != crc.checksum(&unescaped) { - return Err(HdlcError::InvalidChecksum(checksum, crc.checksum(&unescaped))); + return Err(HdlcError::InvalidChecksum( + checksum, + crc.checksum(&unescaped), + )); } Ok(unescaped) diff --git a/lib/src/lib.rs b/lib/src/lib.rs index a71cf33..7e47f59 100644 --- a/lib/src/lib.rs +++ b/lib/src/lib.rs @@ -1,11 +1,11 @@ -pub mod hdlc; +pub mod analysis; pub mod diag; -pub mod qmdl; -pub mod log_codes; pub mod gsmtap; pub mod gsmtap_parser; +pub mod hdlc; +pub mod log_codes; pub mod pcap; -pub mod analysis; +pub mod qmdl; pub mod util; // bin/check.rs may target windows and does not use this mod diff --git a/lib/src/log_codes.rs b/lib/src/log_codes.rs index 6035980..b337e99 100644 --- a/lib/src/log_codes.rs +++ b/lib/src/log_codes.rs @@ -1,6 +1,5 @@ //! Enumerates some relevant diag log codes. Copied from QCSuper - // These are 2G-related log types. pub const LOG_GSM_RR_SIGNALING_MESSAGE_C: u32 = 0x512f; @@ -95,12 +94,10 @@ pub const RRCLOG_SIG_DL_MSCH: u32 = 8; pub const RRCLOG_EXTENSION_SIB: u32 = 9; pub const RRCLOG_SIB_CONTAINER: u32 = 10; - // 3G layer 3 packets: pub const WCDMA_SIGNALLING_MESSAGE: u32 = 0x412f; - // Upper layers pub const LOG_DATA_PROTOCOL_LOGGING_C: u32 = 0x11eb; diff --git a/lib/src/pcap.rs b/lib/src/pcap.rs index a6f7b1d..3e0d59d 100644 --- a/lib/src/pcap.rs +++ b/lib/src/pcap.rs @@ -1,10 +1,8 @@ -//! Parse QMDL files and create a pcap file. -//! Creates a plausible IP header and [GSMtap](https://osmocom.org/projects/baseband/wiki/GSMTAP) header and then puts the rest of the data under that for wireshark to parse. -use crate::gsmtap::GsmtapMessage; +//! Parse QMDL files and create a pcap file. +//! Creates a plausible IP header and [GSMtap](https://osmocom.org/projects/baseband/wiki/GSMTAP) header and then puts the rest of the data under that for wireshark to parse. use crate::diag::Timestamp; +use crate::gsmtap::GsmtapMessage; -use tokio::io::AsyncWrite; -use std::borrow::Cow; use chrono::prelude::*; use deku::prelude::*; use pcap_file_tokio::pcapng::blocks::enhanced_packet::EnhancedPacketBlock; @@ -12,7 +10,9 @@ use pcap_file_tokio::pcapng::blocks::interface_description::InterfaceDescription use pcap_file_tokio::pcapng::blocks::section_header::{SectionHeaderBlock, SectionHeaderOption}; use pcap_file_tokio::pcapng::PcapNgWriter; use pcap_file_tokio::{Endianness, PcapError}; +use std::borrow::Cow; use thiserror::Error; +use tokio::io::AsyncWrite; #[derive(Error, Debug)] pub enum GsmtapPcapError { @@ -26,7 +26,10 @@ pub enum GsmtapPcapError { Deku(#[from] DekuError), } -pub struct GsmtapPcapWriter where T: AsyncWrite { +pub struct GsmtapPcapWriter +where + T: AsyncWrite, +{ writer: PcapNgWriter, ip_id: u16, } @@ -59,10 +62,17 @@ struct UdpHeader { checksum: u16, } -impl GsmtapPcapWriter where T: AsyncWrite + Unpin + Send { +impl GsmtapPcapWriter +where + T: AsyncWrite + Unpin + Send, +{ pub async fn new(writer: T) -> Result { let metadata = crate::util::RuntimeMetadata::new(); - let package = format!("{} {}", env!("CARGO_PKG_NAME").to_owned(), metadata.rayhunter_version); + let package = format!( + "{} {}", + env!("CARGO_PKG_NAME").to_owned(), + metadata.rayhunter_version + ); let section = SectionHeaderBlock { endianness: Endianness::Big, major_version: 1, @@ -88,8 +98,13 @@ impl GsmtapPcapWriter where T: AsyncWrite + Unpin + Send { Ok(()) } - pub async fn write_gsmtap_message(&mut self, msg: GsmtapMessage, timestamp: Timestamp) -> Result<(), GsmtapPcapError> { - let duration = timestamp.to_datetime() + pub async fn write_gsmtap_message( + &mut self, + msg: GsmtapMessage, + timestamp: Timestamp, + ) -> Result<(), GsmtapPcapError> { + let duration = timestamp + .to_datetime() .signed_duration_since(DateTime::UNIX_EPOCH) .to_std()?; diff --git a/lib/src/qmdl.rs b/lib/src/qmdl.rs index aae1a85..0e89d62 100644 --- a/lib/src/qmdl.rs +++ b/lib/src/qmdl.rs @@ -3,18 +3,24 @@ //! QmdlReader and QmdlWriter can read and write MessagesContainers to and from //! QMDL files. -use crate::diag::{MessagesContainer, MESSAGE_TERMINATOR, HdlcEncapsulatedMessage, DataType}; +use crate::diag::{DataType, HdlcEncapsulatedMessage, MessagesContainer, MESSAGE_TERMINATOR}; use futures::TryStream; -use tokio::io::{AsyncRead, AsyncWrite, AsyncWriteExt, BufReader, AsyncBufReadExt}; use log::error; +use tokio::io::{AsyncBufReadExt, AsyncRead, AsyncWrite, AsyncWriteExt, BufReader}; -pub struct QmdlWriter where T: AsyncWrite + Unpin { +pub struct QmdlWriter +where + T: AsyncWrite + Unpin, +{ writer: T, pub total_written: usize, } -impl QmdlWriter where T: AsyncWrite + Unpin { +impl QmdlWriter +where + T: AsyncWrite + Unpin, +{ pub fn new(writer: T) -> Self { QmdlWriter::new_with_existing_size(writer, 0) } @@ -35,13 +41,19 @@ impl QmdlWriter where T: AsyncWrite + Unpin { } } -pub struct QmdlReader where T: AsyncRead { +pub struct QmdlReader +where + T: AsyncRead, +{ reader: BufReader, bytes_read: usize, max_bytes: Option, } -impl QmdlReader where T: AsyncRead + Unpin { +impl QmdlReader +where + T: AsyncRead + Unpin, +{ pub fn new(reader: T, max_bytes: Option) -> Self { QmdlReader { reader: BufReader::new(reader), @@ -50,21 +62,28 @@ impl QmdlReader where T: AsyncRead + Unpin { } } - pub fn as_stream(&mut self) -> impl TryStream + '_ { + pub fn as_stream( + &mut self, + ) -> impl TryStream + '_ { futures::stream::try_unfold(self, |reader| async { let maybe_container = reader.get_next_messages_container().await?; match maybe_container { Some(container) => Ok(Some((container, reader))), - None => Ok(None) + None => Ok(None), } }) } - pub async fn get_next_messages_container(&mut self) -> Result, std::io::Error> { + pub async fn get_next_messages_container( + &mut self, + ) -> Result, std::io::Error> { if let Some(max_bytes) = self.max_bytes { if self.bytes_read >= max_bytes { if self.bytes_read > max_bytes { - error!("warning: {} bytes read, but max_bytes was {}", self.bytes_read, max_bytes); + error!( + "warning: {} bytes read, but max_bytes was {}", + self.bytes_read, max_bytes + ); } return Ok(None); } @@ -82,12 +101,10 @@ impl QmdlReader where T: AsyncRead + Unpin { Ok(Some(MessagesContainer { data_type: DataType::UserSpace, num_messages: 1, - messages: vec![ - HdlcEncapsulatedMessage { - len: bytes_read as u32, - data: buf, - }, - ] + messages: vec![HdlcEncapsulatedMessage { + len: bytes_read as u32, + data: buf, + }], })) } } @@ -96,26 +113,29 @@ impl QmdlReader where T: AsyncRead + Unpin { mod test { use std::io::Cursor; - use crate::hdlc::hdlc_encapsulate; use crate::diag::CRC_CCITT; + use crate::hdlc::hdlc_encapsulate; use super::*; fn get_test_messages() -> Vec { - let messages: Vec = (10..20).map(|i| { - let data = hdlc_encapsulate(&vec![i as u8; i], &CRC_CCITT); - HdlcEncapsulatedMessage { - len: data.len() as u32, - data, - } - }).collect(); + let messages: Vec = (10..20) + .map(|i| { + let data = hdlc_encapsulate(&vec![i as u8; i], &CRC_CCITT); + HdlcEncapsulatedMessage { + len: data.len() as u32, + data, + } + }) + .collect(); messages } // returns a byte array consisting of concatenated HDLC encapsulated // test messages fn get_test_message_bytes() -> Vec { - get_test_messages().iter() + get_test_messages() + .iter() .flat_map(|msg| msg.data.clone()) .collect() } @@ -132,7 +152,7 @@ mod test { MessagesContainer { data_type: DataType::UserSpace, num_messages: messages2.len() as u32, - messages: messages2.to_vec() + messages: messages2.to_vec(), }, ] } @@ -148,7 +168,10 @@ mod test { num_messages: 1, messages: vec![message], }; - assert_eq!(expected_container, reader.get_next_messages_container().await.unwrap().unwrap()); + assert_eq!( + expected_container, + reader.get_next_messages_container().await.unwrap().unwrap() + ); } } @@ -167,9 +190,15 @@ mod test { num_messages: 1, messages: vec![message], }; - assert_eq!(expected_container, reader.get_next_messages_container().await.unwrap().unwrap()); + assert_eq!( + expected_container, + reader.get_next_messages_container().await.unwrap().unwrap() + ); } - assert!(matches!(reader.get_next_messages_container().await, Ok(None))); + assert!(matches!( + reader.get_next_messages_container().await, + Ok(None) + )); } #[tokio::test] @@ -202,8 +231,14 @@ mod test { num_messages: 1, messages: vec![message], }; - assert_eq!(expected_container, reader.get_next_messages_container().await.unwrap().unwrap()); + assert_eq!( + expected_container, + reader.get_next_messages_container().await.unwrap().unwrap() + ); } - assert!(matches!(reader.get_next_messages_container().await, Ok(None))); + assert!(matches!( + reader.get_next_messages_container().await, + Ok(None) + )); } } diff --git a/lib/tests/test_lte_parsing.rs b/lib/tests/test_lte_parsing.rs index 475545a..f1e7a15 100644 --- a/lib/tests/test_lte_parsing.rs +++ b/lib/tests/test_lte_parsing.rs @@ -1,42 +1,46 @@ -use rayhunter::{diag::{ - LogBody, LteRrcOtaPacket, Message, Timestamp -}, gsmtap_parser}; use deku::prelude::*; +use rayhunter::{ + diag::{LogBody, LteRrcOtaPacket, Message, Timestamp}, + gsmtap_parser, +}; // Tests here are based on https://github.com/fgsect/scat/blob/97442580e628de414c9f7c2a185f4e28d0ee7523/tests/test_diagltelogparser.py #[test] fn test_lte_rrc_ota() { let v26_binary = &[ - 0x10, 0x0, 0x23, 0x0, 0x23, 0x0, 0xc0, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x0, - 0x0, 0x0, 0x0, 0x1a, 0xf, 0x40, 0xf, 0x40, 0x1, 0xe, 0x1, 0x13, 0x7, - 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, 0x0, 0x2, 0x0, 0x10, 0x15 + 0x10, 0x0, 0x23, 0x0, 0x23, 0x0, 0xc0, 0xb0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x0, 0x1a, + 0xf, 0x40, 0xf, 0x40, 0x1, 0xe, 0x1, 0x13, 0x7, 0x0, 0x0, 0x0, 0x0, 0xb, 0x0, 0x0, 0x0, + 0x0, 0x2, 0x0, 0x10, 0x15, ]; let (_, parsed) = Message::from_bytes((v26_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 0x23, - inner_length: 0x23, - timestamp: Timestamp { ts: 0 }, - log_type: 0xb0c0, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 26, - packet: LteRrcOtaPacket::V25 { - rrc_rel_maj: 15, - rrc_rel_min: 64, - nr_rrc_rel_maj: 15, - nr_rrc_rel_min: 64, - bearer_id: 1, - phy_cell_id: 270, - earfcn: 1811, - sfn_subfn: 0, - pdu_num: 11, - sib_mask: 0, - len: 2, - packet: vec![0x10, 0x15], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 0x23, + inner_length: 0x23, + timestamp: Timestamp { ts: 0 }, + log_type: 0xb0c0, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 26, + packet: LteRrcOtaPacket::V25 { + rrc_rel_maj: 15, + rrc_rel_min: 64, + nr_rrc_rel_maj: 15, + nr_rrc_rel_min: 64, + bearer_id: 1, + phy_cell_id: 270, + earfcn: 1811, + sfn_subfn: 0, + pdu_num: 11, + sib_mask: 0, + len: 2, + packet: vec![0x10, 0x15], + } } } - }); + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); assert_eq!(&gsmtap_msg.payload, &[0x10, 0x15]); assert_eq!(gsmtap_msg.header.packet_type, 13); @@ -49,41 +53,40 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 0); let v26_binary = &[ - 0x10, 0x00, 0x23, 0x00, 0x23, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x1a, 0x0f, 0x40, 0x0f, 0x40, 0x01, 0x0e, 0x01, - 0x13, 0x07, 0x00, 0x00, 0x00, 0x00, 0x0b, 0x00, - 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x15, + 0x10, 0x00, 0x23, 0x00, 0x23, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x1a, 0x0f, 0x40, 0x0f, 0x40, 0x01, 0x0e, 0x01, 0x13, 0x07, 0x00, 0x00, 0x00, 0x00, + 0x0b, 0x00, 0x00, 0x00, 0x00, 0x02, 0x00, 0x10, 0x15, ]; let (_, parsed) = Message::from_bytes((v26_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 35, - inner_length: 35, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 26, - packet: LteRrcOtaPacket::V25 { - rrc_rel_maj: 15, - rrc_rel_min: 64, - nr_rrc_rel_maj: 15, - nr_rrc_rel_min: 64, - bearer_id: 1, - phy_cell_id: 270, - earfcn: 1811, - sfn_subfn: 0, - pdu_num: 11, - sib_mask: 0, - len: 2, - packet: vec![0x10, 0x15], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 35, + inner_length: 35, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 26, + packet: LteRrcOtaPacket::V25 { + rrc_rel_maj: 15, + rrc_rel_min: 64, + nr_rrc_rel_maj: 15, + nr_rrc_rel_min: 64, + bearer_id: 1, + phy_cell_id: 270, + earfcn: 1811, + sfn_subfn: 0, + pdu_num: 11, + sib_mask: 0, + len: 2, + packet: vec![0x10, 0x15], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); - assert_eq!(&gsmtap_msg.payload, &[ - 0x10, 0x15, - ]); + assert_eq!(&gsmtap_msg.payload, &[0x10, 0x15,]); assert_eq!(gsmtap_msg.header.packet_type, 13); assert_eq!(gsmtap_msg.header.timeslot, 0); assert_eq!(gsmtap_msg.header.arfcn, 1811); @@ -94,44 +97,44 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 0); let v24_binary = &[ - 0x10, 0x00, 0x2c, 0x00, 0x2c, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x18, 0x0f, 0x22, 0x00, 0x68, 0x00, 0xe4, 0x0c, - 0x00, 0x00, 0x09, 0xdc, 0x05, 0x00, 0x00, 0x00, - 0x00, 0x0d, 0x00, 0x40, 0x85, 0x8e, 0xc4, 0xe5, - 0xbf, 0xe0, 0x50, 0xdc, 0x29, 0x15, 0x16, 0x00, + 0x10, 0x00, 0x2c, 0x00, 0x2c, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x18, 0x0f, 0x22, 0x00, 0x68, 0x00, 0xe4, 0x0c, 0x00, 0x00, 0x09, 0xdc, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x0d, 0x00, 0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, 0xdc, 0x29, + 0x15, 0x16, 0x00, ]; let (_, parsed) = Message::from_bytes((v24_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 44, - inner_length: 44, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 24, - packet: LteRrcOtaPacket::V8 { - rrc_rel_maj: 15, - rrc_rel_min: 34, - bearer_id: 0, - phy_cell_id: 104, - earfcn: 3300, - sfn_subfn: 56329, - pdu_num: 5, - sib_mask: 0, - len: 13, - packet: vec![ - 0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, 0xdc, 0x29, - 0x15, 0x16, 0x0 - ], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 44, + inner_length: 44, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 24, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 15, + rrc_rel_min: 34, + bearer_id: 0, + phy_cell_id: 104, + earfcn: 3300, + sfn_subfn: 56329, + pdu_num: 5, + sib_mask: 0, + len: 13, + packet: vec![ + 0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, 0xdc, 0x29, 0x15, 0x16, 0x0 + ], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); - assert_eq!(&gsmtap_msg.payload, &[ - 0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, - 0xdc, 0x29, 0x15, 0x16, 0x00, - ]); + assert_eq!( + &gsmtap_msg.payload, + &[0x40, 0x85, 0x8e, 0xc4, 0xe5, 0xbf, 0xe0, 0x50, 0xdc, 0x29, 0x15, 0x16, 0x00,] + ); assert_eq!(gsmtap_msg.header.packet_type, 13); assert_eq!(gsmtap_msg.header.timeslot, 0); assert_eq!(gsmtap_msg.header.arfcn, 3300); @@ -142,48 +145,48 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 9); let v20_binary = &[ - 0x10, 0x00, 0x37, 0x00, 0x37, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x14, 0x0e, 0x30, 0x01, 0x09, 0x01, 0x9c, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x00, 0x18, 0x00, 0x08, 0x10, 0xa7, 0x14, 0x53, - 0x59, 0xa6, 0x05, 0x43, 0x68, 0xc0, 0x3b, 0xda, - 0x30, 0x04, 0xa6, 0x88, 0x02, 0x8d, 0xa2, 0x00, - 0x9a, 0x68, 0x40, + 0x10, 0x00, 0x37, 0x00, 0x37, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x14, 0x0e, 0x30, 0x01, 0x09, 0x01, 0x9c, 0x18, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x18, 0x00, 0x08, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x05, 0x43, 0x68, + 0xc0, 0x3b, 0xda, 0x30, 0x04, 0xa6, 0x88, 0x02, 0x8d, 0xa2, 0x00, 0x9a, 0x68, 0x40, ]; let (_, parsed) = Message::from_bytes((v20_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 55, - inner_length: 55, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 20, - packet: LteRrcOtaPacket::V8 { - rrc_rel_maj: 14, - rrc_rel_min: 48, - bearer_id: 1, - phy_cell_id: 265, - earfcn: 6300, - sfn_subfn: 0, - pdu_num: 9, - sib_mask: 0, - len: 24, - packet: vec![ - 0x8, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x5, 0x43, 0x68, - 0xc0, 0x3b, 0xda, 0x30, 0x4, 0xa6, 0x88, 0x2, 0x8d, 0xa2, - 0x0, 0x9a, 0x68, 0x40 - ], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 55, + inner_length: 55, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 20, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 14, + rrc_rel_min: 48, + bearer_id: 1, + phy_cell_id: 265, + earfcn: 6300, + sfn_subfn: 0, + pdu_num: 9, + sib_mask: 0, + len: 24, + packet: vec![ + 0x8, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x5, 0x43, 0x68, 0xc0, 0x3b, 0xda, + 0x30, 0x4, 0xa6, 0x88, 0x2, 0x8d, 0xa2, 0x0, 0x9a, 0x68, 0x40 + ], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); - assert_eq!(&gsmtap_msg.payload, &[ - 0x08, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x05, - 0x43, 0x68, 0xc0, 0x3b, 0xda, 0x30, 0x04, 0xa6, - 0x88, 0x02, 0x8d, 0xa2, 0x00, 0x9a, 0x68, 0x40, - ]); + assert_eq!( + &gsmtap_msg.payload, + &[ + 0x08, 0x10, 0xa7, 0x14, 0x53, 0x59, 0xa6, 0x05, 0x43, 0x68, 0xc0, 0x3b, 0xda, 0x30, + 0x04, 0xa6, 0x88, 0x02, 0x8d, 0xa2, 0x00, 0x9a, 0x68, 0x40, + ] + ); assert_eq!(gsmtap_msg.header.packet_type, 13); assert_eq!(gsmtap_msg.header.timeslot, 0); assert_eq!(gsmtap_msg.header.arfcn, 6300); @@ -194,41 +197,41 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 0); let v19_binary = &[ - 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x13, 0x0e, 0x22, 0x00, 0x0b, 0x00, 0xfa, 0x09, - 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, 0x00, 0x00, - 0x00, 0x09, 0x00, 0x28, 0x18, 0x40, 0x16, 0x08, - 0x08, 0x80, 0x00, 0x00, + 0x10, 0x00, 0x28, 0x00, 0x28, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x13, 0x0e, 0x22, 0x00, 0x0b, 0x00, 0xfa, 0x09, 0x00, 0x00, 0x00, 0x00, 0x32, 0x00, + 0x00, 0x00, 0x00, 0x09, 0x00, 0x28, 0x18, 0x40, 0x16, 0x08, 0x08, 0x80, 0x00, 0x00, ]; let (_, parsed) = Message::from_bytes((v19_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 40, - inner_length: 40, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 19, - packet: LteRrcOtaPacket::V8 { - rrc_rel_maj: 14, - rrc_rel_min: 34, - bearer_id: 0, - phy_cell_id: 11, - earfcn: 2554, - sfn_subfn: 0, - pdu_num: 50, - sib_mask: 0, - len: 9, - packet: vec![0x28, 0x18, 0x40, 0x16, 0x8, 0x8, 0x80, 0x0, 0x0], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 40, + inner_length: 40, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 19, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 14, + rrc_rel_min: 34, + bearer_id: 0, + phy_cell_id: 11, + earfcn: 2554, + sfn_subfn: 0, + pdu_num: 50, + sib_mask: 0, + len: 9, + packet: vec![0x28, 0x18, 0x40, 0x16, 0x8, 0x8, 0x80, 0x0, 0x0], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); - assert_eq!(&gsmtap_msg.payload, &[ - 0x28, 0x18, 0x40, 0x16, 0x08, 0x08, 0x80, 0x00, - 0x00, - ]); + assert_eq!( + &gsmtap_msg.payload, + &[0x28, 0x18, 0x40, 0x16, 0x08, 0x08, 0x80, 0x00, 0x00,] + ); assert_eq!(gsmtap_msg.header.packet_type, 13); assert_eq!(gsmtap_msg.header.timeslot, 0); assert_eq!(gsmtap_msg.header.arfcn, 2554); @@ -239,40 +242,41 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 0); let v15_binary = &[ - 0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0x0d, 0x21, 0x00, 0x9e, 0x00, 0x14, 0x05, - 0x00, 0x00, 0x49, 0x8c, 0x05, 0x00, 0x00, 0x00, - 0x00, 0x07, 0x00, 0x40, 0x0c, 0x8e, 0xc9, 0x42, - 0x89, 0xe0, + 0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0d, 0x21, 0x00, 0x9e, 0x00, 0x14, 0x05, 0x00, 0x00, 0x49, 0x8c, 0x05, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x40, 0x0c, 0x8e, 0xc9, 0x42, 0x89, 0xe0, ]; let (_, parsed) = Message::from_bytes((v15_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 38, - inner_length: 38, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 15, - packet: LteRrcOtaPacket::V8 { - rrc_rel_maj: 13, - rrc_rel_min: 33, - bearer_id: 0, - phy_cell_id: 158, - earfcn: 1300, - sfn_subfn: 35913, - pdu_num: 5, - sib_mask: 0, - len: 7, - packet: vec![0x40, 0xc, 0x8e, 0xc9, 0x42, 0x89, 0xe0], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 38, + inner_length: 38, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 15, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 13, + rrc_rel_min: 33, + bearer_id: 0, + phy_cell_id: 158, + earfcn: 1300, + sfn_subfn: 35913, + pdu_num: 5, + sib_mask: 0, + len: 7, + packet: vec![0x40, 0xc, 0x8e, 0xc9, 0x42, 0x89, 0xe0], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); - assert_eq!(&gsmtap_msg.payload, &[ - 0x40, 0x0c, 0x8e, 0xc9, 0x42, 0x89, 0xe0, - ]); + assert_eq!( + &gsmtap_msg.payload, + &[0x40, 0x0c, 0x8e, 0xc9, 0x42, 0x89, 0xe0,] + ); assert_eq!(gsmtap_msg.header.packet_type, 13); assert_eq!(gsmtap_msg.header.timeslot, 0); assert_eq!(gsmtap_msg.header.arfcn, 1300); @@ -283,49 +287,50 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 9); let v15_binary = &[ - 0x10, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0f, 0x0d, 0x21, 0x01, 0x9e, 0x00, 0x14, 0x05, - 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, 0x00, 0x00, - 0x00, 0x1c, 0x00, 0x08, 0x10, 0xa5, 0x34, 0x61, - 0x41, 0xa3, 0x1c, 0x31, 0x68, 0x04, 0x40, 0x1a, - 0x00, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x00, - 0x10, 0x67, 0xc1, 0x06, 0xd9, 0xe0, 0x00, + 0x10, 0x00, 0x3b, 0x00, 0x3b, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0f, 0x0d, 0x21, 0x01, 0x9e, 0x00, 0x14, 0x05, 0x00, 0x00, 0x00, 0x00, 0x09, 0x00, + 0x00, 0x00, 0x00, 0x1c, 0x00, 0x08, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, 0x31, 0x68, + 0x04, 0x40, 0x1a, 0x00, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x00, 0x10, 0x67, 0xc1, 0x06, + 0xd9, 0xe0, 0x00, ]; let (_, parsed) = Message::from_bytes((v15_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 59, - inner_length: 59, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 15, - packet: LteRrcOtaPacket::V8 { - rrc_rel_maj: 13, - rrc_rel_min: 33, - bearer_id: 1, - phy_cell_id: 158, - earfcn: 1300, - sfn_subfn: 0, - pdu_num: 9, - sib_mask: 0, - len: 28, - packet: vec![ - 0x8, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, 0x31, 0x68, - 0x4, 0x40, 0x1a, 0x0, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, - 0x0, 0x10, 0x67, 0xc1, 0x6, 0xd9, 0xe0, 0x0 - ], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 59, + inner_length: 59, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 15, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 13, + rrc_rel_min: 33, + bearer_id: 1, + phy_cell_id: 158, + earfcn: 1300, + sfn_subfn: 0, + pdu_num: 9, + sib_mask: 0, + len: 28, + packet: vec![ + 0x8, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, 0x31, 0x68, 0x4, 0x40, 0x1a, + 0x0, 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x0, 0x10, 0x67, 0xc1, 0x6, 0xd9, + 0xe0, 0x0 + ], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); - assert_eq!(&gsmtap_msg.payload, &[ - 0x08, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, - 0x31, 0x68, 0x04, 0x40, 0x1a, 0x00, 0x49, 0x16, - 0x7c, 0x23, 0x15, 0x9f, 0x00, 0x10, 0x67, 0xc1, - 0x06, 0xd9, 0xe0, 0x00, - ]); + assert_eq!( + &gsmtap_msg.payload, + &[ + 0x08, 0x10, 0xa5, 0x34, 0x61, 0x41, 0xa3, 0x1c, 0x31, 0x68, 0x04, 0x40, 0x1a, 0x00, + 0x49, 0x16, 0x7c, 0x23, 0x15, 0x9f, 0x00, 0x10, 0x67, 0xc1, 0x06, 0xd9, 0xe0, 0x00, + ] + ); assert_eq!(gsmtap_msg.header.packet_type, 13); assert_eq!(gsmtap_msg.header.timeslot, 0); assert_eq!(gsmtap_msg.header.arfcn, 1300); @@ -336,35 +341,36 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 0); let v13_binary = &[ - 0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x0d, 0x0c, 0x74, 0x01, 0x32, 0x00, 0x38, 0x18, - 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x2c, 0x00, + 0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x0d, 0x0c, 0x74, 0x01, 0x32, 0x00, 0x38, 0x18, 0x00, 0x00, 0x00, 0x00, 0x08, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x2c, 0x00, ]; let (_, parsed) = Message::from_bytes((v13_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 33, - inner_length: 33, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 13, - packet: LteRrcOtaPacket::V8 { - rrc_rel_maj: 12, - rrc_rel_min: 116, - bearer_id: 1, - phy_cell_id: 50, - earfcn: 6200, - sfn_subfn: 0, - pdu_num: 8, - sib_mask: 0, - len: 2, - packet: vec![0x2c, 0x0], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 33, + inner_length: 33, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 13, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 12, + rrc_rel_min: 116, + bearer_id: 1, + phy_cell_id: 50, + earfcn: 6200, + sfn_subfn: 0, + pdu_num: 8, + sib_mask: 0, + len: 2, + packet: vec![0x2c, 0x0], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); assert_eq!(&gsmtap_msg.payload, &[0x2c, 0x00]); assert_eq!(gsmtap_msg.header.packet_type, 13); @@ -377,40 +383,41 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 0); let v9_binary = &[ - 0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x09, 0x0b, 0x70, 0x00, 0x00, 0x01, 0x14, 0x05, - 0x00, 0x00, 0x09, 0x91, 0x0b, 0x00, 0x00, 0x00, - 0x00, 0x07, 0x00, 0x40, 0x0b, 0x8e, 0xc1, 0xdd, - 0x13, 0xb0, + 0x10, 0x00, 0x26, 0x00, 0x26, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x09, 0x0b, 0x70, 0x00, 0x00, 0x01, 0x14, 0x05, 0x00, 0x00, 0x09, 0x91, 0x0b, 0x00, + 0x00, 0x00, 0x00, 0x07, 0x00, 0x40, 0x0b, 0x8e, 0xc1, 0xdd, 0x13, 0xb0, ]; let (_, parsed) = Message::from_bytes((v9_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 38, - inner_length: 38, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 9, - packet: LteRrcOtaPacket::V8 { - rrc_rel_maj: 11, - rrc_rel_min: 112, - bearer_id: 0, - phy_cell_id: 256, - earfcn: 1300, - sfn_subfn: 37129, - pdu_num: 11, - sib_mask: 0, - len: 7, - packet: vec![0x40, 0xb, 0x8e, 0xc1, 0xdd, 0x13, 0xb0], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 38, + inner_length: 38, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 9, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 11, + rrc_rel_min: 112, + bearer_id: 0, + phy_cell_id: 256, + earfcn: 1300, + sfn_subfn: 37129, + pdu_num: 11, + sib_mask: 0, + len: 7, + packet: vec![0x40, 0xb, 0x8e, 0xc1, 0xdd, 0x13, 0xb0], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); - assert_eq!(&gsmtap_msg.payload, &[ - 0x40, 0x0b, 0x8e, 0xc1, 0xdd, 0x13, 0xb0, - ]); + assert_eq!( + &gsmtap_msg.payload, + &[0x40, 0x0b, 0x8e, 0xc1, 0xdd, 0x13, 0xb0,] + ); assert_eq!(gsmtap_msg.header.packet_type, 13); assert_eq!(gsmtap_msg.header.timeslot, 0); assert_eq!(gsmtap_msg.header.arfcn, 1300); @@ -421,35 +428,36 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 9); let v8_binary = &[ - 0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x08, 0x0a, 0x72, 0x01, 0x0e, 0x00, 0x9c, 0x18, - 0x00, 0x00, 0xa9, 0x33, 0x06, 0x00, 0x00, 0x00, - 0x00, 0x02, 0x00, 0x2e, 0x02, + 0x10, 0x00, 0x21, 0x00, 0x21, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x08, 0x0a, 0x72, 0x01, 0x0e, 0x00, 0x9c, 0x18, 0x00, 0x00, 0xa9, 0x33, 0x06, 0x00, + 0x00, 0x00, 0x00, 0x02, 0x00, 0x2e, 0x02, ]; let (_, parsed) = Message::from_bytes((v8_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 33, - inner_length: 33, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 8, - packet: LteRrcOtaPacket::V8 { - rrc_rel_maj: 10, - rrc_rel_min: 114, - bearer_id: 1, - phy_cell_id: 14, - earfcn: 6300, - sfn_subfn: 13225, - pdu_num: 6, - sib_mask: 0, - len: 2, - packet: vec![0x2e, 0x2], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 33, + inner_length: 33, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 8, + packet: LteRrcOtaPacket::V8 { + rrc_rel_maj: 10, + rrc_rel_min: 114, + bearer_id: 1, + phy_cell_id: 14, + earfcn: 6300, + sfn_subfn: 13225, + pdu_num: 6, + sib_mask: 0, + len: 2, + packet: vec![0x2e, 0x2], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); assert_eq!(&gsmtap_msg.payload, &[0x2e, 0x02]); assert_eq!(gsmtap_msg.header.packet_type, 13); @@ -462,46 +470,48 @@ fn test_lte_rrc_ota() { assert_eq!(gsmtap_msg.header.subslot, 9); let v6_binary = &[ - 0x10, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0xc0, 0xb0, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x06, 0x09, 0xb1, 0x00, 0x07, 0x01, 0x2c, 0x07, - 0x25, 0x34, 0x02, 0x02, 0x00, 0x00, 0x00, 0x12, - 0x00, 0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02, - 0xd3, 0xb0, 0x98, 0x1c, 0x20, 0xa0, 0x81, 0x8c, - 0x43, 0x26, 0xd0, + 0x10, 0x00, 0x2f, 0x00, 0x2f, 0x00, 0xc0, 0xb0, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x06, 0x09, 0xb1, 0x00, 0x07, 0x01, 0x2c, 0x07, 0x25, 0x34, 0x02, 0x02, 0x00, 0x00, + 0x00, 0x12, 0x00, 0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02, 0xd3, 0xb0, 0x98, 0x1c, 0x20, + 0xa0, 0x81, 0x8c, 0x43, 0x26, 0xd0, ]; let (_, parsed) = Message::from_bytes((v6_binary, 0)).unwrap(); - assert_eq!(&parsed, &Message::Log { - pending_msgs: 0, - outer_length: 47, - inner_length: 47, - timestamp: Timestamp { ts: 0 }, - log_type: 45248, - body: LogBody::LteRrcOtaMessage { - ext_header_version: 6, - packet: LteRrcOtaPacket::V5 { - rrc_rel_maj: 9, - rrc_rel_min: 177, - bearer_id: 0, - phy_cell_id: 263, - earfcn: 1836, - sfn_subfn: 13349, - pdu_num: 2, - sib_mask: 2, - len: 18, - packet: vec![ - 0x40, 0x49, 0x88, 0x5, 0xc0, 0x97, 0x2, 0xd3, 0xb0, 0x98, - 0x1c, 0x20, 0xa0, 0x81, 0x8c, 0x43, 0x26, 0xd0 - ], + assert_eq!( + &parsed, + &Message::Log { + pending_msgs: 0, + outer_length: 47, + inner_length: 47, + timestamp: Timestamp { ts: 0 }, + log_type: 45248, + body: LogBody::LteRrcOtaMessage { + ext_header_version: 6, + packet: LteRrcOtaPacket::V5 { + rrc_rel_maj: 9, + rrc_rel_min: 177, + bearer_id: 0, + phy_cell_id: 263, + earfcn: 1836, + sfn_subfn: 13349, + pdu_num: 2, + sib_mask: 2, + len: 18, + packet: vec![ + 0x40, 0x49, 0x88, 0x5, 0xc0, 0x97, 0x2, 0xd3, 0xb0, 0x98, 0x1c, 0x20, 0xa0, + 0x81, 0x8c, 0x43, 0x26, 0xd0 + ], + }, }, - }, - }); + } + ); let (_, gsmtap_msg) = gsmtap_parser::parse(parsed).unwrap().unwrap(); - assert_eq!(&gsmtap_msg.payload, &[ - 0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02, 0xd3, - 0xb0, 0x98, 0x1c, 0x20, 0xa0, 0x81, 0x8c, 0x43, - 0x26, 0xd0, - ]); + assert_eq!( + &gsmtap_msg.payload, + &[ + 0x40, 0x49, 0x88, 0x05, 0xc0, 0x97, 0x02, 0xd3, 0xb0, 0x98, 0x1c, 0x20, 0xa0, 0x81, + 0x8c, 0x43, 0x26, 0xd0, + ] + ); assert_eq!(gsmtap_msg.header.packet_type, 13); assert_eq!(gsmtap_msg.header.timeslot, 0); assert_eq!(gsmtap_msg.header.arfcn, 1836); diff --git a/rootshell/src/main.rs b/rootshell/src/main.rs index f06c95b..a8ee845 100644 --- a/rootshell/src/main.rs +++ b/rootshell/src/main.rs @@ -1,10 +1,10 @@ //! a simple shell for uploading to the orbic device. -//! +//! //! It literally just runs bash as UID/GID 0, with special Android GIDs 3003 //! (AID_INET) and 3004 (AID_NET_RAW). -use std::process::Command; -use std::os::unix::process::CommandExt; use std::env; +use std::os::unix::process::CommandExt; +use std::process::Command; #[cfg(target_arch = "arm")] use nix::unistd::Gid; @@ -15,22 +15,19 @@ fn main() { // Android's "paranoid network" feature restricts network access to // processes in specific groups. More info here: // https://www.elinux.org/Android_Security#Paranoid_network-ing - #[cfg(target_arch = "arm")] { - let gids = &[ - Gid::from_raw(3003), // AID_INET - Gid::from_raw(3004), // AID_NET_RAW - ]; - nix::unistd::setgroups(gids).expect("setgroups failed"); + #[cfg(target_arch = "arm")] + { + let gids = &[ + Gid::from_raw(3003), // AID_INET + Gid::from_raw(3004), // AID_NET_RAW + ]; + nix::unistd::setgroups(gids).expect("setgroups failed"); } // discard argv[0] let _ = args.next(); // This call will only return if there is an error - let error = Command::new("/bin/bash") - .args(args) - .uid(0) - .gid(0) - .exec(); + let error = Command::new("/bin/bash").args(args).uid(0).gid(0).exec(); eprintln!("Error running command: {error}"); std::process::exit(1); } diff --git a/telcom-parser/src/lib.rs b/telcom-parser/src/lib.rs index 17ec14d..8fc1f27 100644 --- a/telcom-parser/src/lib.rs +++ b/telcom-parser/src/lib.rs @@ -10,9 +10,9 @@ pub enum ParsingError { } pub fn decode(data: &[u8]) -> Result - where T: UperCodec +where + T: UperCodec, { let mut asn_data = PerCodecData::from_slice_uper(data); - T::uper_decode(&mut asn_data) - .map_err(ParsingError::UperDecodeError) + T::uper_decode(&mut asn_data).map_err(ParsingError::UperDecodeError) } diff --git a/telcom-parser/src/lte_rrc.rs b/telcom-parser/src/lte_rrc.rs index d433907..c09c25f 100644 --- a/telcom-parser/src/lte_rrc.rs +++ b/telcom-parser/src/lte_rrc.rs @@ -1,4 +1,4 @@ -/* +/* This file was autogenerated using hampi (https://github.com/ystero-dev/hampi), do not modify! This place is not a place of honor... diff --git a/telcom-parser/tests/lte_rrc_test.rs b/telcom-parser/tests/lte_rrc_test.rs index d19a682..c475eb5 100644 --- a/telcom-parser/tests/lte_rrc_test.rs +++ b/telcom-parser/tests/lte_rrc_test.rs @@ -1,10 +1,10 @@ -use telcom_parser::lte_rrc::BCCH_DL_SCH_Message; use asn1_codecs::{uper::UperCodec, PerCodecData}; +use telcom_parser::lte_rrc::BCCH_DL_SCH_Message; fn hex_to_bin(hex: &str) -> Vec { (0..hex.len()) .step_by(2) - .map(|i| u8::from_str_radix(&hex[i..i+2], 16).unwrap()) + .map(|i| u8::from_str_radix(&hex[i..i + 2], 16).unwrap()) .collect() }