unified rayhunter-daemon binary for all devices

Replace per-device features with config "display" field with the value
set at install time.
This commit is contained in:
oopsbagel
2025-07-17 11:04:50 -07:00
committed by Will Greenberg
parent 5b59efa4c8
commit 22d927aa25
13 changed files with 85 additions and 118 deletions

View File

@@ -10,10 +10,7 @@ on:
env:
CARGO_TERM_COLOR: always
FILE_ROOTSHELL: ../../rootshell/rootshell
FILE_RAYHUNTER_DAEMON_ORBIC: ../../rayhunter-daemon-orbic/rayhunter-daemon
FILE_RAYHUNTER_DAEMON_TPLINK: ../../rayhunter-daemon-tplink/rayhunter-daemon
FILE_RAYHUNTER_DAEMON_TMOBILE: ../../rayhunter-daemon-tmobile/rayhunter-daemon
FILE_RAYHUNTER_DAEMON_WINGTECH: ../../rayhunter-daemon-wingtech/rayhunter-daemon
FILE_RAYHUNTER_DAEMON: ../../rayhunter-daemon/rayhunter-daemon
RUSTFLAGS: "-Dwarnings"
jobs:
@@ -102,13 +99,6 @@ jobs:
check_and_test:
needs: files_changed
if: needs.files_changed.outputs.code_changed != '0'
strategy:
matrix:
device:
- name: orbic
- name: tplink
- name: wingtech
- name: tmobile
runs-on: ubuntu-latest
permissions:
contents: read
@@ -123,13 +113,13 @@ jobs:
npm install
npm run build
popd
NO_FIRMWARE_BIN=true cargo check --verbose --no-default-features --features=${{ matrix.device.name }}
NO_FIRMWARE_BIN=true cargo check --verbose
- name: Run tests
run: |
NO_FIRMWARE_BIN=true cargo test --verbose --no-default-features --features=${{ matrix.device.name }}
NO_FIRMWARE_BIN=true cargo test --verbose
- name: Run clippy
run: |
NO_FIRMWARE_BIN=true cargo clippy --verbose --no-default-features --features=${{ matrix.device.name }}
NO_FIRMWARE_BIN=true cargo clippy --verbose
test_web_frontend:
needs: files_changed
@@ -241,13 +231,6 @@ jobs:
permissions:
contents: read
packages: write
strategy:
matrix:
device:
- name: orbic
- name: tplink
- name: wingtech
- name: tmobile
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
@@ -269,10 +252,10 @@ jobs:
# what the feature selection in rayhunter-daemon is.
#
# https://github.com/rust-lang/cargo/issues/4463
cargo build -p rayhunter-daemon --bin rayhunter-daemon --target armv7-unknown-linux-musleabihf --profile=firmware --no-default-features --features ${{ matrix.device.name }}
cargo build -p rayhunter-daemon --bin rayhunter-daemon --target armv7-unknown-linux-musleabihf --profile=firmware
- uses: actions/upload-artifact@v4
with:
name: rayhunter-daemon-${{ matrix.device.name }}
name: rayhunter-daemon
path: target/armv7-unknown-linux-musleabihf/firmware/rayhunter-daemon
if-no-files-found: error

View File

@@ -3,15 +3,6 @@ name = "rayhunter-daemon"
version = "0.4.0"
edition = "2024"
[features]
# These feature flags are mutually exclusive, and exactly one must be enabled.
orbic = ["rayhunter/orbic"]
tmobile = ["rayhunter/tmobile"]
tplink = ["rayhunter/tplink"]
wingtech = ["rayhunter/wingtech"]
default = ["orbic"]
[dependencies]
rayhunter = { path = "../lib" }
toml = "0.8.8"

View File

