Skip to main content

sig_net/
types.rs

1use core::fmt;
2
3use crate::*;
4use crate::util::hex_char;
5
6pub type Result<T> = core::result::Result<T, SigNetError>;
7
8pub type SoemCode = u32;
9
10/// Собрать SoemCode из ESTA MfgID и ProductVariantID.
11pub fn soem_code(mfg_id: u16, product_variant_id: u16) -> SoemCode {
12    ((mfg_id as u32) << 16) | (product_variant_id as u32)
13}
14pub fn soem_code_mfg(sc: SoemCode) -> u16 { (sc >> 16) as u16 }
15pub fn soem_code_variant(sc: SoemCode) -> u16 { sc as u16 }
16
17#[derive(Debug, Clone, Copy, PartialEq, Eq)]
18pub enum SigNetError {
19    InvalidArgument,
20    BufferFull,
21    Crypto,
22    Encode,
23    Network,
24    BufferTooSmall,
25    InvalidPacket,
26    InvalidOption,
27    HmacFailed,
28    TestFailure,
29    PassphraseTooShort,
30    PassphraseTooLong,
31    PassphraseInsufficientClasses,
32    PassphraseConsecutiveIdentical,
33    PassphraseConsecutiveSequential,
34    SessionIdOverflow,
35}
36
37impl fmt::Display for SigNetError {
38    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
39        match self {
40            SigNetError::InvalidArgument => write!(f, "invalid argument"),
41            SigNetError::BufferFull => write!(f, "buffer full"),
42            SigNetError::Crypto => write!(f, "cryptographic operation failed"),
43            SigNetError::Encode => write!(f, "encoding error"),
44            SigNetError::Network => write!(f, "network error"),
45            SigNetError::BufferTooSmall => write!(f, "buffer too small"),
46            SigNetError::InvalidPacket => write!(f, "invalid packet"),
47            SigNetError::InvalidOption => write!(f, "invalid option"),
48            SigNetError::HmacFailed => write!(f, "HMAC verification failed"),
49            SigNetError::TestFailure => write!(f, "self-test failed"),
50            SigNetError::PassphraseTooShort => write!(f, "passphrase too short"),
51            SigNetError::PassphraseTooLong => write!(f, "passphrase too long"),
52            SigNetError::PassphraseInsufficientClasses => write!(f, "insufficient character classes"),
53            SigNetError::PassphraseConsecutiveIdentical => write!(f, "consecutive identical characters"),
54            SigNetError::PassphraseConsecutiveSequential => write!(f, "consecutive sequential characters"),
55            SigNetError::SessionIdOverflow => write!(f, "session ID overflow, manual intervention required"),
56        }
57    }
58}
59
60impl std::error::Error for SigNetError {}
61
62#[derive(Debug, Clone, Copy, PartialEq, Eq)]
63pub struct CoAPHeader {
64    pub version: u8,
65    pub type_: u8,
66    pub token_length: u8,
67    pub code: u8,
68    pub message_id: u16,
69}
70
71impl CoAPHeader {
72    pub const SIZE: usize = 4;
73
74    pub fn from_bytes(bytes: &[u8; 4]) -> Self {
75        CoAPHeader {
76            version: bytes[0] >> 6,
77            type_: (bytes[0] >> 4) & 0x03,
78            token_length: bytes[0] & 0x0F,
79            code: bytes[1],
80            message_id: u16::from_be_bytes([bytes[2], bytes[3]]),
81        }
82    }
83
84    pub fn to_bytes(&self) -> [u8; 4] {
85        let vtt = ((self.version & 0x03) << 6) | ((self.type_ & 0x03) << 4) | (self.token_length & 0x0F);
86        let mid = self.message_id.to_be_bytes();
87        [vtt, self.code, mid[0], mid[1]]
88    }
89
90    pub fn new(message_id: u16) -> Self {
91        CoAPHeader {
92            version: COAP_VERSION,
93            type_: COAP_TYPE_NON,
94            token_length: 0,
95            code: COAP_CODE_POST,
96            message_id,
97        }
98    }
99}
100
101#[derive(Debug, Clone, Copy)]
102pub struct TLVBlock<'a> {
103    pub type_id: u16,
104    pub value: &'a [u8],
105}
106
107impl<'a> TLVBlock<'a> {
108    pub fn length(&self) -> u16 {
109        self.value.len() as u16
110    }
111}
112
113#[derive(Debug, Clone, Default)]
114pub struct SigNetOptions {
115    pub security_mode: u8,
116    pub sender_id: [u8; SENDER_ID_LENGTH],
117    pub mfg_code: u16,
118    pub session_id: u32,
119    pub seq_num: u32,
120    pub hmac: [u8; HMAC_SHA256_LENGTH],
121}
122
123#[derive(Debug, Clone)]
124pub struct PacketBuffer {
125    buffer: [u8; MAX_UDP_PAYLOAD as usize],
126    position: u16,
127}
128
129impl PacketBuffer {
130    pub fn new() -> Self {
131        PacketBuffer {
132            buffer: [0u8; MAX_UDP_PAYLOAD as usize],
133            position: 0,
134        }
135    }
136
137    pub fn reset(&mut self) {
138        self.position = 0;
139        self.buffer.fill(0);
140    }
141
142    pub fn position(&self) -> u16 {
143        self.position
144    }
145
146    pub fn len(&self) -> u16 {
147        self.position
148    }
149
150    pub fn is_empty(&self) -> bool {
151        self.position == 0
152    }
153
154    pub fn as_slice(&self) -> &[u8] {
155        &self.buffer[..self.position as usize]
156    }
157
158    pub fn as_mut_slice(&mut self) -> &mut [u8] {
159        &mut self.buffer[..self.position as usize]
160    }
161
162    pub fn remaining(&self) -> u16 {
163        MAX_UDP_PAYLOAD - self.position
164    }
165
166    pub fn has_space(&self, size: u16) -> bool {
167        (self.position + size) <= MAX_UDP_PAYLOAD
168    }
169
170    pub fn write_byte(&mut self, value: u8) -> Result<()> {
171        if !self.has_space(1) {
172            return Err(SigNetError::BufferFull);
173        }
174        self.buffer[self.position as usize] = value;
175        self.position += 1;
176        Ok(())
177    }
178
179    pub fn write_bytes(&mut self, data: &[u8]) -> Result<()> {
180        let len = data.len() as u16;
181        if !self.has_space(len) {
182            return Err(SigNetError::BufferFull);
183        }
184        let pos = self.position as usize;
185        self.buffer[pos..pos + data.len()].copy_from_slice(data);
186        self.position += len;
187        Ok(())
188    }
189
190    pub fn write_u16(&mut self, value: u16) -> Result<()> {
191        if !self.has_space(2) {
192            return Err(SigNetError::BufferFull);
193        }
194        let pos = self.position as usize;
195        self.buffer[pos..pos + 2].copy_from_slice(&value.to_be_bytes());
196        self.position += 2;
197        Ok(())
198    }
199
200    pub fn write_u32(&mut self, value: u32) -> Result<()> {
201        if !self.has_space(4) {
202            return Err(SigNetError::BufferFull);
203        }
204        let pos = self.position as usize;
205        self.buffer[pos..pos + 4].copy_from_slice(&value.to_be_bytes());
206        self.position += 4;
207        Ok(())
208    }
209
210    pub fn seek(&mut self, position: u16) -> Result<()> {
211        if position > MAX_UDP_PAYLOAD {
212            return Err(SigNetError::InvalidArgument);
213        }
214        self.position = position;
215        Ok(())
216    }
217
218    pub fn as_raw(&self) -> &[u8; MAX_UDP_PAYLOAD as usize] {
219        &self.buffer
220    }
221
222    pub fn as_raw_mut(&mut self) -> &mut [u8; MAX_UDP_PAYLOAD as usize] {
223        &mut self.buffer
224    }
225}
226
227impl Default for PacketBuffer {
228    fn default() -> Self {
229        Self::new()
230    }
231}
232
233#[derive(Debug, Clone, Default)]
234pub struct ReceiverSenderState {
235    pub sender_id: [u8; SENDER_ID_LENGTH],
236    pub session_id: u32,
237    pub seq_num: u32,
238    pub last_packet_time_ms: u32,
239    pub total_packets_received: u32,
240    pub total_packets_accepted: u32,
241}
242
243#[derive(Debug, Clone, Default)]
244pub struct ReceiverStatistics {
245    pub total_packets: u32,
246    pub accepted_packets: u32,
247    pub coap_version_errors: u32,
248    pub coap_type_errors: u32,
249    pub coap_code_errors: u32,
250    pub uri_mismatches: u32,
251    pub missing_options: u32,
252    pub hmac_failures: u32,
253    pub replay_detected: u32,
254    pub parse_errors: u32,
255    pub last_packet_time_ms: u32,
256}
257
258#[derive(Debug, Clone, Default)]
259pub struct ReceivedPacketInfo {
260    pub message_id: u16,
261    pub sender_tuid: [u8; TUID_LENGTH],
262    pub endpoint: u16,
263    pub soem_code: SoemCode,
264    pub session_id: u32,
265    pub seq_num: u32,
266    pub dmx_slot_count: u16,
267    pub hmac_valid: bool,
268    pub session_fresh: bool,
269    pub rejection_reason: Option<&'static str>,
270    pub timestamp_ms: u32,
271}
272
273#[derive(Debug, Clone, Copy, PartialEq, Eq)]
274pub struct TUID(pub [u8; TUID_LENGTH]);
275
276impl TUID {
277    pub fn from_bytes(bytes: &[u8]) -> Result<Self> {
278        if bytes.len() != TUID_LENGTH {
279            return Err(SigNetError::InvalidArgument);
280        }
281        let mut arr = [0u8; TUID_LENGTH];
282        arr.copy_from_slice(bytes);
283        Ok(TUID(arr))
284    }
285
286    pub fn as_bytes(&self) -> &[u8] {
287        &self.0
288    }
289
290    /// Uppercase hex для URI (§8.2 нормативное требование).
291    pub fn to_hex_upper(&self) -> [u8; TUID_HEX_LENGTH] {
292        const HEX_CHARS: &[u8; 16] = b"0123456789ABCDEF";
293        let mut out = [0u8; TUID_HEX_LENGTH];
294        for i in 0..TUID_LENGTH {
295            out[i * 2] = HEX_CHARS[(self.0[i] >> 4) as usize];
296            out[i * 2 + 1] = HEX_CHARS[(self.0[i] & 0x0F) as usize];
297        }
298        out
299    }
300
301    /// Uppercase hex для отображения на экране (§6.6).
302    pub fn to_hex_display(&self) -> [u8; TUID_HEX_LENGTH] {
303        const HEX_CHARS: &[u8; 16] = b"0123456789ABCDEF";
304        let mut out = [0u8; TUID_HEX_LENGTH];
305        for i in 0..TUID_LENGTH {
306            out[i * 2] = HEX_CHARS[(self.0[i] >> 4) as usize];
307            out[i * 2 + 1] = HEX_CHARS[(self.0[i] & 0x0F) as usize];
308        }
309        out
310    }
311
312    #[deprecated(since = "0.18.0", note = "use to_hex_display() or to_hex_upper()")]
313    pub fn to_hex(&self) -> [u8; TUID_HEX_LENGTH] {
314        self.to_hex_display()
315    }
316
317    pub fn from_hex(hex: &[u8]) -> Result<Self> {
318        if hex.len() != TUID_HEX_LENGTH {
319            return Err(SigNetError::InvalidArgument);
320        }
321        let mut arr = [0u8; TUID_LENGTH];
322        for i in 0..TUID_LENGTH {
323            arr[i] = (hex_char(hex[i * 2])? << 4) | hex_char(hex[i * 2 + 1])?;
324        }
325        Ok(TUID(arr))
326    }
327}
328
329pub fn increment_sequence(current_seq: u32) -> u32 {
330    if current_seq == 0xFFFFFFFF {
331        1
332    } else {
333        current_seq + 1
334    }
335}
336
337pub fn should_increment_session(seq_num: u32) -> bool {
338    seq_num == 0xFFFFFFFF
339}
340
341pub fn calculate_multicast_address(universe: u16) -> Result<[u8; 4]> {
342    if !(MIN_UNIVERSE..=MAX_UNIVERSE).contains(&universe) {
343        return Err(SigNetError::InvalidArgument);
344    }
345    let index = ((universe - 1) % 100) + 1;
346    Ok([MULTICAST_BASE_OCTET_0, MULTICAST_BASE_OCTET_1, MULTICAST_BASE_OCTET_2, index as u8])
347}