From f88706bac87426b80a2ebb52bd88ad4c2054676d Mon Sep 17 00:00:00 2001 From: Raine Date: Sun, 15 Oct 2023 18:06:34 +0200 Subject: [PATCH] feat: new stuff --- .idea/.gitignore | 8 ++ .idea/domo.iml | 14 ++++ .idea/encodings.xml | 6 ++ .idea/misc.xml | 10 +++ .idea/modules.xml | 8 ++ .idea/vcs.xml | 6 ++ doc/domo_node/node_types.md | 4 +- domo_node/config.toml | 2 +- domo_node/ping_packet | Bin 0 -> 30 bytes domo_node/register_packet | Bin 0 -> 34 bytes domo_node/src/app/master.rs | 72 ++++++++++++++++++ domo_node/src/{app.rs => app/mod.rs} | 53 +++++++++---- domo_node/src/connection/server.rs | 18 ++--- domo_node/src/prelude.rs | 12 +++ domo_node/test.py | 36 +++++++++ .../src/commands/node_management/vec.rs | 4 +- domo_proto/src/packet/packet_data.rs | 4 +- domo_proto/src/packet/raw_packet/packet.rs | 4 + domo_proto/src/packet/raw_packet/vec.rs | 3 +- recv_server.py | 45 ----------- 20 files changed, 232 insertions(+), 77 deletions(-) create mode 100644 .idea/.gitignore create mode 100644 .idea/domo.iml create mode 100644 .idea/encodings.xml create mode 100644 .idea/misc.xml create mode 100644 .idea/modules.xml create mode 100644 .idea/vcs.xml create mode 100644 domo_node/ping_packet create mode 100644 domo_node/register_packet create mode 100644 domo_node/src/app/master.rs rename domo_node/src/{app.rs => app/mod.rs} (56%) create mode 100644 domo_node/test.py delete mode 100644 recv_server.py diff --git a/.idea/.gitignore b/.idea/.gitignore new file mode 100644 index 0000000..13566b8 --- /dev/null +++ b/.idea/.gitignore @@ -0,0 +1,8 @@ +# Default ignored files +/shelf/ +/workspace.xml +# Editor-based HTTP Client requests +/httpRequests/ +# Datasource local storage ignored files +/dataSources/ +/dataSources.local.xml diff --git a/.idea/domo.iml b/.idea/domo.iml new file mode 100644 index 0000000..e8ac0a0 --- /dev/null +++ b/.idea/domo.iml @@ -0,0 +1,14 @@ + + + + + + + + + + + + + + \ No newline at end of file diff --git a/.idea/encodings.xml b/.idea/encodings.xml new file mode 100644 index 0000000..a8e7b91 --- /dev/null +++ b/.idea/encodings.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/.idea/misc.xml b/.idea/misc.xml new file mode 100644 index 0000000..2ca2389 --- /dev/null +++ b/.idea/misc.xml @@ -0,0 +1,10 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/modules.xml b/.idea/modules.xml new file mode 100644 index 0000000..4d64f43 --- /dev/null +++ b/.idea/modules.xml @@ -0,0 +1,8 @@ + + + + + + + + \ No newline at end of file diff --git a/.idea/vcs.xml b/.idea/vcs.xml new file mode 100644 index 0000000..94a25f7 --- /dev/null +++ b/.idea/vcs.xml @@ -0,0 +1,6 @@ + + + + + + \ No newline at end of file diff --git a/doc/domo_node/node_types.md b/doc/domo_node/node_types.md index d63fcc1..dc2818f 100644 --- a/doc/domo_node/node_types.md +++ b/doc/domo_node/node_types.md @@ -8,7 +8,7 @@ The job of a master node is rather complex. ## Forwarding packets The most straight forward job of the node. It works by essentially having a hashmap with a reference to a socket. -When a packet comes in the node gets forwarded to the right socket. +When a packet comes in the packet gets forwarded to the right node and socket. ## Handling network state The master node ensures there are no duplicate identifiers and therefore nodes. @@ -16,7 +16,7 @@ Also, it will make sure no invalid updates can be sent. Most packets go through the master node before getting to the source. Most because the subnet node can have private nodes and handle those themselves. -# The Bridge node (`relay`) +# The Bridge node (`bridge`) The bridge node is simple. In config you define where to forward the packets to, and they get delivered to there. diff --git a/domo_node/config.toml b/domo_node/config.toml index e79669e..940b986 100644 --- a/domo_node/config.toml +++ b/domo_node/config.toml @@ -1,5 +1,5 @@ [node.type.master] -bind = "127.0.0.1:4480" +bind = "0.0.0.0:4480" [node] device_id = "ffffffff" diff --git a/domo_node/ping_packet b/domo_node/ping_packet new file mode 100644 index 0000000000000000000000000000000000000000..c3d68d762d40777f86ab710ebb38aad5ec6988ae GIT binary patch literal 30 acmZSl4+IPh46DA|?u!J{2oOKN&=~-(O9-U^ literal 0 HcmV?d00001 diff --git a/domo_node/register_packet b/domo_node/register_packet new file mode 100644 index 0000000000000000000000000000000000000000..a3eb7ecd00502bd594089d1ffeee92648b815c5d GIT binary patch literal 34 hcmZSl4+JHdf9)>6d|1c;1dJd82v~r4)!~BUmjMbC44?o2 literal 0 HcmV?d00001 diff --git a/domo_node/src/app/master.rs b/domo_node/src/app/master.rs new file mode 100644 index 0000000..944e9c9 --- /dev/null +++ b/domo_node/src/app/master.rs @@ -0,0 +1,72 @@ +use std::collections::HashMap; +use std::io; +use std::net::SocketAddr; +use log::{info, trace}; +use domo_proto::data_types::DataType; +use domo_proto::identifier::Identifier; + +pub struct Device { + /// Device identifier + pub device_id: Identifier, + /// Device socket address + pub socket_addr: SocketAddr, + /// List of properties and last state. + pub properties: HashMap +} + +pub struct Devices { + devices: HashMap +} + +impl Devices { + pub fn new() -> Self { + Self { + devices: HashMap::new() + } + } + + pub fn add_device_random(&mut self, socket_addr: SocketAddr) -> Identifier { + let random_id = loop { + // TODO: random should avoid 0xFFFF---- + let id = Identifier::random(); + trace!("checking if id is taken: {}", id); + if !self.devices.contains_key(&id) { break id; } + }; + self.devices.insert(random_id, Device { + device_id: random_id, + socket_addr, + properties: HashMap::new() + }); + info!("Registered new device: {}", random_id); + random_id + } + + pub fn add_device(&mut self, socket_addr: SocketAddr, identifier: Identifier) -> Identifier { + self.devices.insert(identifier, Device { + device_id: identifier, + socket_addr, + properties: HashMap::new() + }); + identifier + } + + pub fn add_device_auto(&mut self, socket_addr: SocketAddr, identifier: Option) -> Identifier { + let identifier = if identifier == Some(Identifier::default()) { + None + } else { + identifier + }; + if let Some(identifier) = identifier { + match (identifier, self.devices.contains_key(&identifier)) { + (identifier, false) => self.add_device(socket_addr, identifier), + _ => self.add_device_random(socket_addr) + } + } else { + self.add_device_random(socket_addr) + } + } + + pub fn get_device(&self, identifier: Identifier) -> Option<&Device> { + self.devices.get(&identifier) + } +} \ No newline at end of file diff --git a/domo_node/src/app.rs b/domo_node/src/app/mod.rs similarity index 56% rename from domo_node/src/app.rs rename to domo_node/src/app/mod.rs index 0aea84b..45e2eca 100644 --- a/domo_node/src/app.rs +++ b/domo_node/src/app/mod.rs @@ -1,20 +1,21 @@ use std::io; -use log::{error, trace, warn}; -use tokio::sync::mpsc::channel; +use log::{error, info, trace, warn}; use domo_proto::commands::node_management::NodeManagementCommand; use domo_proto::identifier::Identifier; -use domo_proto::packet::{Packet, ToPacket}; +use domo_proto::packet::{ToPacket}; use domo_proto::packet::packet_data::PacketData; -use domo_proto::packet::raw_packet::RawPacket; use crate::{CONFIG, prelude}; +use crate::app::master::Devices; use crate::config::node::NodeType; use crate::connection::server::Server; +pub mod master; + impl NodeType { pub async fn start(self) -> io::Result<()> { match self { NodeType::Master { bind } => { - let server = Server::new( + let mut server = Server::new( bind, Some( Identifier::from( @@ -24,24 +25,50 @@ impl NodeType { ), ).await?; + info!("Started server at {}", server.sock().local_addr()?); + + let mut devices = Devices::new(); + { - let p = NodeManagementCommand::Ping.to_packet( + std::fs::write("./register_packet", Into::>::into(NodeManagementCommand::RegisterNode { + device_id: Identifier::from(0x000000AA) + }.to_packet( Identifier::random(), server.device_id(), Identifier::random(), Identifier::default(), - ); - std::fs::write("./packet", Into::>::into(p))?; + )))?; + std::fs::write("./ping_packet", Into::>::into(NodeManagementCommand::Ping.to_packet( + Identifier::from(0x000000AA), + server.device_id(), + Identifier::random(), + Identifier::default(), + )))?; } loop { match server.recv_packet().await { (Ok(p), Some(s)) => { - if p.dest != server.device_id() { - if let Some(d) = server.devices().get(&p.dest) { - let _ = &server.send(d.socket_addr, p).await; + if devices.get_device(p.src).is_none() { + if let PacketData::NodeManagement(NodeManagementCommand::RegisterNode { device_id }) = p.data { + let device_id = devices.add_device_auto(s, Some(device_id)); + let _ = server.send(s, NodeManagementCommand::RegisterNode { + device_id + }.to_packet( + server.device_id(), + device_id, + Identifier::random(), + p.packet_id + )).await; + } else { + warn!("{s} is unregistered and tried to send a packet {p:#?}."); + let _ = server.send(s, prelude::quick_err::errc_not_registered( + server.device_id(), + p.src, + p.packet_id + )).await; + continue; } - continue; } match p.data { PacketData::NodeManagement(NodeManagementCommand::Ping) => { @@ -53,7 +80,7 @@ impl NodeType { p.packet_id, )).await; } - _ => {} + _ => warn!("dropped packet") } } (Err(e), Some(source)) => { diff --git a/domo_node/src/connection/server.rs b/domo_node/src/connection/server.rs index 19943e4..8e22a2e 100644 --- a/domo_node/src/connection/server.rs +++ b/domo_node/src/connection/server.rs @@ -5,20 +5,15 @@ use std::net::SocketAddr; use std::io; use domo_proto::packet::{FULL_PACKET_SIZE, Packet}; use std::io::ErrorKind; -use log::error; +use log::{error, trace}; +use domo_proto::data_types::DataType; use domo_proto::packet::raw_packet::RawPacket; -pub struct Device { - /// Device identifier - pub device_id: Identifier, - /// Device socket address - pub socket_addr: SocketAddr, -} + pub struct Server { sock: UdpSocket, device_id: Identifier, - devices: HashMap } impl Server { @@ -26,7 +21,6 @@ impl Server { Ok(Self { sock: UdpSocket::bind(socket_addr).await?, device_id: device_id.unwrap_or(Identifier::random()), - devices: HashMap::new() }) } @@ -38,6 +32,8 @@ impl Server { Err(e) => return (Err(e), None) }; + trace!("received: {} bytes from {}", size, socket_addr); + ( Packet::try_from(buf[..size].to_vec()), Some(socket_addr) @@ -59,8 +55,4 @@ impl Server { pub fn sock(&self) -> &UdpSocket { &self.sock } - - pub fn devices(&self) -> &HashMap { - &self.devices - } } diff --git a/domo_node/src/prelude.rs b/domo_node/src/prelude.rs index 1cafefb..8f7b283 100644 --- a/domo_node/src/prelude.rs +++ b/domo_node/src/prelude.rs @@ -39,4 +39,16 @@ pub mod quick_err { reply_to ) } + + pub fn errc_not_registered(src: Identifier, dest: Identifier, reply_to: Identifier) -> Packet { + NodeManagementCommand::Error { + error_code: NodeManagementError::errc_not_registered.into(), + metadata: "this node is not registered".to_string().into_bytes() + }.to_packet( + src, + dest, + Identifier::random(), + reply_to + ) + } } \ No newline at end of file diff --git a/domo_node/test.py b/domo_node/test.py new file mode 100644 index 0000000..9e1c148 --- /dev/null +++ b/domo_node/test.py @@ -0,0 +1,36 @@ +import socket +import sys + +# Create a UDP socket +udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) + +def send_file_contents(filename): + try: + print(f"reading {filename}...") + with open(filename, 'rb') as file: + file_contents = file.read() + except FileNotFoundError: + print(f"File '{filename}' not found.") + return + + print(f"sending content len: {len(file_contents)}...") + # Send the file contents to the UDP socket + udp_socket.sendto(file_contents, ('0.0.0.0', 4480)) + + print("waiting for response...") + # Receive and display the response + response, server_address = udp_socket.recvfrom(1024) + print(f"Received response from {server_address}:") + print(response.hex()) + +if __name__ == "__main__": + try: + while True: + filename = input("Enter a file name (or 'exit' to quit): ") + if filename == 'exit': + break + send_file_contents(filename) + except KeyboardInterrupt: + print("\nExiting the program.") + finally: + udp_socket.close() diff --git a/domo_proto/src/commands/node_management/vec.rs b/domo_proto/src/commands/node_management/vec.rs index 7283d80..610a0d4 100644 --- a/domo_proto/src/commands/node_management/vec.rs +++ b/domo_proto/src/commands/node_management/vec.rs @@ -5,7 +5,9 @@ impl Into> for NodeManagementCommand { fn into(self) -> Vec { match self { NodeManagementCommand::Ping => vec![], - NodeManagementCommand::RegisterNode { device_id } => device_id.bytes.to_vec(), + NodeManagementCommand::RegisterNode { device_id } => { + vec![device_id.bytes[0], device_id.bytes[1], device_id.bytes[2], device_id.bytes[3]] + }, NodeManagementCommand::RemoveNode => vec![], NodeManagementCommand::RegisterProperty { property_name, data_type, read_only } => { let mut vec = vec![]; diff --git a/domo_proto/src/packet/packet_data.rs b/domo_proto/src/packet/packet_data.rs index 0eef305..0daa807 100644 --- a/domo_proto/src/packet/packet_data.rs +++ b/domo_proto/src/packet/packet_data.rs @@ -31,7 +31,9 @@ impl PacketData { impl TryFrom for PacketData { type Error = io::Error; fn try_from(raw_packet: RawPacket) -> io::Result { - match raw_packet.command { + match { + raw_packet.command >> 4 + } { 0x0 => Ok(PacketData::NodeManagement(NodeManagementCommand::try_from(raw_packet)?)), 0x1 => Ok(PacketData::PropertyControl(PropertyControlCommand::try_from(raw_packet)?)), _ => Err(io::Error::new(io::ErrorKind::InvalidInput, "command group is unsupported")) diff --git a/domo_proto/src/packet/raw_packet/packet.rs b/domo_proto/src/packet/raw_packet/packet.rs index efd380a..c3fcfeb 100644 --- a/domo_proto/src/packet/raw_packet/packet.rs +++ b/domo_proto/src/packet/raw_packet/packet.rs @@ -40,6 +40,10 @@ impl Into for RawPacket { reply_to: Identifier::from(self.reply_to), command: self.command, data: PacketData::try_from(self.clone()) + .map_err(|e| { + println!("{}", e); + e + }) .unwrap_or(PacketData::Raw(self.data)), } } diff --git a/domo_proto/src/packet/raw_packet/vec.rs b/domo_proto/src/packet/raw_packet/vec.rs index 610aba8..c85125b 100644 --- a/domo_proto/src/packet/raw_packet/vec.rs +++ b/domo_proto/src/packet/raw_packet/vec.rs @@ -10,10 +10,11 @@ impl TryFrom> for RawPacket { return Err(io::Error::new(io::ErrorKind::InvalidData, "Can't parse data into RawPacket")) } let data_length = (((data[0x12] as u16) << 8) | data[0x13] as u16) as usize; + println!("LENGTH {data_length}"); if data.len() < 0x14 + data_length { return Err(io::Error::new(io::ErrorKind::InvalidData, "Can't parse data into RawPacket")) } - let checksum = as_u32_be(data[(data.len() - 4)..].as_ref()); + let checksum = as_u32_be(data[(0x14 + data_length)..].as_ref()); let built_checksum = crc32fast::hash(data[..(data.len() - 4)].as_ref()); if checksum != built_checksum { return Err(io::Error::new(io::ErrorKind::InvalidData, format!("Checksum does not match {checksum:X?} != {built_checksum:X?}"))) diff --git a/recv_server.py b/recv_server.py deleted file mode 100644 index 6a59c55..0000000 --- a/recv_server.py +++ /dev/null @@ -1,45 +0,0 @@ - -import socket, zlib - -HOST = "127.0.0.1" # Standard loopback interface address (localhost) -PORT = 2000 # Port to listen on (non-privileged ports are > 1023) - -def calculate_crc(data): - # Calculate the CRC32 checksum of the data - crc = zlib.crc32(data) - # Convert the CRC32 value to a 4-byte big-endian byte array - crc_bytes = crc.to_bytes(4, byteorder='big') - return crc_bytes - -with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as s: - s.bind((HOST, PORT)) - s.listen() - while True: - try: - conn, addr = s.accept() - with conn: - print(f"Connected by {addr}") - while True: - data = conn.recv(1024) - if not data: - break - print(f'RECV: {data.hex()} [{len(data)}]') - if data[0x11] == 0x01: - pckt = bytearray([0x01]) - pckt.extend([0x00, 0x00, 0x00, 0x00]) # src - pckt.extend([0x00, 0x00, 0x00, 0xFF]) # dest - pckt.extend([0x00, 0x00, 0x00, 0x00]) # packet_id - pckt.extend(data[0x09:0x0D]) # reply_to - pckt.extend([0x01]) # command - pckt.extend([0x00, 0x04]) # data_length - pckt.extend([0x00, 0x00, 0x00, 0xFF]) - - # Calculate CRC for the received data - crc = calculate_crc(pckt) - # Append the CRC value to the data - data_with_crc = pckt + crc - # Send the modified data (with CRC) back to the client - conn.sendall(data_with_crc) - print(f'SEND: {data_with_crc.hex()} [{len(data_with_crc)}]') - except: - s.close() \ No newline at end of file