@@ -1,3 +1,4 @@
use log::warn;
use serde::{Deserialize, Serialize};
use rayhunter::analysis::analyzer::AnalyzerConfig;
@@ -10,18 +11,29 @@ pub struct Config {
pub qmdl_store_path: String,
pub port: u16,
pub debug_mode: bool,
pub display: Display,
pub ui_level: u8,
pub colorblind_mode: bool,
pub key_input_mode: u8,
pub analyzers: AnalyzerConfig,
}
#[derive(PartialEq, Debug, Clone, Deserialize, Serialize)]
#[serde(rename_all = "lowercase")]
pub enum Display {
Orbic,
Tplink,
Tmobile,
Wingtech,
}
impl Default for Config {
fn default() -> Self {
Config {
qmdl_store_path: "/data/rayhunter/qmdl".to_string(),
port: 8080,
debug_mode: false,
display: Display::Orbic,
ui_level: 1,
colorblind_mode: false,
key_input_mode: 0,
@@ -37,6 +49,7 @@ where
if let Ok(config_file) = tokio::fs::read_to_string(&path).await {
Ok(toml::from_str(&config_file).map_err(RayhunterError::ConfigFileParsingError)?)
} else {
warn!("unable to read config file, using default config");
Ok(Config::default())
}
}

View File

@@ -1,30 +1,11 @@
#[cfg(any(feature = "orbic", feature = "tplink", feature = "wingtech"))]
mod generic_framebuffer;
#[cfg(feature = "tmobile")]
mod tmobile;
#[cfg(feature = "tmobile")]
pub use tmobile::update_ui;
#[cfg(feature = "tplink")]
mod tplink;
#[cfg(feature = "tplink")]
mod tplink_framebuffer;
#[cfg(feature = "tplink")]
mod tplink_onebit;
#[cfg(feature = "tplink")]
pub use tplink::update_ui;
#[cfg(feature = "orbic")]
mod orbic;
#[cfg(feature = "orbic")]
pub use orbic::update_ui;
#[cfg(feature = "wingtech")]
mod wingtech;
#[cfg(feature = "wingtech")]
pub use wingtech::update_ui;
pub mod orbic;
pub mod tmobile;
pub mod tplink;
pub mod tplink_framebuffer;
pub mod tplink_onebit;
pub mod wingtech;
#[derive(Clone, Copy, PartialEq)]
pub enum DisplayState {

View File

@@ -13,7 +13,7 @@ use std::net::SocketAddr;
use std::sync::Arc;
use std::sync::atomic::{AtomicBool, Ordering};
use crate::config::{parse_args, parse_config};
use crate::config::{Display, parse_args, parse_config};
use crate::diag::run_diag_read_thread;
use crate::error::RayhunterError;
use crate::pcap::get_pcap;
@@ -215,7 +215,7 @@ async fn run_with_config(
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()
let mut dev = DiagDevice::new(config.display == Display::Tplink)
.await
.map_err(RayhunterError::DiagInitError)?;
dev.config_logs()
@@ -233,7 +233,16 @@ async fn run_with_config(
config.analyzers.clone(),
);
info!("Starting UI");
display::update_ui(&task_tracker, &config, ui_shutdown_rx, ui_update_rx);
let display = &config.display;
info!("Display type {display:?}");
let update_ui = match display {
Display::Orbic => display::orbic::update_ui,
Display::Tplink => display::tplink::update_ui,
Display::Tmobile => display::tmobile::update_ui,
Display::Wingtech => display::wingtech::update_ui,
};
update_ui(&task_tracker, &config, ui_shutdown_rx, ui_update_rx);
info!("Starting Key Input service");
let (key_input_shutdown_tx, key_input_shutdown_rx) = oneshot::channel();

View File

@@ -3,6 +3,8 @@ qmdl_store_path = "/data/rayhunter/qmdl"
port = 8080
debug_mode = false
colorblind_mode = false
# Display module, this will be overwritten by the installer. Defaults to "orbic".
#display = "orbic"
# UI Levels:
#
# Orbic and TP-Link with color display:

View File

@@ -9,26 +9,7 @@ fn main() {
"/../target/armv7-unknown-linux-musleabihf/firmware/"
));
set_binary_var(include_dir, "FILE_ROOTSHELL", "rootshell");
set_binary_var(
include_dir,
"FILE_RAYHUNTER_DAEMON_ORBIC",
"rayhunter-daemon",
);
set_binary_var(
include_dir,
"FILE_RAYHUNTER_DAEMON_TMOBILE",
"rayhunter-daemon",
);
set_binary_var(
include_dir,
"FILE_RAYHUNTER_DAEMON_TPLINK",
"rayhunter-daemon",
);
set_binary_var(
include_dir,
"FILE_RAYHUNTER_DAEMON_WINGTECH",
"rayhunter-daemon",
);
set_binary_var(include_dir, "FILE_RAYHUNTER_DAEMON", "rayhunter-daemon");
}
fn set_binary_var(include_dir: &Path, var: &str, file: &str) {

View File

@@ -91,7 +91,7 @@ async fn setup_rootshell(adb_device: &mut ADBUSBDevice) -> Result<()> {
}
async fn setup_rayhunter(mut adb_device: ADBUSBDevice) -> Result<ADBUSBDevice> {
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON_ORBIC"));
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON"));
adb_at_syscmd(&mut adb_device, "mkdir -p /data/rayhunter").await?;
install_file(
@@ -103,7 +103,9 @@ async fn setup_rayhunter(mut adb_device: ADBUSBDevice) -> Result<ADBUSBDevice> {
install_file(
&mut adb_device,
"/data/rayhunter/config.toml",
CONFIG_TOML.as_bytes(),
CONFIG_TOML
.replace("#display = \"orbic\"", "display = \"orbic\"")
.as_bytes(),
)
.await?;
install_file(
@@ -194,11 +196,11 @@ async fn install_file_impl(
.stat(dest)
.context("Failed to stat transfered file")?;
if file_info.file_size == 0 {
bail!("File transfer unseccessful\nFile is empty");
bail!("File transfer unsuccessful\nFile is empty");
}
let ouput = adb_command(adb_device, &["sha256sum", dest])?;
if !ouput.contains(&file_hash) {
bail!("File transfer unseccessful\nBad hash expected {file_hash} got {ouput}");
let output = adb_command(adb_device, &["sha256sum", dest])?;
if !output.contains(&file_hash) {
bail!("File transfer unsuccessful\nBad hash expected {file_hash} got {output}");
}
Ok(())
}

View File

@@ -41,11 +41,13 @@ async fn run_install(admin_ip: String, admin_password: String) -> Result<()> {
telnet_send_file(
addr,
"/data/rayhunter/config.toml",
crate::CONFIG_TOML.as_bytes(),
crate::CONFIG_TOML
.replace("#display = \"orbic\"", "display = \"tmobile\"")
.as_bytes(),
)
.await?;
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON_TMOBILE"));
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON"));
telnet_send_file(
addr,
"/data/rayhunter/rayhunter-daemon",

View File

@@ -154,11 +154,13 @@ async fn tplink_run_install(
telnet_send_file(
addr,
&format!("{sdcard_path}/config.toml"),
crate::CONFIG_TOML.as_bytes(),
crate::CONFIG_TOML
.replace("#display = \"orbic\"", "display = \"tplink\"")
.as_bytes(),
)
.await?;
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON_TPLINK"));
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON"));
telnet_send_file(
addr,

View File

@@ -101,11 +101,13 @@ async fn wingtech_run_install(admin_ip: String, admin_password: String) -> Resul
telnet_send_file(
addr,
"/data/rayhunter/config.toml",
crate::CONFIG_TOML.as_bytes(),
crate::CONFIG_TOML
.replace("#display = \"orbic\"", "display = \"wingtech\"")
.as_bytes(),
)
.await?;
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON_WINGTECH"));
let rayhunter_daemon_bin = include_bytes!(env!("FILE_RAYHUNTER_DAEMON"));
telnet_send_file(
addr,
"/data/rayhunter/rayhunter-daemon",

View File

@@ -9,13 +9,6 @@ description = "Realtime cellular data decoding and analysis for IMSI catcher det
name = "rayhunter"
path = "src/lib.rs"
[features]
default = []
orbic = []
tmobile = []
tplink = []
wingtech = []
[dependencies]
bytes = "1.5.0"
chrono = { version = "0.4.31", features = ["serde"] }

View File

@@ -86,11 +86,11 @@ pub struct DiagDevice {
}
impl DiagDevice {
pub async fn new() -> DiagResult<Self> {
Self::new_with_retries(Duration::from_secs(30)).await
pub async fn new(tplink: bool) -> DiagResult<Self> {
Self::new_with_retries(Duration::from_secs(30), tplink).await
}
pub async fn new_with_retries(max_duration: Duration) -> DiagResult<Self> {
pub async fn new_with_retries(max_duration: Duration, tplink: bool) -> DiagResult<Self> {
// For some reason the diag device needs a very long time to become available again with in
// the same process, on TP-Link M7350 v3. While process restart would reset it faster.
@@ -101,7 +101,7 @@ impl DiagDevice {
let mut num_retries = 0;
loop {
match Self::try_new().await {
match Self::try_new(tplink).await {
Ok(device) => {
info!("Diag device initialization succeeded after {num_retries} retries");
return Ok(device);
@@ -125,7 +125,7 @@ impl DiagDevice {
}
}
async fn try_new() -> DiagResult<Self> {
async fn try_new(tplink: bool) -> DiagResult<Self> {
let diag_file = File::options()
.read(true)
.write(true)
@@ -134,7 +134,7 @@ impl DiagDevice {
.map_err(DiagDeviceError::OpenDiagDeviceError)?;
let fd = diag_file.as_raw_fd();
enable_frame_readwrite(fd, MEMORY_DEVICE_MODE)?;
enable_frame_readwrite(fd, MEMORY_DEVICE_MODE, tplink)?;
let use_mdm = determine_use_mdm(fd)?;
Ok(DiagDevice {
@@ -300,24 +300,30 @@ struct DiagLoggingModeParam {
}
// Triggers the diag device's debug logging mode
fn enable_frame_readwrite(fd: i32, mode: u32) -> DiagResult<()> {
fn enable_frame_readwrite(fd: i32, mode: u32, tplink: bool) -> DiagResult<()> {
unsafe {
if libc::ioctl(fd, DIAG_IOCTL_SWITCH_LOGGING, mode, 0, 0, 0) < 0 {
let try_params: &[DiagLoggingModeParam] = &[
// tplink M7350 HW revision 3-8 need this mode
#[cfg(feature = "tplink")]
DiagLoggingModeParam {
req_mode: mode,
peripheral_mask: 0,
mode_param: 1,
},
// tplink M7350 HW revision v9 requires the same parameters as orbic
DiagLoggingModeParam {
let try_params: &[DiagLoggingModeParam] = match tplink {
true => &[
// tplink M7350 HW revision 3-8 need this mode
DiagLoggingModeParam {
req_mode: mode,
peripheral_mask: 0,
mode_param: 1,
},
// tplink M7350 HW revision v9 requires the same parameters as orbic
DiagLoggingModeParam {
req_mode: mode,
peripheral_mask: u32::MAX,
mode_param: 0,
},
],
false => &[DiagLoggingModeParam {
req_mode: mode,
peripheral_mask: u32::MAX,
mode_param: 0,
},
];
}],
};
let mut ret = 0;