Skip to main content

sig_net/
parse.rs

1use crate::*;
2use crate::util::hex_char;
3
4pub struct PacketReader<'a> {
5    buffer: &'a [u8],
6    position: u16,
7}
8
9impl<'a> PacketReader<'a> {
10    pub fn new(buffer: &'a [u8], size: u16) -> Self {
11        let size = (size as usize).min(buffer.len());
12        PacketReader {
13            buffer: &buffer[..size],
14            position: 0,
15        }
16    }
17
18    pub fn position(&self) -> u16 {
19        self.position
20    }
21
22    pub fn remaining(&self) -> u16 {
23        (self.buffer.len() as u16) - self.position
24    }
25
26    pub fn can_read(&self, bytes: u16) -> bool {
27        (self.position + bytes) <= self.buffer.len() as u16
28    }
29
30    pub fn read_byte(&mut self) -> Result<u8> {
31        if !self.can_read(1) {
32            return Err(SigNetError::BufferTooSmall);
33        }
34        let val = self.buffer[self.position as usize];
35        self.position += 1;
36        Ok(val)
37    }
38
39    pub fn read_u16(&mut self) -> Result<u16> {
40        if !self.can_read(2) {
41            return Err(SigNetError::BufferTooSmall);
42        }
43        let val = u16::from_be_bytes([
44            self.buffer[self.position as usize],
45            self.buffer[(self.position + 1) as usize],
46        ]);
47        self.position += 2;
48        Ok(val)
49    }
50
51    pub fn read_u32(&mut self) -> Result<u32> {
52        if !self.can_read(4) {
53            return Err(SigNetError::BufferTooSmall);
54        }
55        let val = u32::from_be_bytes([
56            self.buffer[self.position as usize],
57            self.buffer[(self.position + 1) as usize],
58            self.buffer[(self.position + 2) as usize],
59            self.buffer[(self.position + 3) as usize],
60        ]);
61        self.position += 4;
62        Ok(val)
63    }
64
65    pub fn read_bytes(&mut self, dest: &mut [u8]) -> Result<()> {
66        let count = dest.len() as u16;
67        if !self.can_read(count) {
68            return Err(SigNetError::BufferTooSmall);
69        }
70        let start = self.position as usize;
71        dest.copy_from_slice(&self.buffer[start..start + count as usize]);
72        self.position += count;
73        Ok(())
74    }
75
76    pub fn skip(&mut self, count: u16) -> Result<()> {
77        if !self.can_read(count) {
78            return Err(SigNetError::BufferTooSmall);
79        }
80        self.position += count;
81        Ok(())
82    }
83
84    pub fn peek_byte(&self) -> Result<u8> {
85        if !self.can_read(1) {
86            return Err(SigNetError::BufferTooSmall);
87        }
88        Ok(self.buffer[self.position as usize])
89    }
90
91    pub fn current_ptr(&self) -> &'a [u8] {
92        &self.buffer[self.position as usize..]
93    }
94
95    pub fn parse_coap_header(&mut self) -> Result<CoAPHeader> {
96        if !self.can_read(CoAPHeader::SIZE as u16) {
97            return Err(SigNetError::BufferTooSmall);
98        }
99        let mut bytes = [0u8; 4];
100        bytes.copy_from_slice(&self.buffer[self.position as usize..self.position as usize + 4]);
101        self.position += 4;
102        Ok(CoAPHeader::from_bytes(&bytes))
103    }
104
105    pub fn skip_token(&mut self, token_length: u8) -> Result<()> {
106        self.skip(token_length as u16)
107    }
108
109    /// Parse a single CoAP option. Returns (delta, length, value_slice).
110    pub fn parse_coap_option(&mut self) -> Result<(u16, u16, &'a [u8])> {
111        let nibble = self.read_byte()?;
112        let delta_nib = nibble >> 4;
113        let len_nib = nibble & 0x0F;
114
115        let delta = match delta_nib {
116            0..=12 => delta_nib as u16,
117            13 => COAP_OPTION_EXT8_BASE + self.read_byte()? as u16,
118            14 => COAP_OPTION_EXT16_BASE + self.read_u16()?,
119            _ => return Err(SigNetError::InvalidOption),
120        };
121
122        let length = match len_nib {
123            0..=12 => len_nib as u16,
124            13 => COAP_OPTION_EXT8_BASE + self.read_byte()? as u16,
125            14 => COAP_OPTION_EXT16_BASE + self.read_u16()?,
126            _ => return Err(SigNetError::InvalidOption),
127        };
128
129        if !self.can_read(length) {
130            return Err(SigNetError::BufferTooSmall);
131        }
132        let start = self.position as usize;
133        self.skip(length)?;
134        let value = &self.buffer[start..start + length as usize];
135
136        Ok((delta, length, value))
137    }
138
139    /// Parse all 6 SigNet options (2076–2236) from the current position.
140    /// Stops at the payload marker (0xFF) — all options including HMAC must precede it.
141    /// Leaves the reader positioned at the first byte of the payload.
142    pub fn parse_signet_options(&mut self) -> Result<SigNetOptions> {
143        let mut options = SigNetOptions::default();
144        let mut prev_option: u16 = 0;
145        let mut seen_hmac = false;
146
147        loop {
148            if !self.can_read(1) {
149                break;
150            }
151            let peek = self.peek_byte()?;
152            if peek == COAP_PAYLOAD_MARKER {
153                self.read_byte()?; // consume marker; reader now sits at payload start
154                break;
155            }
156
157            let (delta, _len, value) = self.parse_coap_option()?;
158            let opt_num = prev_option + delta;
159            prev_option = opt_num;
160
161            if Self::apply_option(&mut options, opt_num, value) {
162                seen_hmac = true;
163            }
164        }
165
166        if !seen_hmac {
167            return Err(SigNetError::InvalidOption);
168        }
169
170        Ok(options)
171    }
172
173    fn apply_option(options: &mut SigNetOptions, opt_num: u16, value: &[u8]) -> bool {
174        match opt_num {
175            SIGNET_OPTION_SECURITY_MODE => {
176                if !value.is_empty() {
177                    options.security_mode = value[0];
178                }
179                false
180            }
181            SIGNET_OPTION_SENDER_ID => {
182                let len = value.len().min(SENDER_ID_LENGTH);
183                options.sender_id[..len].copy_from_slice(&value[..len]);
184                false
185            }
186            SIGNET_OPTION_MFG_CODE => {
187                if value.len() >= 2 {
188                    options.mfg_code = u16::from_be_bytes([value[0], value[1]]);
189                }
190                false
191            }
192            SIGNET_OPTION_SESSION_ID => {
193                if value.len() >= 4 {
194                    options.session_id =
195                        u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
196                }
197                false
198            }
199            SIGNET_OPTION_SEQ_NUM => {
200                if value.len() >= 4 {
201                    options.seq_num =
202                        u32::from_be_bytes([value[0], value[1], value[2], value[3]]);
203                }
204                false
205            }
206            SIGNET_OPTION_HMAC => {
207                let len = value.len().min(HMAC_SHA256_LENGTH);
208                options.hmac[..len].copy_from_slice(&value[..len]);
209                true
210            }
211            _ => false,
212        }
213    }
214
215    pub fn parse_tlv_block(&mut self) -> Result<TLVBlock<'a>> {
216        let type_id = self.read_u16()?;
217        let length = self.read_u16()?;
218        let start = self.position as usize;
219        self.skip(length)?;
220        Ok(TLVBlock {
221            type_id,
222            value: &self.buffer[start..start + length as usize],
223        })
224    }
225
226    pub fn extract_uri_string(&mut self, uri_string: &mut [u8]) -> Result<usize> {
227        let mut prev_option: u16 = 0;
228        let mut pos = 0;
229
230        loop {
231            if !self.can_read(1) {
232                break;
233            }
234            let peek = self.peek_byte()?;
235            if peek == COAP_PAYLOAD_MARKER || peek == 0 {
236                break;
237            }
238
239            let (delta, _len, value) = self.parse_coap_option()?;
240            let opt_num = prev_option + delta;
241            prev_option = opt_num;
242
243            if opt_num == COAP_OPTION_URI_PATH {
244                if pos < uri_string.len() {
245                    uri_string[pos] = b'/';
246                    pos += 1;
247                }
248                if pos + value.len() <= uri_string.len() {
249                    uri_string[pos..pos + value.len()].copy_from_slice(value);
250                    pos += value.len();
251                } else {
252                    return Err(SigNetError::BufferTooSmall);
253                }
254            }
255        }
256
257        Ok(pos)
258    }
259}
260
261pub fn parse_tid_level(tlv: &TLVBlock, dmx_data: &mut [u8]) -> Result<u16> {
262    if tlv.type_id != TID_LEVEL {
263        return Err(SigNetError::InvalidArgument);
264    }
265    if tlv.value.len() > MAX_DMX_SLOTS as usize {
266        return Err(SigNetError::InvalidPacket);
267    }
268    dmx_data[..tlv.value.len()].copy_from_slice(tlv.value);
269    Ok(tlv.value.len() as u16)
270}
271
272/// Parse TID_TIMECODE (0x0202, Length=5) → (hours, minutes, seconds, frames, tc_type)
273pub fn parse_tid_timecode(tlv: &TLVBlock) -> Result<(u8, u8, u8, u8, u8)> {
274    if tlv.type_id != TID_TIMECODE {
275        return Err(SigNetError::InvalidArgument);
276    }
277    if tlv.value.len() != 5 {
278        return Err(SigNetError::InvalidPacket);
279    }
280    Ok((tlv.value[0], tlv.value[1], tlv.value[2], tlv.value[3], tlv.value[4]))
281}
282
283/// Parse TID_UNIVERSE (0x0203, Length=7) → (universe, command, multicast_ip)
284pub fn parse_tid_universe(tlv: &TLVBlock) -> Result<(u16, u8, [u8; 4])> {
285    if tlv.type_id != TID_UNIVERSE {
286        return Err(SigNetError::InvalidArgument);
287    }
288    if tlv.value.len() != 7 {
289        return Err(SigNetError::InvalidPacket);
290    }
291    let universe = u16::from_be_bytes([tlv.value[0], tlv.value[1]]);
292    let command = tlv.value[2];
293    let mut ip = [0u8; 4];
294    ip.copy_from_slice(&tlv.value[3..7]);
295    Ok((universe, command, ip))
296}
297
298pub fn parse_hex_bytes(text: &[u8], out_bytes: &mut [u8], byte_count: u16) -> Result<()> {
299    let text = if text.starts_with(b"0x") || text.starts_with(b"0X") {
300        &text[2..]
301    } else {
302        text
303    };
304    // stack buffer: max byte_count is 32 (K0) → 64 hex chars; 70 gives headroom
305    let mut stack_buf = [0u8; 70];
306    let mut stack_len = 0usize;
307    for &b in text {
308        if !b.is_ascii_whitespace() {
309            if stack_len >= stack_buf.len() {
310                return Err(SigNetError::InvalidArgument);
311            }
312            stack_buf[stack_len] = b;
313            stack_len += 1;
314        }
315    }
316    let text = &stack_buf[..stack_len];
317    let expected = byte_count as usize * 2;
318    if text.len() != expected {
319        return Err(SigNetError::InvalidArgument);
320    }
321    for i in 0..byte_count as usize {
322        out_bytes[i] = (hex_char(text[i * 2])? << 4) | hex_char(text[i * 2 + 1])?;
323    }
324    Ok(())
325}