feat: rust proto implementation

This commit is contained in:
Strix 2023-10-15 18:06:20 +02:00
parent 1e68840042
commit e4b508ae86
No known key found for this signature in database
GPG key ID: 49B2E37B8915B774
12 changed files with 381 additions and 0 deletions

1
.gitignore vendored Normal file
View file

@ -0,0 +1 @@
**/target/

25
domo_proto/Cargo.lock generated Normal file
View file

@ -0,0 +1,25 @@
# This file is automatically @generated by Cargo.
# It is not intended for manual editing.
version = 3
[[package]]
name = "cfg-if"
version = "1.0.0"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "baf1de4339761588bc0619e3cbc0120ee582ebb74b53b4efbf79117bd2da40fd"
[[package]]
name = "crc32fast"
version = "1.3.2"
source = "registry+https://github.com/rust-lang/crates.io-index"
checksum = "b540bd8bc810d3885c6ea91e2018302f68baba2129ab3e88f32389ee9370880d"
dependencies = [
"cfg-if",
]
[[package]]
name = "domo_proto"
version = "0.1.0"
dependencies = [
"crc32fast",
]

9
domo_proto/Cargo.toml Normal file
View file

@ -0,0 +1,9 @@
[package]
name = "domo_proto"
version = "0.1.0"
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
crc32fast = "1.3.2"

View file

@ -0,0 +1,5 @@
use crate::packet::packet_data::PacketData;
pub mod node_management;
pub trait Command: Into<PacketData> + From<PacketData> {}

View file

@ -0,0 +1,39 @@
use crate::packet::identifier::Identifier;
pub enum NodeManagement {
Ping,
RegisterNode { device_id: Identifier },
RemoveNode,
RegisterProperty {
property_name: String,
data_type: u8,
read_only: bool
},
RemoveProperty {
property_name: String,
},
Error {
error_code: u8,
metadata: [u8; 0xFFFF]
}
}
#[allow(non_camel_case_types)]
pub enum NodeManagementError {
net_duplicate_packet,
net_broken_packet,
net_invalid_packet,
errc_not_registered
}
impl NodeManagementError {
pub fn error_code(&self) -> u8 {
match self {
NodeManagementError::net_duplicate_packet => 0x00,
NodeManagementError::net_broken_packet => 0x01,
NodeManagementError::net_invalid_packet => 0x02,
NodeManagementError::errc_not_registered => 0x10
}
}
}

3
domo_proto/src/lib.rs Normal file
View file

@ -0,0 +1,3 @@
pub mod commands;
pub mod packet;
pub mod prelude;

21
domo_proto/src/main.rs Normal file
View file

@ -0,0 +1,21 @@
use domo_proto::packet::data_types::DataType;
use domo_proto::packet::identifier::Identifier;
use domo_proto::packet::packet_data::PacketData;
fn main() {
let _ = domo_proto::packet::Packet::V1 {
src: Identifier::from(0xAABBCCDD),
dest: Identifier::from(0xAABBCCDD),
packet_id: Identifier::from(0x00000001),
reply_to: Identifier::from(0x00000000),
command: 0x00,
data: PacketData::default(),
};
let buf = vec![
0x00, 0x01, 0x00, 0x10, 0x00, 0xFF, 0x20, 0x90, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
0x01, 0xF0, 0xAA, 0xBB, 0xCC, 0xDD,
];
println!("{:?}", DataType::get_data(buf));
}

View file

@ -0,0 +1,98 @@
use crate::prelude::{as_u32_be, as_u64_be};
#[derive(Debug)]
pub enum DataType {
// Basic types
Nothing,
Switch(bool),
Slider(u8),
Text(String),
// Cosmetic
RGB(u8, u8, u8),
// Time & Space
Seconds(u64),
// Domo Types
NodeRef(u32),
}
impl DataType {
pub fn get_data_size(data_type: u8) -> usize {
match data_type {
0x01 => 1,
0x02 => 1,
0x03 => 256,
0x10 => 3,
0x90 => 8,
0xF0 => 4,
_ => 0,
}
}
pub fn get_data(buf: Vec<u8>) -> Vec<DataType> {
let mut data = vec![];
let mut pieces = vec![];
let mut byte = buf.iter().peekable();
while let Some(&c) = byte.next() {
let mut piece = vec![c];
let mut len = DataType::get_data_size(c);
while len > 0 {
if let Some(_) = byte.peek() {
piece.push(*byte.next().unwrap());
}
len -= 1;
}
if piece.len() == DataType::get_data_size(c) + 1 {
pieces.push(piece);
} else {
pieces.push(vec![0x00]);
}
}
for piece in pieces {
data.push(DataType::from(piece))
}
data
}
}
macro_rules! impl_data_type {
($v: ident, $len: literal, $($t:tt)*) => {
if $v.len() >= $len {
$($t)*
} else {
DataType::Nothing
}
};
}
impl From<Vec<u8>> for DataType {
/// Turn a Vec<u8> into a single data type.
/// Returns Datatype::Nothing if it's nothing or can find nothing.
fn from(value: Vec<u8>) -> Self {
if value.len() == 0 {
return DataType::Nothing;
};
match value[0] {
0x01 => impl_data_type!(value, 1, DataType::Switch(value[1] != 0)),
0x02 => impl_data_type!(value, 1, DataType::Slider(value[1])),
0x03 => impl_data_type!(
value,
256,
if let Ok(s) = String::from_utf8(value[1..257].to_vec()) {
DataType::Text(s)
} else {
DataType::Nothing
}
),
0x10 => impl_data_type!(value, 3, DataType::RGB(value[1], value[2], value[3])),
0x90 => impl_data_type!(value, 8, DataType::Seconds(as_u64_be(&value[1..9]))),
0xF0 => impl_data_type!(value, 4, DataType::NodeRef(as_u32_be(&value[1..5]))),
_ => DataType::Nothing,
}
}
}

