Merge remote-tracking branch 'origin/main' into build-features

This commit is contained in:
Markus Unterwaditzer
2025-04-08 21:15:57 +02:00
17 changed files with 197 additions and 86 deletions

62
.github/ISSUE_TEMPLATE/bug.yaml vendored Normal file
View File

@@ -0,0 +1,62 @@
name: Bug Report
description: File a bug report.
title: "[Bug]: "
type: Bug
body:
- type: markdown
attributes:
value: |
Thanks for taking the time to fill out this bug report!
- type: input
attributes:
label: Rayhunter Version
description: |
Which version did you install?
placeholder: v0.2.6
- type: input
attributes:
label: Capture Date
description: |
YYYY-MM-DD
placeholder: 2025-05-01
validations:
required: true
- type: input
attributes:
label: Capture Location
description: |
(If comfortable disclosing) What region or country were you in?
placeholder: Washington State
validations:
required: false
- type: input
attributes:
label: Device and Model
description: |
Device you installed Rayhunter on to.
placeholder: Orbic RC400L
validations:
required: true
- type: textarea
id: what-happened
attributes:
label: What happened?
description: |
What steps did you take to get to your issue?
placeholder: Tell us what you see!
validations:
required: true
- type: textarea
id: expected
attributes:
label: Expected behavior
description: Rayhunter's behavior differed from what I expected because.
placeholder: "What was expected?"
validations:
required: true
- type: textarea
id: logs
attributes:
label: Relevant log output
description: Rayhunter data captures (QMDL and PCAP logs) or error codes
render: shell

8
.github/ISSUE_TEMPLATE/config.yaml vendored Normal file
View File

@@ -0,0 +1,8 @@
blank_issues_enabled: false
contact_links:
- name: Rayhunter Mattermost
url: https://opensource.eff.org/signup_user_complete/?id=6iqur37ucfrctfswrs14iscobw&md=link&sbr=su
about: If you're having trouble using Rayhunter and aren't sure you've found a bug or request for a new feature, please first try asking for help here. There is a much larger community there of people familiar with the project who will be able to more quickly answer your questions.
- name: Rayhunter Security Policy
url: https://github.com/EFForg/rayhunter/security/advisories/new
about: Please report security vulnerabilities here.

27
.github/ISSUE_TEMPLATE/feature.yaml vendored Normal file
View File

@@ -0,0 +1,27 @@
name: Feature Request
description: Suggest a new feature or improvement to Rayhunter
title: "[Feature Request]: "
labels: ["enhancement"]
body:
- type: textarea
id: problem
attributes:
label: What problem does this feature solve or what does it enhance?
description: Explain what this feature addresses, ors the benefit it provides.
placeholder: For example, "Currently, users have to manually do X, which is time-consuming."
validations:
required: true
- type: textarea
id: solution
attributes:
label: Proposed Solution
description: Describe the solution you'd like to see implemented.
placeholder: For example, "Implement a new button that automatically does X."
validations:
required: true
- type: textarea
id: alternatives
attributes:
label: Alternatives Considered
description: Have you considered any alternative solutions?
placeholder: For example, "We considered Y, but Z is a better approach because..."

6
.github/pull_request_template.md vendored Normal file
View File

@@ -0,0 +1,6 @@
## Pull Request Checklist
- [ ] The Rayhunter team has recently expressed interest in reviewing a PR for this. If not, this PR may be closed due our limited resources and need to prioritize how we spend them.
- [ ] Added or updated any documentation as needed to support the changes in this PR.
- [ ] Code has been linted and run through `cargo fmt`
- [ ] If any new functionality has been added, unit tests were also added

View File

@@ -16,6 +16,8 @@ jobs:
platform:
- name: ubuntu-24
os: ubuntu-latest
- name: ubuntu-24-aarch64
os: ubuntu-24.04-arm
- name: macos-arm
os: macos-latest
- name: macos-intel

4
Cargo.lock generated
View File

@@ -1645,9 +1645,9 @@ dependencies = [
[[package]]
name = "tokio"
version = "1.44.1"
version = "1.44.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "f382da615b842244d4b8738c82ed1275e6c5dd90c459a30941cd07080b06c91a"
checksum = "e6b88822cbe49de4185e3a4cbf8321dd487cf5fe0c5c65695fef6346371e9c48"
dependencies = [
"backtrace",
"bytes",

View File

@@ -22,7 +22,7 @@ path = "src/check.rs"
rayhunter = { path = "../lib" }
toml = "0.8.8"
serde = { version = "1.0.193", features = ["derive"] }
tokio = { version = "1.35.1", features = ["full"] }
tokio = { version = "1.44.2", features = ["full"] }
axum = "0.7.3"
futures-core = "0.3.30"
thiserror = "1.0.52"

View File

@@ -184,7 +184,6 @@ async fn main() -> Result<(), RayhunterError> {
debug_mode: config.debug_mode,
analysis_status_lock,
analysis_sender: analysis_tx,
colorblind_mode: config.colorblind_mode,
});
run_server(&task_tracker, &config, state, server_shutdown_rx).await;

View File

@@ -130,11 +130,7 @@ pub async fn start_recording(State(state): State<Arc<ServerState>>) -> Result<(S
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 = if state.colorblind_mode {
display::DisplayState::RecordingCBM
} else {
display::DisplayState::Recording
};
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)))?;

