mirror of
https://github.com/EFForg/rayhunter.git
synced 2026-07-21 07:08:10 -07:00
log parsing
This commit is contained in:
+65
-13
@@ -1,5 +1,6 @@
|
||||
//! Diag protocol serialization/deserialization
|
||||
|
||||
use chrono::{DateTime, Local, FixedOffset};
|
||||
use deku::prelude::*;
|
||||
|
||||
#[derive(Debug, Clone, DekuWrite)]
|
||||
@@ -43,31 +44,66 @@ pub enum DataType {
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, DekuRead)]
|
||||
pub struct ResponseContainer {
|
||||
pub struct MessagesContainer {
|
||||
pub data_type: DataType,
|
||||
pub num_responses: u32,
|
||||
#[deku(count = "num_responses")]
|
||||
pub responses: Vec<HdlcEncapsulatedResponse>,
|
||||
pub messages: Vec<HdlcEncapsulatedMessage>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, DekuRead)]
|
||||
pub struct HdlcEncapsulatedResponse {
|
||||
pub struct HdlcEncapsulatedMessage {
|
||||
pub len: u32,
|
||||
#[deku(count = "len")]
|
||||
pub data: Vec<u8>,
|
||||
}
|
||||
|
||||
// kinda unpleasant deku hackery here. deku expects an enum's variant to be
|
||||
// right before its data, but in this case, a status value comes between the
|
||||
// variants and the data. so we need to use deku's context (ctx) feature to pass
|
||||
// those opcodes down to their respective parsers.
|
||||
#[derive(Debug, Clone, DekuRead)]
|
||||
pub struct Response {
|
||||
opcode: u32,
|
||||
subopcode: u32,
|
||||
pub status: u32,
|
||||
#[deku(ctx = "*opcode, *subopcode")]
|
||||
pub payload: ResponsePayload,
|
||||
#[deku(type = "u8")]
|
||||
pub enum Message {
|
||||
#[deku(id = "16")]
|
||||
Log {
|
||||
pending_msgs: u8,
|
||||
outer_length: u16,
|
||||
inner_length: u16,
|
||||
log_type: u16,
|
||||
timestamp: Timestamp,
|
||||
#[deku(count = "inner_length - 12")]
|
||||
payload: Vec<u8>,
|
||||
},
|
||||
|
||||
// kinda unpleasant deku hackery here. deku expects an enum's variant to be
|
||||
// right before its data, but in this case, a status value comes between the
|
||||
// variants and the data. so we need to use deku's context (ctx) feature to
|
||||
// pass those opcodes down to their respective parsers.
|
||||
#[deku(id_pat = "_")]
|
||||
Response {
|
||||
opcode: u32,
|
||||
subopcode: u32,
|
||||
status: u32,
|
||||
#[deku(ctx = "*opcode, *subopcode")]
|
||||
payload: ResponsePayload,
|
||||
},
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, DekuRead)]
|
||||
#[deku(endian = "little")]
|
||||
pub struct Timestamp {
|
||||
pub ts: u64,
|
||||
}
|
||||
|
||||
impl Timestamp {
|
||||
pub fn to_datetime(&self) -> DateTime<FixedOffset> {
|
||||
// Upper 48 bits: epoch at 1980-01-06 00:00:00, incremented by 1 for 1/800s
|
||||
// Lower 16 bits: time since last 1/800s tick in 1/32 chip units
|
||||
let ts_upper = self.ts >> 16;
|
||||
let ts_lower = self.ts & 0xffff;
|
||||
let epoch = chrono::DateTime::parse_from_rfc3339("1980-01-06T00:00:00-00:00").unwrap();
|
||||
let mut delta_seconds = ts_upper as f64 * 1.25;
|
||||
delta_seconds += ts_lower as f64 / 40960.0;
|
||||
let ts_delta = chrono::Duration::milliseconds(delta_seconds as i64);
|
||||
epoch + ts_delta
|
||||
}
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, DekuRead)]
|
||||
@@ -177,4 +213,20 @@ mod test {
|
||||
1, 2, 3, 4,
|
||||
]);
|
||||
}
|
||||
|
||||
#[test]
|
||||
fn test_message_parsing() {
|
||||
let msg_bytes = vec![16, 0, 26, 0, 26, 0, 167, 24, 38, 161, 72, 107, 146, 30, 2, 1, 1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0];
|
||||
match Message::from_bytes((msg_bytes.as_slice(), 0)) {
|
||||
Ok((_, Message::Log { pending_msgs, outer_length, inner_length, log_type, timestamp, payload })) => {
|
||||
assert_eq!(pending_msgs, 0);
|
||||
assert_eq!(outer_length, 26);
|
||||
assert_eq!(inner_length, 26);
|
||||
assert_eq!(log_type, 6311);
|
||||
assert_eq!(timestamp.to_datetime().date_naive(), chrono::NaiveDate::from_ymd_opt(2023, 12, 4).unwrap());
|
||||
assert_eq!(payload, vec![1, 1, 0, 0, 0, 0, 0, 140, 10, 0, 0, 220, 5, 0]);
|
||||
},
|
||||
_ => panic!("failed to parse message"),
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+42
-31
@@ -1,5 +1,5 @@
|
||||
use crate::hdlc::{hdlc_encapsulate, hdlc_decapsulate, HdlcError};
|
||||
use crate::diag::{Response, ResponsePayload, Request, LogConfigRequest, LogConfigResponse, build_log_mask_request, RequestContainer, DataType, ResponseContainer};
|
||||
use crate::diag::{Message, ResponsePayload, Request, LogConfigRequest, LogConfigResponse, build_log_mask_request, RequestContainer, DataType, MessagesContainer};
|
||||
|
||||
use std::fs::File;
|
||||
use std::io::{Cursor, Read, Write};
|
||||
@@ -72,32 +72,35 @@ impl DiagDevice {
|
||||
})
|
||||
}
|
||||
|
||||
fn parse_response_container(&self, container: ResponseContainer) -> DiagResult<Vec<Response>> {
|
||||
fn parse_response_container(&self, container: MessagesContainer) -> DiagResult<Vec<Message>> {
|
||||
let mut result = Vec::new();
|
||||
for msg in container.responses {
|
||||
let data = hdlc_decapsulate(msg.data, &self.crc)?;
|
||||
match Response::from_bytes((&data, 0)) {
|
||||
Ok(((_, leftover_bytes), res)) => {
|
||||
if leftover_bytes > 0 {
|
||||
println!("warning: {} leftover bytes when Response", leftover_bytes);
|
||||
}
|
||||
result.push(res);
|
||||
},
|
||||
Err(e) => {
|
||||
println!("{:?}", data);
|
||||
println!("error parsing response: {:?}", e);
|
||||
for msg in container.messages {
|
||||
match hdlc_decapsulate(msg.data, &self.crc) {
|
||||
Ok(data) => match Message::from_bytes((&data, 0)) {
|
||||
Ok(((_, leftover_bytes), res)) => {
|
||||
if leftover_bytes > 0 {
|
||||
println!("warning: {} leftover bytes when Response", leftover_bytes);
|
||||
}
|
||||
result.push(res);
|
||||
},
|
||||
Err(e) => {
|
||||
println!("error parsing response: {:?}", e);
|
||||
},
|
||||
},
|
||||
Err(err) => {
|
||||
println!("error decapsulating response: {:?}", err);
|
||||
}
|
||||
}
|
||||
}
|
||||
Ok(result)
|
||||
}
|
||||
|
||||
pub fn read_response(&mut self) -> DiagResult<Vec<Response>> {
|
||||
pub fn read_response(&mut self) -> DiagResult<Vec<Message>> {
|
||||
let mut buf = vec![0; BUFFER_LEN];
|
||||
|
||||
loop {
|
||||
let _ = self.file.read(&mut buf)?;
|
||||
let ((_, leftover_bytes), res_container) = ResponseContainer::from_bytes((&buf, 0))?;
|
||||
let ((_, leftover_bytes), res_container) = MessagesContainer::from_bytes((&buf, 0))?;
|
||||
if leftover_bytes > 0 {
|
||||
println!("warning: {} leftover bytes when parsing ResponseContainer", leftover_bytes);
|
||||
}
|
||||
@@ -124,7 +127,7 @@ impl DiagDevice {
|
||||
let msg = format!("write failed with error code {}", ret);
|
||||
return Err(DiagDeviceError::DeviceReadFailed(msg));
|
||||
}
|
||||
println!("{}. wrote {} bytes to device", buf.len(), ret);
|
||||
println!("wrote {} bytes to device", ret);
|
||||
}
|
||||
Ok(())
|
||||
}
|
||||
@@ -133,15 +136,18 @@ impl DiagDevice {
|
||||
let req = Request::LogConfig(LogConfigRequest::RetrieveIdRanges);
|
||||
self.write_request(&req)?;
|
||||
|
||||
for res in self.read_response()? {
|
||||
match res.payload {
|
||||
ResponsePayload::LogConfig(LogConfigResponse::RetrieveIdRanges { log_mask_sizes }) => {
|
||||
if res.status != 0 {
|
||||
return Err(DiagDeviceError::RequestFailed(res.status, req));
|
||||
}
|
||||
return Ok(log_mask_sizes);
|
||||
for msg in self.read_response()? {
|
||||
match msg {
|
||||
Message::Log { .. } => println!("skipping log response..."),
|
||||
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);
|
||||
},
|
||||
_ => println!("skipping non-LogConfigResponse response..."),
|
||||
},
|
||||
_ => println!("skipping non-LogConfigResponse response..."),
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,12 +159,17 @@ impl DiagDevice {
|
||||
let req = build_log_mask_request(log_type, log_mask_bitsize);
|
||||
self.write_request(&req)?;
|
||||
|
||||
for res in self.read_response()? {
|
||||
if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = res.payload {
|
||||
if res.status != 0 {
|
||||
return Err(DiagDeviceError::RequestFailed(res.status, req));
|
||||
}
|
||||
return Ok(());
|
||||
for msg in self.read_response()? {
|
||||
match msg {
|
||||
Message::Log { .. } => println!("skipping log response..."),
|
||||
Message::Response { payload, status, .. } => {
|
||||
if let ResponsePayload::LogConfig(LogConfigResponse::SetMask) = payload {
|
||||
if status != 0 {
|
||||
return Err(DiagDeviceError::RequestFailed(status, req));
|
||||
}
|
||||
return Ok(());
|
||||
}
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user