View file

@ -0,0 +1,45 @@
use std::fmt::{Formatter, LowerHex};
#[derive(Debug, Eq, PartialEq)]
pub struct Identifier {
pub bytes: [u8; 4],
}
impl LowerHex for Identifier {
fn fmt(&self, f: &mut Formatter<'_>) -> std::fmt::Result {
write!(
f,
"{:x}{:x}{:x}{:x}",
self.bytes[0], self.bytes[1], self.bytes[2], self.bytes[3]
)
}
}
impl From<&[u8]> for Identifier {
fn from(value: &[u8]) -> Self {
let mut bytes = [0_u8; 4];
bytes[..value.len()].copy_from_slice(value);
Identifier {
bytes: [bytes[0], bytes[1], bytes[2], bytes[3]],
}
}
}
impl From<Vec<u8>> for Identifier {
fn from(value: Vec<u8>) -> Self {
Identifier::from(value.as_slice())
}
}
impl From<u32> for Identifier {
fn from(value: u32) -> Self {
Identifier {
bytes: [
(value >> 24) as u8,
(value >> 16) as u8,
(value >> 8) as u8,
(value >> 0) as u8,
],
}
}
}

View file

@ -0,0 +1,94 @@
pub mod data_types;
pub mod identifier;
pub mod packet_data;
use crate::prelude;
use identifier::Identifier;
use packet_data::PacketData;
use std::convert::TryFrom;
use std::fmt::Debug;
#[derive(Debug)]
pub enum Packet {
V1 {
src: Identifier,
dest: Identifier,
packet_id: Identifier,
reply_to: Identifier,
command: u8,
data: PacketData,
},
}
impl Packet {
/// Build packet **without** CRC32
pub fn build_base_packet(&self) -> Vec<u8> {
match self {
Packet::V1 {
src,
dest,
packet_id,
reply_to,
command,
data,
} => {
let mut buf = Vec::new();
let data_length = data.len();
buf.push(0x01);
buf.extend_from_slice(&src.bytes);
buf.extend_from_slice(&dest.bytes);
buf.extend_from_slice(&packet_id.bytes);
buf.extend_from_slice(&reply_to.bytes);
buf.push(command.clone());
buf.push((data_length >> 8) as u8);
buf.push((data_length & 0xFF) as u8);
buf.extend(data.get_data());
buf
}
}
}
pub fn build_full_packet(&self) -> Vec<u8> {
match self {
Packet::V1 { .. } => {
let mut buf = self.build_base_packet();
buf.extend_from_slice(crc32fast::hash(&buf.as_slice()).to_be_bytes().as_ref());
buf
}
}
}
}
impl TryFrom<Vec<u8>> for Packet {
type Error = ();
fn try_from(value: Vec<u8>) -> Result<Self, Self::Error> {
match value[0] {
0x01 => {
let header: Vec<u16> = value[..0x15].iter().map(|v| u16::from(*v)).collect();
let data_length = (header[0x12] << 8) | header[0x13];
let crc32 = prelude::as_u32_be(&value[(0x14 + data_length as usize)..]);
let packet = Packet::V1 {
src: Identifier::from(&value[0x01..0x05]),
dest: Identifier::from(&value[0x05..0x09]),
packet_id: Identifier::from(&value[0x09..0x0D]),
reply_to: Identifier::from(&value[0x0D..0x11]),
command: value[0x11],
data: PacketData::new(value[0x14..(data_length as usize + 0x14)].to_owned()),
};
if crc32fast::hash(packet.build_base_packet().as_slice()) != crc32 {
Err(())
} else {
Ok(packet)
}
}
_ => Err(()),
}
}
}
impl Into<Vec<u8>> for Packet {
fn into(self) -> Vec<u8> {
self.build_full_packet()
}
}

View file

@ -0,0 +1,24 @@
#[derive(Debug)]
pub struct PacketData {
data: Vec<u8>,
}
impl PacketData {
pub fn new(data: Vec<u8>) -> PacketData {
PacketData { data }
}
pub fn len(&self) -> usize {
self.data.len()
}
pub fn get_data(&self) -> Vec<u8> {
self.data.clone()
}
}
impl Default for PacketData {
fn default() -> Self {
PacketData { data: vec![] }
}
}

17
domo_proto/src/prelude.rs Normal file
View file

@ -0,0 +1,17 @@
pub fn as_u32_be(array: &[u8]) -> u32 {
((array[0] as u32) << 24)
+ ((array[1] as u32) << 16)
+ ((array[2] as u32) << 8)
+ ((array[3] as u32) << 0)
}
pub fn as_u64_be(array: &[u8]) -> u64 {
((array[0] as u64) << 56)
+ ((array[1] as u64) << 48)
+ ((array[2] as u64) << 40)
+ ((array[3] as u64) << 32)
+ ((array[4] as u64) << 24)
+ ((array[5] as u64) << 16)
+ ((array[6] as u64) << 8)
+ ((array[7] as u64) << 0)
}