// Commands from user space use num_derive::FromPrimitive; use num_traits::FromPrimitive; #[repr(u8)] #[derive(Clone, Copy, FromPrimitive)] #[rustfmt::skip] pub enum CommandType { Shutdown = 0, Verdict = 1, UpdateV4 = 2, UpdateV6 = 3, ClearCache = 4, GetLogs = 5, GetBandwidthStats = 6, PrintMemoryStats = 7, CleanEndedConnections = 8, } #[repr(C, packed)] pub struct Command { pub command_type: CommandType, value: [u8; 0], } #[repr(C, packed)] #[derive(Debug, PartialEq, Eq)] pub struct Verdict { pub id: u64, pub verdict: u8, } #[repr(C, packed)] #[derive(Debug, PartialEq, Eq)] pub struct UpdateV4 { pub protocol: u8, pub local_address: [u8; 4], pub local_port: u16, pub remote_address: [u8; 4], pub remote_port: u16, pub verdict: u8, } #[repr(C, packed)] #[derive(Debug, PartialEq, Eq)] pub struct UpdateV6 { pub protocol: u8, pub local_address: [u8; 16], pub local_port: u16, pub remote_address: [u8; 16], pub remote_port: u16, pub verdict: u8, } pub fn parse_type(bytes: &[u8]) -> Option { FromPrimitive::from_u8(bytes[0]) } pub fn parse_verdict(bytes: &[u8]) -> &Verdict { as_type(bytes) } pub fn parse_update_v4(bytes: &[u8]) -> &UpdateV4 { as_type(bytes) } pub fn parse_update_v6(bytes: &[u8]) -> &UpdateV6 { as_type(bytes) } fn as_type(bytes: &[u8]) -> &T { let ptr: *const u8 = &bytes[0]; let t_ptr: *const T = ptr as _; unsafe { t_ptr.as_ref().unwrap() } } #[cfg(test)] use std::fs::File; #[cfg(test)] use std::io::Read; #[cfg(test)] use std::mem::size_of; #[cfg(test)] use std::panic; #[test] fn test_go_command_file() { let mut file = File::open("testdata/go_command_test.bin").unwrap(); loop { let mut command: [u8; 1] = [0]; let bytes_count = file.read(&mut command).unwrap(); if bytes_count == 0 { return; } if let Some(command) = parse_type(&command) { match command { CommandType::Shutdown => {} CommandType::Verdict => { let mut buf = [0; size_of::()]; let bytes_count = file.read(&mut buf).unwrap(); if bytes_count != size_of::() { panic!("unexpected bytes count") } assert_eq!(parse_verdict(&buf), &Verdict { id: 1, verdict: 2 }) } CommandType::UpdateV4 => { let mut buf = [0; size_of::()]; let bytes_count = file.read(&mut buf).unwrap(); if bytes_count != size_of::() { panic!("unexpected bytes count") } assert_eq!( parse_update_v4(&buf), &UpdateV4 { protocol: 1, local_address: [1, 2, 3, 4], local_port: 2, remote_address: [2, 3, 4, 5], remote_port: 3, verdict: 4 } ) } CommandType::UpdateV6 => { let mut buf = [0; size_of::()]; let bytes_count = file.read(&mut buf).unwrap(); if bytes_count != size_of::() { panic!("unexpected bytes count") } assert_eq!( parse_update_v6(&buf), &UpdateV6 { protocol: 1, local_address: [1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16], local_port: 2, remote_address: [ 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 17 ], remote_port: 3, verdict: 4 } ) } CommandType::ClearCache => {} CommandType::GetLogs => {} CommandType::GetBandwidthStats => {} CommandType::PrintMemoryStats => {} CommandType::CleanEndedConnections => {} } } else { panic!("Unknown command: {}", command[0]); } } }