Start implementing device communication
This commit is contained in:
parent
6f1106ee28
commit
e0befff868
8 changed files with 201 additions and 21 deletions
21
src/command.rs
Normal file
21
src/command.rs
Normal file
|
@ -0,0 +1,21 @@
|
||||||
|
use serde::{Deserialize, Serialize};
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct Command {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Command {
|
||||||
|
pub fn sub_type(device_type: u32) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Debug, Clone, Serialize, Deserialize)]
|
||||||
|
pub struct CommandResponse {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
impl CommandResponse {
|
||||||
|
//
|
||||||
|
}
|
143
src/device.rs
143
src/device.rs
|
@ -1,27 +1,53 @@
|
||||||
use std::{
|
use std::{
|
||||||
io::{Read, Write},
|
io::{Read, Write},
|
||||||
net::TcpStream,
|
net::TcpStream,
|
||||||
|
sync::Mutex,
|
||||||
thread,
|
thread,
|
||||||
time::Duration,
|
time::Duration,
|
||||||
};
|
};
|
||||||
|
|
||||||
use anyhow::{bail, Context, Error, Result};
|
use anyhow::{bail, Context, Error, Result};
|
||||||
|
use serde_json::to_string;
|
||||||
|
|
||||||
use crate::{
|
use crate::{
|
||||||
|
command::Command,
|
||||||
devices::{e1::E1, DeviceBackend},
|
devices::{e1::E1, DeviceBackend},
|
||||||
hex,
|
hex,
|
||||||
|
packet_builder::PacketBuilder,
|
||||||
security::{MsgType, Security},
|
security::{MsgType, Security},
|
||||||
DeviceInfo,
|
DeviceInfo,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[repr(u8)]
|
||||||
|
enum MessageType {
|
||||||
|
Set = 0x02,
|
||||||
|
Query = 0x03,
|
||||||
|
Notify1 = 0x04,
|
||||||
|
Notify2 = 0x05,
|
||||||
|
Exception = 0x06,
|
||||||
|
QuerySN = 0x07,
|
||||||
|
Exception2 = 0x0A,
|
||||||
|
QuerySubtype = 0xA0,
|
||||||
|
}
|
||||||
|
|
||||||
|
enum ParseMessage {
|
||||||
|
Success,
|
||||||
|
Padding,
|
||||||
|
}
|
||||||
|
|
||||||
pub struct Device {
|
pub struct Device {
|
||||||
info: DeviceInfo,
|
info: DeviceInfo,
|
||||||
|
|
||||||
socket: TcpStream,
|
socket: Mutex<TcpStream>,
|
||||||
security: Security,
|
security: Security,
|
||||||
|
|
||||||
device_backend: Box<dyn DeviceBackend>,
|
device_backend: Box<dyn DeviceBackend>,
|
||||||
|
|
||||||
|
buffer: Vec<u8>,
|
||||||
|
sub_type: Option<String>,
|
||||||
|
|
||||||
|
updates: Vec<Box<dyn Fn(&[u8]) -> Result<()>>>,
|
||||||
|
|
||||||
token: [u8; 64],
|
token: [u8; 64],
|
||||||
key: [u8; 32],
|
key: [u8; 32],
|
||||||
}
|
}
|
||||||
|
@ -52,9 +78,14 @@ impl Device {
|
||||||
}),
|
}),
|
||||||
|
|
||||||
info,
|
info,
|
||||||
socket,
|
socket: Mutex::new(socket),
|
||||||
security: Security::default(),
|
security: Security::default(),
|
||||||
|
|
||||||
|
buffer: Vec::new(),
|
||||||
|
sub_type: None,
|
||||||
|
|
||||||
|
updates: Vec::new(),
|
||||||
|
|
||||||
token: hex(token)?.try_into().unwrap(),
|
token: hex(token)?.try_into().unwrap(),
|
||||||
key: hex(key)?.try_into().unwrap(),
|
key: hex(key)?.try_into().unwrap(),
|
||||||
};
|
};
|
||||||
|
@ -73,10 +104,12 @@ impl Device {
|
||||||
.security
|
.security
|
||||||
.encode_8370(&self.token, MsgType::HANDSHAKE_REQUEST)?;
|
.encode_8370(&self.token, MsgType::HANDSHAKE_REQUEST)?;
|
||||||
|
|
||||||
self.socket.write(&request)?;
|
let mut socket = self.socket.lock().unwrap();
|
||||||
|
|
||||||
|
socket.write(&request)?;
|
||||||
|
|
||||||
let mut buffer = [0; 512];
|
let mut buffer = [0; 512];
|
||||||
let bytes_read = self.socket.read(&mut buffer)?;
|
let bytes_read = socket.read(&mut buffer)?;
|
||||||
|
|
||||||
if bytes_read < 20 {
|
if bytes_read < 20 {
|
||||||
bail!(
|
bail!(
|
||||||
|
@ -90,9 +123,109 @@ impl Device {
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn refresh_status(&self) -> Result<()> {
|
pub fn refresh_status(&mut self) -> Result<()> {
|
||||||
|
let mut cmds = self.device_backend.build_query()?;
|
||||||
|
|
||||||
|
if self.sub_type.is_none() {
|
||||||
|
cmds.insert(0, Command::sub_type(self.info.device_type));
|
||||||
|
}
|
||||||
|
|
||||||
|
for cmd in cmds {
|
||||||
|
self.build_send(cmd)?;
|
||||||
|
|
||||||
|
loop {
|
||||||
|
let mut buf = [0; 512];
|
||||||
|
let bytes_read = self.socket.lock().unwrap().read(&mut buf)?;
|
||||||
|
|
||||||
|
if bytes_read == 0 {
|
||||||
|
bail!("socket error");
|
||||||
|
}
|
||||||
|
|
||||||
|
match self.parse_message(&buf[..bytes_read])? {
|
||||||
|
ParseMessage::Success => break,
|
||||||
|
ParseMessage::Padding => continue,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn register_update<F>(&mut self, f: F)
|
||||||
|
where
|
||||||
|
F: Fn(&[u8]) -> Result<()> + 'static,
|
||||||
|
{
|
||||||
|
self.updates.push(Box::new(f));
|
||||||
|
}
|
||||||
|
|
||||||
|
fn parse_message(&mut self, msg: &[u8]) -> Result<ParseMessage> {
|
||||||
|
let (messages, buffer) = self.security.decode_8370(&self.buffer, msg)?;
|
||||||
|
self.buffer = buffer;
|
||||||
|
|
||||||
|
if messages.is_empty() {
|
||||||
|
return Ok(ParseMessage::Padding);
|
||||||
|
}
|
||||||
|
|
||||||
|
for mut message in messages {
|
||||||
|
if message == b"ERROR" {
|
||||||
|
bail!("parse message error");
|
||||||
|
}
|
||||||
|
|
||||||
|
let payload_len = message[4] as u16 + ((message[5] as u16) << 8) - 56;
|
||||||
|
let payload_type = message[2] as u16 + ((message[3] as u16) << 8);
|
||||||
|
|
||||||
|
// heartbeat
|
||||||
|
if payload_type == 0x1001 || payload_type == 0x0001 {
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if message.len() > 56 && payload_len % 16 == 0 {
|
||||||
|
let len = message.len();
|
||||||
|
let crypt = &mut message[40..len - 16];
|
||||||
|
|
||||||
|
Security::aes_decrypt(crypt);
|
||||||
|
|
||||||
|
if self.pre_process_message(crypt)? {
|
||||||
|
let status = self.device_backend.process_message(crypt)?;
|
||||||
|
|
||||||
|
if status.len() > 0 {
|
||||||
|
for update in self.updates.iter() {
|
||||||
|
update(&status)?;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
Ok(ParseMessage::Success)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn pre_process_message(&self, msg: &[u8]) -> Result<bool> {
|
||||||
|
if msg[9] == MessageType::QuerySubtype as u8 {
|
||||||
|
let message = todo!();
|
||||||
|
|
||||||
|
Ok(false)
|
||||||
|
} else {
|
||||||
|
Ok(true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_message(&self, msg: &[u8]) -> Result<()> {
|
||||||
|
let data = self.security.encode_8370(msg, MsgType::ENCRYPTED_REQUEST)?;
|
||||||
|
self.socket.lock().unwrap().write(&data)?;
|
||||||
|
|
||||||
|
Ok(())
|
||||||
|
}
|
||||||
|
|
||||||
|
fn send_heartbeat(&self) -> Result<()> {
|
||||||
|
let msg = PacketBuilder::builder(self.info.id, &[0x00]).finalize(0);
|
||||||
|
self.send_message(&msg)
|
||||||
|
}
|
||||||
|
|
||||||
|
fn build_send(&self, cmd: Command) -> Result<()> {
|
||||||
|
let data = PacketBuilder::builder(self.info.id, to_string(&cmd)?.as_bytes()).finalize(1);
|
||||||
|
self.send_message(&data)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
|
@ -2,6 +2,8 @@ use std::collections::HashMap;
|
||||||
|
|
||||||
use anyhow::Result;
|
use anyhow::Result;
|
||||||
|
|
||||||
|
use crate::command::Command;
|
||||||
|
|
||||||
use super::DeviceBackend;
|
use super::DeviceBackend;
|
||||||
|
|
||||||
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
|
||||||
|
@ -189,11 +191,11 @@ impl E1 {
|
||||||
}
|
}
|
||||||
|
|
||||||
impl DeviceBackend for E1 {
|
impl DeviceBackend for E1 {
|
||||||
fn build_query(&self) -> () {
|
fn build_query(&self) -> Result<Vec<Command>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
fn process_message(&self, msg: &str) -> () {
|
fn process_message(&self, msg: &[u8]) -> Result<Vec<u8>> {
|
||||||
todo!()
|
todo!()
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,7 +1,9 @@
|
||||||
|
use crate::command::Command;
|
||||||
|
|
||||||
pub mod e1;
|
pub mod e1;
|
||||||
|
|
||||||
pub trait DeviceBackend {
|
pub trait DeviceBackend {
|
||||||
fn build_query(&self) -> ();
|
fn build_query(&self) -> anyhow::Result<Vec<Command>>;
|
||||||
fn process_message(&self, msg: &str) -> ();
|
fn process_message(&self, msg: &[u8]) -> anyhow::Result<Vec<u8>>;
|
||||||
fn set_attribute(&self, attribute: &str, value: &str) -> ();
|
fn set_attribute(&self, attribute: &str, value: &str) -> ();
|
||||||
}
|
}
|
||||||
|
|
|
@ -72,7 +72,8 @@ impl Startup {
|
||||||
}
|
}
|
||||||
|
|
||||||
let len = bytes.len();
|
let len = bytes.len();
|
||||||
let encrypt_data = Security::aes_decrypt(&mut bytes[40..(len - 16)]);
|
let encrypt_data = &mut bytes[40..(len - 16)];
|
||||||
|
Security::aes_decrypt(encrypt_data);
|
||||||
|
|
||||||
let start = 41;
|
let start = 41;
|
||||||
let upper = start + encrypt_data[40] as usize;
|
let upper = start + encrypt_data[40] as usize;
|
||||||
|
|
|
@ -2,9 +2,11 @@ use std::num::ParseIntError;
|
||||||
|
|
||||||
mod cloud;
|
mod cloud;
|
||||||
mod cloud_security;
|
mod cloud_security;
|
||||||
|
mod command;
|
||||||
mod device;
|
mod device;
|
||||||
mod devices;
|
mod devices;
|
||||||
mod discover;
|
mod discover;
|
||||||
|
mod packet_builder;
|
||||||
mod security;
|
mod security;
|
||||||
|
|
||||||
fn hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
|
fn hex(s: &str) -> Result<Vec<u8>, ParseIntError> {
|
||||||
|
|
13
src/packet_builder.rs
Normal file
13
src/packet_builder.rs
Normal file
|
@ -0,0 +1,13 @@
|
||||||
|
pub struct PacketBuilder {
|
||||||
|
//
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PacketBuilder {
|
||||||
|
pub fn builder(device_id: u64, msg: &[u8]) -> Self {
|
||||||
|
Self {}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub fn finalize(self, msg_type: u8) -> Vec<u8> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,3 +1,5 @@
|
||||||
|
use std::sync::atomic::{AtomicU16, Ordering::SeqCst};
|
||||||
|
|
||||||
use aes::{
|
use aes::{
|
||||||
cipher::{
|
cipher::{
|
||||||
block_padding::NoPadding, generic_array::GenericArray, BlockDecrypt, BlockDecryptMut,
|
block_padding::NoPadding, generic_array::GenericArray, BlockDecrypt, BlockDecryptMut,
|
||||||
|
@ -22,8 +24,8 @@ pub enum MsgType {
|
||||||
|
|
||||||
#[derive(Debug, Default)]
|
#[derive(Debug, Default)]
|
||||||
pub struct Security {
|
pub struct Security {
|
||||||
request_count: u16,
|
request_count: AtomicU16,
|
||||||
response_count: u16,
|
response_count: AtomicU16,
|
||||||
|
|
||||||
tcp_key: Option<[u8; 32]>,
|
tcp_key: Option<[u8; 32]>,
|
||||||
}
|
}
|
||||||
|
@ -33,7 +35,7 @@ impl Security {
|
||||||
const KEY: [u8; 16] = Self::N.to_be_bytes();
|
const KEY: [u8; 16] = Self::N.to_be_bytes();
|
||||||
const IV: [u8; 16] = [b'\0'; 16];
|
const IV: [u8; 16] = [b'\0'; 16];
|
||||||
|
|
||||||
pub fn aes_decrypt(data: &mut [u8]) -> &[u8] {
|
pub fn aes_decrypt(data: &mut [u8]) {
|
||||||
let array = GenericArray::from(Self::KEY);
|
let array = GenericArray::from(Self::KEY);
|
||||||
let cipher = Aes128::new(&array);
|
let cipher = Aes128::new(&array);
|
||||||
|
|
||||||
|
@ -41,8 +43,6 @@ impl Security {
|
||||||
let mut block = GenericArray::from_mut_slice(chunk);
|
let mut block = GenericArray::from_mut_slice(chunk);
|
||||||
cipher.decrypt_block(&mut block);
|
cipher.decrypt_block(&mut block);
|
||||||
}
|
}
|
||||||
|
|
||||||
data
|
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn aes_cbc_encrypt(&self, raw: [u8; 32], key: &[u8; 32]) -> [u8; 32] {
|
pub fn aes_cbc_encrypt(&self, raw: [u8; 32], key: &[u8; 32]) -> [u8; 32] {
|
||||||
|
@ -84,8 +84,8 @@ impl Security {
|
||||||
}
|
}
|
||||||
|
|
||||||
self.tcp_key = Some(Self::xorstr(&result, key).try_into().unwrap());
|
self.tcp_key = Some(Self::xorstr(&result, key).try_into().unwrap());
|
||||||
self.request_count = 0;
|
self.request_count.store(0, SeqCst);
|
||||||
self.response_count = 0;
|
self.response_count.store(0, SeqCst);
|
||||||
|
|
||||||
Ok(())
|
Ok(())
|
||||||
}
|
}
|
||||||
|
@ -96,7 +96,7 @@ impl Security {
|
||||||
lhs.iter().zip(rhs.iter()).map(|(&l, &r)| l ^ r).collect()
|
lhs.iter().zip(rhs.iter()).map(|(&l, &r)| l ^ r).collect()
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn encode_8370(&mut self, msg: &[u8], msg_type: MsgType) -> Result<Vec<u8>> {
|
pub fn encode_8370(&self, msg: &[u8], msg_type: MsgType) -> Result<Vec<u8>> {
|
||||||
let mut header = hex("8370")?;
|
let mut header = hex("8370")?;
|
||||||
let mut data: Vec<u8> = msg.to_vec();
|
let mut data: Vec<u8> = msg.to_vec();
|
||||||
|
|
||||||
|
@ -120,13 +120,15 @@ impl Security {
|
||||||
header.extend([0x20, (padding << 4) as u8 | msg_type as u8]);
|
header.extend([0x20, (padding << 4) as u8 | msg_type as u8]);
|
||||||
|
|
||||||
data = {
|
data = {
|
||||||
let mut b = self.request_count.to_be_bytes().to_vec();
|
let mut b = self
|
||||||
|
.request_count
|
||||||
|
.fetch_add(1, SeqCst)
|
||||||
|
.to_be_bytes()
|
||||||
|
.to_vec();
|
||||||
b.extend(data);
|
b.extend(data);
|
||||||
b
|
b
|
||||||
};
|
};
|
||||||
|
|
||||||
(self.request_count, _) = self.request_count.overflowing_add(1);
|
|
||||||
|
|
||||||
if msg_type == MsgType::ENCRYPTED_RESPONSE || msg_type == MsgType::ENCRYPTED_REQUEST {
|
if msg_type == MsgType::ENCRYPTED_RESPONSE || msg_type == MsgType::ENCRYPTED_REQUEST {
|
||||||
let sign: Vec<u8> = Sha256::digest(Self::add_bytes(header.clone(), data.clone()))
|
let sign: Vec<u8> = Sha256::digest(Self::add_bytes(header.clone(), data.clone()))
|
||||||
.into_iter()
|
.into_iter()
|
||||||
|
@ -146,6 +148,10 @@ impl Security {
|
||||||
lhs.extend(rhs);
|
lhs.extend(rhs);
|
||||||
lhs
|
lhs
|
||||||
}
|
}
|
||||||
|
|
||||||
|
pub fn decode_8370(&self, buffer: &[u8], msg: &[u8]) -> Result<(Vec<Vec<u8>>, Vec<u8>)> {
|
||||||
|
todo!()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
#[cfg(test)]
|
#[cfg(test)]
|
||||||
|
|
Loading…
Reference in a new issue