View File

@@ -1,15 +1,15 @@
use image::{codecs::gif::GifDecoder, imageops::FilterType, AnimationDecoder, DynamicImage};
use std::time::Duration;
use std::io::Cursor;
use std::time::Duration;
use crate::config;
use crate::display::DisplayState;
use log::{error, info};
use tokio::sync::oneshot;
use tokio::sync::mpsc::Receiver;
use tokio_util::task::TaskTracker;
use tokio::sync::oneshot;
use tokio::sync::oneshot::error::TryRecvError;
use tokio_util::task::TaskTracker;
use std::thread::sleep;
@@ -21,17 +21,16 @@ pub struct Dimensions {
pub width: u32,
}
#[allow(dead_code)]
#[derive(Copy, Clone)]
pub enum Color {
Red ,
Green ,
Blue ,
White ,
Black ,
Cyan ,
Yellow ,
Red,
Green,
Blue,
White,
Black,
Cyan,
Yellow,
Pink,
}
@@ -50,12 +49,17 @@ impl Color {
}
}
impl From<DisplayState> for Color{
fn from(state: DisplayState) -> Self {
impl Color {
fn from_state(state: DisplayState, colorblind_mode: bool) -> Self {
match state {
DisplayState::Paused => Color::White,
DisplayState::Recording => Color::Green,
DisplayState::RecordingCBM => Color::Blue,
DisplayState::Recording => {
if colorblind_mode {
Color::Blue
} else {
Color::Green
}
}
DisplayState::WarningDetected => Color::Red,
}
}
@@ -74,9 +78,8 @@ pub trait GenericFramebuffer: Send + 'static {
let mut width = img.width();
let mut height = img.height();
let resized_img: DynamicImage;
if height > dimensions.height ||
width > dimensions.width {
resized_img = img.resize( dimensions.width, dimensions.height, FilterType::CatmullRom);
if height > dimensions.height || width > dimensions.width {
resized_img = img.resize(dimensions.width, dimensions.height, FilterType::CatmullRom);
width = dimensions.width.min(resized_img.width());
height = dimensions.height.min(resized_img.height());
} else {
@@ -91,9 +94,7 @@ pub trait GenericFramebuffer: Send + 'static {
}
}
self.write_buffer(
&buf,
);
self.write_buffer(&buf);
}
fn draw_gif(&mut self, img_buffer: &[u8]) {
@@ -131,64 +132,69 @@ pub fn update_ui(
config: &config::Config,
mut fb: impl GenericFramebuffer,
mut ui_shutdown_rx: oneshot::Receiver<()>,
mut ui_update_rx: Receiver<DisplayState>
mut ui_update_rx: Receiver<DisplayState>,
) {
static IMAGE_DIR: Dir<'_> = include_dir!("$CARGO_MANIFEST_DIR/static/images/");
let mut display_color: Color;
let display_level = config.ui_level;
if display_level == 0 {
info!("Invisible mode, not spawning UI.");
}
if config.colorblind_mode {
display_color = Color::Blue;
} else {
display_color = Color::Green;
}
let colorblind_mode = config.colorblind_mode;
let mut display_color = Color::from_state(DisplayState::Recording, colorblind_mode);
task_tracker.spawn_blocking(move || {
// this feels wrong, is there a more rusty way to do this?
let mut img: Option<&[u8]> = None;
if display_level == 2 {
img = Some(IMAGE_DIR.get_file("orca.gif").expect("failed to read orca.gif").contents());
img = Some(
IMAGE_DIR
.get_file("orca.gif")
.expect("failed to read orca.gif")
.contents(),
);
} else if display_level == 3 {
img = Some(IMAGE_DIR.get_file("eff.png").expect("failed to read eff.png").contents());
img = Some(
IMAGE_DIR
.get_file("eff.png")
.expect("failed to read eff.png")
.contents(),
);
}
loop {
match ui_shutdown_rx.try_recv() {
Ok(_) => {
info!("received UI shutdown");
break;
},
Err(TryRecvError::Empty) => {},
Err(e) => panic!("error receiving shutdown message: {e}")
}
Err(TryRecvError::Empty) => {}
Err(e) => panic!("error receiving shutdown message: {e}"),
}
match ui_update_rx.try_recv() {
Ok(state) => {
display_color = state.into();
},
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {},
Err(e) => error!("error receiving framebuffer update message: {e}")
Ok(state) => {
display_color = Color::from_state(state, colorblind_mode);
}
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {}
Err(e) => error!("error receiving framebuffer update message: {e}"),
}
match display_level {
match display_level {
2 => {
fb.draw_gif(img.unwrap());
},
3 => {
fb.draw_img(img.unwrap())
},
}
3 => fb.draw_img(img.unwrap()),
128 => {
fb.draw_line(Color::Cyan, 128);
fb.draw_line(Color::Pink, 102);
fb.draw_line(Color::White, 76);
fb.draw_line(Color::Pink, 50);
fb.draw_line(Color::Cyan, 25);
},
_ => { // this branch id for ui_level 1, which is also the default if an
// unknown value is used
}
_ => {
// this branch id for ui_level 1, which is also the default if an
// unknown value is used
fb.draw_line(display_color, 2);
},
}
};
sleep(Duration::from_millis(1000));
}

View File

@@ -19,7 +19,6 @@ pub enum DisplayState {
Recording,
Paused,
WarningDetected,
RecordingCBM,
}
#[cfg(all(feature = "orbic", feature = "tplink"))]

View File

@@ -160,7 +160,6 @@ pub fn update_ui(
match ui_update_rx.try_recv() {
Ok(DisplayState::Paused) => pixels = paused(),
Ok(DisplayState::Recording) => pixels = smiling(),
Ok(DisplayState::RecordingCBM) => pixels = smiling(),
Ok(DisplayState::WarningDetected) => pixels = frowning(),
Err(tokio::sync::mpsc::error::TryRecvError::Empty) => {},
Err(e) => {

View File

@@ -1,5 +1,5 @@
use axum::body::Body;
use axum::http::header::{CONTENT_TYPE, self};
use axum::http::header::{self, CONTENT_LENGTH, CONTENT_TYPE};
use axum::extract::State;
use axum::http::{StatusCode, HeaderValue};
use axum::response::{Response, IntoResponse};
@@ -23,7 +23,6 @@ pub struct ServerState {
pub analysis_status_lock: Arc<RwLock<AnalysisStatus>>,
pub analysis_sender: Sender<AnalysisCtrlMessage>,
pub debug_mode: bool,
pub colorblind_mode: bool,
}
pub async fn get_qmdl(State(state): State<Arc<ServerState>>, Path(qmdl_name): Path<String>) -> Result<Response, (StatusCode, String)> {
@@ -36,7 +35,10 @@ pub async fn get_qmdl(State(state): State<Arc<ServerState>>, Path(qmdl_name): Pa
let limited_qmdl_file = qmdl_file.take(entry.qmdl_size_bytes as u64);
let qmdl_stream = ReaderStream::new(limited_qmdl_file);
let headers = [(CONTENT_TYPE, "application/octet-stream")];
let headers = [
(CONTENT_TYPE, "application/octet-stream"),
(CONTENT_LENGTH, &entry.qmdl_size_bytes.to_string()),
];
let body = Body::from_stream(qmdl_stream);
Ok((headers, body).into_response())
}

9
dist/install.sh vendored
View File

@@ -100,7 +100,11 @@ test_rayhunter() {
##### Main #####
##### ##### #####
if [[ `uname -s` == "Linux" ]]; then
export SERIAL_PATH="./serial-ubuntu-24/serial"
if [[ `uname -m` == "arm64" ]]; then
export SERIAL_PATH="./serial-ubuntu-24-aarch64/serial"
elif [[ `uname -m` == "x86_64" ]]; then
export SERIAL_PATH="./serial-ubuntu-24/serial"
fi
export PLATFORM_TOOLS="platform-tools-latest-linux.zip"
elif [[ `uname -s` == "Darwin" ]]; then
if [[ `uname -m` == "arm64" ]]; then
@@ -109,7 +113,8 @@ elif [[ `uname -s` == "Darwin" ]]; then
export SERIAL_PATH="./serial-macos-intel/serial"
fi
export PLATFORM_TOOLS="platform-tools-latest-darwin.zip"
xattr -d com.apple.quarantine "$SERIAL_PATH"
# if we've already deleted this attribute, xattr errors out
xattr -d com.apple.quarantine "$SERIAL_PATH" || echo
else
echo "This script only supports Linux or macOS"
exit 1

View File

@@ -26,7 +26,7 @@ nix = { version = "0.29.0", features = ["feature"] }
pcap-file-tokio = "0.1.0"
thiserror = "1.0.50"
telcom-parser = { path = "../telcom-parser" }
tokio = { version = "1.35.1", features = ["full"] }
tokio = { version = "1.44.2", features = ["full"] }
futures-core = "0.3.30"
futures = "0.3.30"
serde = { version = "1.0.197", features = ["derive"] }

View File

@@ -10,27 +10,27 @@ use std::env;
use nix::unistd::Gid;
fn main() {
let mut args = env::args();
let mut args = env::args();
// 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");
}
// 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");
}
// 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();
eprintln!("Error running command: {error}");
std::process::exit(1);
// 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();
eprintln!("Error running command: {error}");
std::process::exit(1);
}

View File

@@ -8,4 +8,4 @@ edition = "2021"
[dependencies]
anyhow = "1.0.97"
nusb = "0.1.13"
tokio = { version = "1.44.1", features = ["macros", "rt", "time"] }
tokio = { version = "1.44.2", features = ["macros", "rt", "time"] }