mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-04-29 17:09:59 -07:00
Merge branch 'main' into notifications
This commit is contained in:
@@ -2,7 +2,7 @@
|
||||
name = "rayhunter-daemon"
|
||||
version = "0.5.1"
|
||||
edition = "2024"
|
||||
rust-version = "1.88"
|
||||
rust-version = "1.88.0"
|
||||
|
||||
[dependencies]
|
||||
rayhunter = { path = "../lib" }
|
||||
|
||||
@@ -96,16 +96,15 @@ impl DiagTask {
|
||||
/// Stop recording
|
||||
async fn stop(&mut self, qmdl_store: &mut RecordingStore) {
|
||||
self.stop_current_recording().await;
|
||||
if let Some((_, entry)) = qmdl_store.get_current_entry() {
|
||||
if let Err(e) = self
|
||||
if let Some((_, entry)) = qmdl_store.get_current_entry()
|
||||
&& let Err(e) = self
|
||||
.analysis_sender
|
||||
.send(AnalysisCtrlMessage::RecordingFinished(
|
||||
entry.name.to_string(),
|
||||
))
|
||||
.await
|
||||
{
|
||||
warn!("couldn't send analysis message: {e}");
|
||||
}
|
||||
{
|
||||
warn!("couldn't send analysis message: {e}");
|
||||
}
|
||||
if let Err(e) = qmdl_store.close_current_entry().await {
|
||||
error!("couldn't close current entry: {e}");
|
||||
|
||||
@@ -145,10 +145,10 @@ pub fn update_ui(
|
||||
|
||||
// we write the status every second because it may have been overwritten through menu
|
||||
// navigation.
|
||||
if display_level != 0 {
|
||||
if let Err(e) = tokio::fs::write(OLED_PATH, pixels).await {
|
||||
error!("failed to write to display: {e}");
|
||||
}
|
||||
if display_level != 0
|
||||
&& let Err(e) = tokio::fs::write(OLED_PATH, pixels).await
|
||||
{
|
||||
error!("failed to write to display: {e}");
|
||||
}
|
||||
|
||||
tokio::time::sleep(Duration::from_millis(1000)).await;
|
||||
|
||||
@@ -61,11 +61,11 @@ pub fn run_key_input_thread(
|
||||
|
||||
// On orbic it was observed that pressing the power button can trigger many successive
|
||||
// events. Drop events that are too close together.
|
||||
if let Some(last_time) = last_event_time {
|
||||
if now.duration_since(last_time) < Duration::from_millis(50) {
|
||||
last_event_time = Some(now);
|
||||
continue;
|
||||
}
|
||||
if let Some(last_time) = last_event_time
|
||||
&& now.duration_since(last_time) < Duration::from_millis(50)
|
||||
{
|
||||
last_event_time = Some(now);
|
||||
continue;
|
||||
}
|
||||
last_event_time = Some(now);
|
||||
|
||||
|
||||
@@ -174,10 +174,9 @@ pub async fn test_rayhunter(adb_device: &mut ADBUSBDevice) -> Result<()> {
|
||||
if let Ok(output) = adb_command(
|
||||
adb_device,
|
||||
&["wget", "-O", "-", "http://localhost:8080/index.html"],
|
||||
) {
|
||||
if output.contains("html") {
|
||||
return Ok(());
|
||||
}
|
||||
) && output.contains("html")
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
failures += 1;
|
||||
sleep(Duration::from_secs(3)).await;
|
||||
@@ -297,14 +296,12 @@ async fn adb_echo_test(mut adb_device: ADBUSBDevice) -> Result<ADBUSBDevice> {
|
||||
Ok::<(ADBUSBDevice, Vec<u8>), RustADBError>((adb_device, buf))
|
||||
});
|
||||
sleep(Duration::from_secs(1)).await;
|
||||
if thread.is_finished() {
|
||||
if let Ok(Ok((dev, buf))) = thread.join() {
|
||||
if let Ok(s) = std::str::from_utf8(&buf) {
|
||||
if s.contains(test_echo) {
|
||||
return Ok(dev);
|
||||
}
|
||||
}
|
||||
}
|
||||
if thread.is_finished()
|
||||
&& let Ok(Ok((dev, buf))) = thread.join()
|
||||
&& let Ok(s) = std::str::from_utf8(&buf)
|
||||
&& s.contains(test_echo)
|
||||
{
|
||||
return Ok(dev);
|
||||
}
|
||||
// I'd like to kill the background thread here if that was possible.
|
||||
bail!("Could not communicate with the Orbic. Try disconnecting and reconnecting.");
|
||||
@@ -317,10 +314,11 @@ async fn wait_for_usb_device(vendor_id: u16, product_id: u16) -> Result<()> {
|
||||
loop {
|
||||
let mut watcher = nusb::watch_devices()?;
|
||||
while let Some(event) = watcher.next().await {
|
||||
if let HotplugEvent::Connected(dev) = event {
|
||||
if dev.vendor_id() == vendor_id && dev.product_id() == product_id {
|
||||
return Ok(());
|
||||
}
|
||||
if let HotplugEvent::Connected(dev) = event
|
||||
&& dev.vendor_id() == vendor_id
|
||||
&& dev.product_id() == product_id
|
||||
{
|
||||
return Ok(());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -76,14 +76,14 @@ pub struct Event {
|
||||
/// many hours at a time with dozens of [Analyzers](Analyzer) working in parallel.
|
||||
pub trait Analyzer {
|
||||
/// Returns a user-friendly, concise name for your heuristic.
|
||||
fn get_name(&self) -> Cow<str>;
|
||||
fn get_name(&self) -> Cow<'_, str>;
|
||||
|
||||
/// Returns a user-friendly description of what your heuristic looks for,
|
||||
/// the types of [Events](Event) it may return, as well as possible false-positive
|
||||
/// conditions that may trigger an [Event]. If different [Events](Event) have
|
||||
/// different false-positive conditions, consider including them in its
|
||||
/// `message` field.
|
||||
fn get_description(&self) -> Cow<str>;
|
||||
fn get_description(&self) -> Cow<'_, str>;
|
||||
|
||||
/// Analyze a single [InformationElement], possibly returning an [Event] if your
|
||||
/// heuristic deems it relevant. Again, be mindful of any state your
|
||||
|
||||
@@ -2,7 +2,6 @@ use std::borrow::Cow;
|
||||
|
||||
use super::analyzer::{Analyzer, Event, EventType, Severity};
|
||||
use super::information_element::{InformationElement, LteInformationElement};
|
||||
use super::util::unpack;
|
||||
use telcom_parser::lte_rrc::{
|
||||
DL_DCCH_MessageType, DL_DCCH_MessageType_c1, RRCConnectionReleaseCriticalExtensions,
|
||||
RRCConnectionReleaseCriticalExtensions_c1, RedirectedCarrierInfo,
|
||||
@@ -14,11 +13,11 @@ pub struct ConnectionRedirect2GDowngradeAnalyzer {}
|
||||
|
||||
// TODO: keep track of SIB state to compare LTE reselection blocks w/ 2g/3g ones
|
||||
impl Analyzer for ConnectionRedirect2GDowngradeAnalyzer {
|
||||
fn get_name(&self) -> Cow<str> {
|
||||
fn get_name(&self) -> Cow<'_, str> {
|
||||
Cow::from("Connection Release/Redirected Carrier 2G Downgrade")
|
||||
}
|
||||
|
||||
fn get_description(&self) -> Cow<str> {
|
||||
fn get_description(&self) -> Cow<'_, str> {
|
||||
Cow::from("Tests if a cell releases our connection and redirects us to a 2G cell.")
|
||||
}
|
||||
|
||||
@@ -27,27 +26,28 @@ impl Analyzer for ConnectionRedirect2GDowngradeAnalyzer {
|
||||
}
|
||||
|
||||
fn analyze_information_element(&mut self, ie: &InformationElement) -> Option<Event> {
|
||||
unpack!(InformationElement::LTE(lte_ie) = ie);
|
||||
let message = match &**lte_ie {
|
||||
LteInformationElement::DlDcch(msg_cont) => &msg_cont.message,
|
||||
_ => return None,
|
||||
};
|
||||
unpack!(DL_DCCH_MessageType::C1(c1) = message);
|
||||
unpack!(DL_DCCH_MessageType_c1::RrcConnectionRelease(release) = c1);
|
||||
unpack!(RRCConnectionReleaseCriticalExtensions::C1(c1) = &release.critical_extensions);
|
||||
unpack!(RRCConnectionReleaseCriticalExtensions_c1::RrcConnectionRelease_r8(r8_ies) = c1);
|
||||
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,
|
||||
},
|
||||
message: "Detected 2G downgrade".to_owned(),
|
||||
}),
|
||||
_ => Some(Event {
|
||||
event_type: EventType::Informational,
|
||||
message: format!("RRCConnectionRelease CarrierInfo: {carrier_info:?}"),
|
||||
}),
|
||||
if let InformationElement::LTE(lte_ie) = ie
|
||||
&& let LteInformationElement::DlDcch(msg_cont) = &**lte_ie
|
||||
&& let DL_DCCH_MessageType::C1(c1) = &msg_cont.message
|
||||
&& let DL_DCCH_MessageType_c1::RrcConnectionRelease(release) = c1
|
||||
&& let RRCConnectionReleaseCriticalExtensions::C1(c1) = &release.critical_extensions
|
||||
&& let RRCConnectionReleaseCriticalExtensions_c1::RrcConnectionRelease_r8(r8_ies) = c1
|
||||
&& let 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,
|
||||
},
|
||||
message: "Detected 2G downgrade".to_owned(),
|
||||
}),
|
||||
_ => Some(Event {
|
||||
event_type: EventType::Informational,
|
||||
message: format!("RRCConnectionRelease CarrierInfo: {carrier_info:?}"),
|
||||
}),
|
||||
}
|
||||
} else {
|
||||
None
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -98,11 +98,11 @@ impl ImsiRequestedAnalyzer {
|
||||
}
|
||||
|
||||
impl Analyzer for ImsiRequestedAnalyzer {
|
||||
fn get_name(&self) -> Cow<str> {
|
||||
fn get_name(&self) -> Cow<'_, str> {
|
||||
Cow::from("Identity (IMSI or IMEI) requested in suspicious manner")
|
||||
}
|
||||
|
||||
fn get_description(&self) -> Cow<str> {
|
||||
fn get_description(&self) -> Cow<'_, str> {
|
||||
Cow::from(
|
||||
"Tests whether the ME sends an Identity Request NAS message without either an associated attach request or auth accept message",
|
||||
)
|
||||
|
||||
@@ -2,8 +2,6 @@ use std::borrow::Cow;
|
||||
|
||||
use telcom_parser::lte_rrc::{BCCH_DL_SCH_MessageType, BCCH_DL_SCH_MessageType_c1};
|
||||
|
||||
use crate::analysis::util::unpack;
|
||||
|
||||
use super::analyzer::{Analyzer, Event, EventType, Severity};
|
||||
use super::information_element::{InformationElement, LteInformationElement};
|
||||
|
||||
@@ -24,11 +22,11 @@ impl IncompleteSibAnalyzer {
|
||||
}
|
||||
|
||||
impl Analyzer for IncompleteSibAnalyzer {
|
||||
fn get_name(&self) -> Cow<str> {
|
||||
fn get_name(&self) -> Cow<'_, str> {
|
||||
Cow::from("Incomplete SIB")
|
||||
}
|
||||
|
||||
fn get_description(&self) -> Cow<str> {
|
||||
fn get_description(&self) -> Cow<'_, str> {
|
||||
Cow::from("Tests whether a SIB1 message contains a full chain of followup sibs")
|
||||
}
|
||||
|
||||
@@ -39,12 +37,12 @@ impl Analyzer for IncompleteSibAnalyzer {
|
||||
fn analyze_information_element(&mut self, ie: &InformationElement) -> Option<Event> {
|
||||
self.packet_num += 1;
|
||||
|
||||
unpack!(InformationElement::LTE(lte_ie) = ie);
|
||||
unpack!(LteInformationElement::BcchDlSch(sch_msg) = &**lte_ie);
|
||||
unpack!(BCCH_DL_SCH_MessageType::C1(c1) = &sch_msg.message);
|
||||
unpack!(BCCH_DL_SCH_MessageType_c1::SystemInformationBlockType1(sib1) = c1);
|
||||
|
||||
if sib1.scheduling_info_list.0.len() < 2 {
|
||||
if let InformationElement::LTE(lte_ie) = ie
|
||||
&& let LteInformationElement::BcchDlSch(sch_msg) = &**lte_ie
|
||||
&& let BCCH_DL_SCH_MessageType::C1(c1) = &sch_msg.message
|
||||
&& let BCCH_DL_SCH_MessageType_c1::SystemInformationBlockType1(sib1) = c1
|
||||
&& sib1.scheduling_info_list.0.len() < 2
|
||||
{
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::Medium,
|
||||
|
||||
@@ -24,11 +24,11 @@ impl NasNullCipherAnalyzer {
|
||||
}
|
||||
|
||||
impl Analyzer for NasNullCipherAnalyzer {
|
||||
fn get_name(&self) -> Cow<str> {
|
||||
fn get_name(&self) -> Cow<'_, str> {
|
||||
Cow::from("NAS Null Cipher Requested")
|
||||
}
|
||||
|
||||
fn get_description(&self) -> Cow<str> {
|
||||
fn get_description(&self) -> Cow<'_, str> {
|
||||
Cow::from(
|
||||
"Tests whether the MME requests to use a null cipher in the NAS security mode command",
|
||||
)
|
||||
@@ -48,18 +48,18 @@ impl Analyzer for NasNullCipherAnalyzer {
|
||||
_ => return None,
|
||||
};
|
||||
|
||||
if let NASMessage::EMMMessage(EMMMessage::EMMSecurityModeCommand(req)) = payload {
|
||||
if req.nas_sec_algo.inner.ciph_algo == EPSEncryptionAlgorithmEEA0Null {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High,
|
||||
},
|
||||
message: format!(
|
||||
"NAS Security mode command requested null cipher(packet {})",
|
||||
self.packet_num
|
||||
),
|
||||
});
|
||||
}
|
||||
if let NASMessage::EMMMessage(EMMMessage::EMMSecurityModeCommand(req)) = payload
|
||||
&& req.nas_sec_algo.inner.ciph_algo == EPSEncryptionAlgorithmEEA0Null
|
||||
{
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High,
|
||||
},
|
||||
message: format!(
|
||||
"NAS Security mode command requested null cipher(packet {})",
|
||||
self.packet_num
|
||||
),
|
||||
});
|
||||
}
|
||||
None
|
||||
}
|
||||
|
||||
@@ -37,10 +37,10 @@ impl NullCipherAnalyzer {
|
||||
Some(&rat.security_algorithm_config)
|
||||
}
|
||||
};
|
||||
if let Some(security_config) = maybe_security_config {
|
||||
if security_config.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
return true;
|
||||
}
|
||||
if let Some(security_config) = maybe_security_config
|
||||
&& security_config.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
// Use map/flatten to dig into a long chain of nested Option types
|
||||
@@ -62,10 +62,10 @@ impl NullCipherAnalyzer {
|
||||
.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 {
|
||||
if cipher.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
return true;
|
||||
}
|
||||
if let Some(cipher) = maybe_cipher
|
||||
&& cipher.0 == CipheringAlgorithm_r12::EEA0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -90,10 +90,10 @@ impl NullCipherAnalyzer {
|
||||
Some(&to_5gc.security_algorithm_config_r15)
|
||||
}
|
||||
};
|
||||
if let Some(security_algorithm) = maybe_security_algorithm {
|
||||
if security_algorithm.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0 {
|
||||
return true;
|
||||
}
|
||||
if let Some(security_algorithm) = maybe_security_algorithm
|
||||
&& security_algorithm.ciphering_algorithm.0 == CipheringAlgorithm_r12::EEA0
|
||||
{
|
||||
return true;
|
||||
}
|
||||
false
|
||||
}
|
||||
@@ -119,11 +119,11 @@ impl NullCipherAnalyzer {
|
||||
}
|
||||
|
||||
impl Analyzer for NullCipherAnalyzer {
|
||||
fn get_name(&self) -> Cow<str> {
|
||||
fn get_name(&self) -> Cow<'_, str> {
|
||||
Cow::from("Null Cipher")
|
||||
}
|
||||
|
||||
fn get_description(&self) -> Cow<str> {
|
||||
fn get_description(&self) -> Cow<'_, str> {
|
||||
Cow::from("Tests whether the cell suggests using a null cipher (EEA0)")
|
||||
}
|
||||
|
||||
|
||||
@@ -16,19 +16,15 @@ impl LteSib6And7DowngradeAnalyzer {
|
||||
&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
|
||||
{
|
||||
return Some(&sib.sib_type_and_info);
|
||||
}
|
||||
}
|
||||
}
|
||||
if let InformationElement::LTE(lte_ie) = ie
|
||||
&& let LteInformationElement::BcchDlSch(bcch_dl_sch_message) = &**lte_ie
|
||||
&& let BCCH_DL_SCH_MessageType::C1(BCCH_DL_SCH_MessageType_c1::SystemInformation(
|
||||
system_information,
|
||||
)) = &bcch_dl_sch_message.message
|
||||
&& let SystemInformationCriticalExtensions::SystemInformation_r8(sib) =
|
||||
&system_information.critical_extensions
|
||||
{
|
||||
return Some(&sib.sib_type_and_info);
|
||||
}
|
||||
None
|
||||
}
|
||||
@@ -36,11 +32,11 @@ impl LteSib6And7DowngradeAnalyzer {
|
||||
|
||||
// TODO: keep track of SIB state to compare LTE reselection blocks w/ 2g/3g ones
|
||||
impl Analyzer for LteSib6And7DowngradeAnalyzer {
|
||||
fn get_name(&self) -> Cow<str> {
|
||||
fn get_name(&self) -> Cow<'_, str> {
|
||||
Cow::from("LTE SIB 6/7 Downgrade")
|
||||
}
|
||||
|
||||
fn get_description(&self) -> Cow<str> {
|
||||
fn get_description(&self) -> Cow<'_, str> {
|
||||
Cow::from(
|
||||
"Tests for LTE cells broadcasting a SIB type 6 and 7 which include 2G/3G frequencies with higher priorities.",
|
||||
)
|
||||
@@ -62,13 +58,16 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer {
|
||||
for carrier_info in &carrier_info_list.0 {
|
||||
if let Some(CellReselectionPriority(p)) =
|
||||
carrier_info.cell_reselection_priority
|
||||
&& p == 0
|
||||
{
|
||||
if p == 0 {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning { severity: Severity::High },
|
||||
message: "LTE cell advertised a 3G cell for priority 0 reselection".to_string(),
|
||||
});
|
||||
}
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High,
|
||||
},
|
||||
message:
|
||||
"LTE cell advertised a 3G cell for priority 0 reselection"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -76,13 +75,16 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer {
|
||||
for carrier_info in &carrier_info_list.0 {
|
||||
if let Some(CellReselectionPriority(p)) =
|
||||
carrier_info.cell_reselection_priority
|
||||
&& p == 0
|
||||
{
|
||||
if p == 0 {
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning { severity: Severity::High },
|
||||
message: "LTE cell advertised a 3G cell for priority 0 reselection".to_string(),
|
||||
});
|
||||
}
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High,
|
||||
},
|
||||
message:
|
||||
"LTE cell advertised a 3G cell for priority 0 reselection"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,17 +98,15 @@ impl Analyzer for LteSib6And7DowngradeAnalyzer {
|
||||
for carrier_info in &carrier_info_list.0 {
|
||||
if let Some(CellReselectionPriority(p)) =
|
||||
carrier_info.common_info.cell_reselection_priority
|
||||
&& p == 0
|
||||
{
|
||||
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(),
|
||||
});
|
||||
}
|
||||
return Some(Event {
|
||||
event_type: EventType::QualitativeWarning {
|
||||
severity: Severity::High,
|
||||
},
|
||||
message: "LTE cell advertised a 2G cell for priority 0 reselection"
|
||||
.to_string(),
|
||||
});
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,33 +1 @@
|
||||
// Unpacks a pattern, or returns None.
|
||||
//
|
||||
// # Examples
|
||||
// You can use `unpack!` to unroll highly nested enums like this:
|
||||
// ```
|
||||
// enum Foo {
|
||||
// A(Bar),
|
||||
// B,
|
||||
// }
|
||||
//
|
||||
// enum Bar {
|
||||
// C(Baz)
|
||||
// }
|
||||
//
|
||||
// struct Baz;
|
||||
//
|
||||
// fn get_bang(foo: Foo) -> Option<Baz> {
|
||||
// unpack!(Foo::A(bar) = foo);
|
||||
// unpack!(Bar::C(baz) = bar);
|
||||
// baz
|
||||
// }
|
||||
// ```
|
||||
//
|
||||
macro_rules! unpack {
|
||||
($pat:pat = $val:expr) => {
|
||||
let $pat = $val else {
|
||||
return None;
|
||||
};
|
||||
};
|
||||
}
|
||||
|
||||
// this is apparently how you make a macro publicly usable from this module
|
||||
pub(crate) use unpack;
|
||||
|
||||
@@ -198,10 +198,10 @@ impl DiagDevice {
|
||||
return Err(DiagDeviceError::DeviceWriteFailed(err));
|
||||
}
|
||||
}
|
||||
if let Err(err) = self.file.flush().await {
|
||||
if err.kind() != ErrorKind::WriteZero {
|
||||
return Err(DiagDeviceError::DeviceWriteFailed(err));
|
||||
}
|
||||
if let Err(err) = self.file.flush().await
|
||||
&& err.kind() != ErrorKind::WriteZero
|
||||
{
|
||||
return Err(DiagDeviceError::DeviceWriteFailed(err));
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
|
||||
@@ -77,16 +77,16 @@ where
|
||||
pub async fn get_next_messages_container(
|
||||
&mut self,
|
||||
) -> Result<Option<MessagesContainer>, 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
|
||||
);
|
||||
}
|
||||
return Ok(None);
|
||||
if let Some(max_bytes) = self.max_bytes
|
||||
&& self.bytes_read >= max_bytes
|
||||
{
|
||||
if self.bytes_read > max_bytes {
|
||||
error!(
|
||||
"warning: {} bytes read, but max_bytes was {}",
|
||||
self.bytes_read, max_bytes
|
||||
);
|
||||
}
|
||||
return Ok(None);
|
||||
}
|
||||
|
||||
let mut buf = Vec::new();
|
||||
|
||||
Reference in New Issue
Block a user