calamp_rs/
options_header.rs

1//
2// Copyright (c) 2021 Murilo Ijanc' <mbsd@m0x.ru>
3//
4// Permission to use, copy, modify, and distribute this software for any
5// purpose with or without fee is hereby granted, provided that the above
6// copyright notice and this permission notice appear in all copies.
7//
8// THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
9// WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
10// WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
11// ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
12// OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
13//
14
15use std::fmt;
16
17use nom::bits::{bits, streaming};
18use nom::error::{Error, ErrorKind};
19use nom::number::streaming::be_u8;
20use nom::IResult;
21#[cfg(feature = "serde")]
22use serde::{Deserialize, Serialize};
23
24const OPTIONS_HEADER: u8 = 0x83;
25
26#[derive(Clone, PartialEq, Eq)]
27#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
28pub enum MobileIDType {
29    Off,
30
31    /// Electronic Serial Number (ESN) of the LMU
32    Esn,
33
34    /// International Mobile Equipment Identifier (IMEI) or Electronic
35    /// Identifier (EID) of the wireless modem
36    Equipment,
37
38    /// International Mobile Subscriber Identifier (IMSI) of the SIM card
39    /// (GSM/GPRS devices only)
40    Subscriber,
41
42    /// User Defined Mobile ID
43    Defined,
44
45    /// Phone Number ofkk the mobile (if available)
46    PhoneNumber,
47
48    /// The current IP Address of the LMU
49    IpAddress,
50
51    /// CDMA Mobile Equipment ID (MEID) or International Mobile Equipment
52    /// Identifier (IMEI) of the wireless modem
53    Cdma,
54}
55
56impl MobileIDType {
57    pub fn parse(input: &[u8]) -> IResult<&[u8], MobileIDType> {
58        let (i, _): (&[u8], u8) = be_u8::<_, (_, ErrorKind)>(input).unwrap();
59        let (i, b): (&[u8], u8) = be_u8::<_, (_, ErrorKind)>(i).unwrap();
60
61        match b {
62            0 => Ok((i, MobileIDType::Off)),
63            1 => Ok((i, MobileIDType::Esn)),
64            2 => Ok((i, MobileIDType::Equipment)),
65            3 => Ok((i, MobileIDType::Subscriber)),
66            4 => Ok((i, MobileIDType::Defined)),
67            5 => Ok((i, MobileIDType::PhoneNumber)),
68            6 => Ok((i, MobileIDType::IpAddress)),
69            7 => Ok((i, MobileIDType::Cdma)),
70            _ => panic!("not found"),
71        }
72    }
73}
74
75impl fmt::Display for MobileIDType {
76    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
77        match *self {
78            MobileIDType::Off => write!(f, "MobileIDType::Off"),
79            MobileIDType::Esn => write!(f, "MobileIDType::Esn"),
80            MobileIDType::Equipment => write!(f, "MobileIDType::Equipament"),
81            MobileIDType::Subscriber => write!(f, "MobileIDType::Subscriber"),
82            MobileIDType::Defined => write!(f, "MobileIDType::Defined"),
83            MobileIDType::PhoneNumber => write!(f, "MobileIDType::PhoneNumber"),
84            MobileIDType::IpAddress => write!(f, "MobileIDType::IpAddress"),
85            MobileIDType::Cdma => write!(f, "MobileIDType::Cdma"),
86        }
87    }
88}
89
90impl fmt::Debug for MobileIDType {
91    fn fmt(&self, f: &mut fmt::Formatter<'_>) -> fmt::Result {
92        match *self {
93            MobileIDType::Off => write!(f, "MobileIDType::Off"),
94            MobileIDType::Esn => write!(f, "MobileIDType::Esn"),
95            MobileIDType::Equipment => write!(f, "MobileIDType::Equipament"),
96            MobileIDType::Subscriber => write!(f, "MobileIDType::Subscriber"),
97            MobileIDType::Defined => write!(f, "MobileIDType::Defined"),
98            MobileIDType::PhoneNumber => write!(f, "MobileIDType::PhoneNumber"),
99            MobileIDType::IpAddress => write!(f, "MobileIDType::IpAddress"),
100            MobileIDType::Cdma => write!(f, "MobileIDType::Cdma"),
101        }
102    }
103}
104
105#[derive(Debug)]
106#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
107pub struct MobileID(pub String);
108
109impl MobileID {
110    pub fn len(&self) -> usize {
111        self.0.len()
112    }
113
114    pub fn parse(input: &[u8]) -> IResult<&[u8], Self> {
115        let (i, a): (&[u8], u8) = be_u8::<_, (_, ErrorKind)>(input).unwrap();
116        let (i, b): (&[u8], &[u8]) = nom::bytes::streaming::take(a)(i)?;
117        let mut id = String::from("");
118        for d in b.iter() {
119            id.push_str(&format!("{0:2x}", d))
120        }
121        Ok((i, Self(id)))
122    }
123
124    pub fn is_empty(&self) -> bool {
125        self.len() == 0
126    }
127}
128
129#[derive(Debug)]
130#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
131pub struct OptionsStatus {
132    /// MobileID is set
133    pub is_mobile_id: bool,
134
135    /// MobileIdType is set
136    pub is_mobile_id_type: bool,
137
138    /// Authentication World is set
139    pub is_authentication_world: bool,
140
141    /// Routing is set
142    pub is_routing: bool,
143
144    /// Forwarding is set
145    pub is_forwarding: bool,
146
147    /// Response redirections is set
148    pub is_response_redirection: bool,
149
150    /// Options extension is set
151    pub is_options_extension: bool,
152
153    /// Options headers exist
154    pub is_always_set: bool,
155}
156
157impl OptionsStatus {
158    pub fn is_mobile_id(&self) -> bool {
159        self.is_mobile_id
160    }
161
162    pub fn is_mobile_id_type(&self) -> bool {
163        self.is_mobile_id_type
164    }
165
166    pub fn is_authentication_world(&self) -> bool {
167        self.is_authentication_world
168    }
169
170    pub fn is_routing(&self) -> bool {
171        self.is_routing
172    }
173
174    pub fn is_forwarding(&self) -> bool {
175        self.is_forwarding
176    }
177
178    pub fn is_response_redirection(&self) -> bool {
179        self.is_response_redirection
180    }
181
182    pub fn is_options_extension(&self) -> bool {
183        self.is_options_extension
184    }
185
186    pub fn is_always_set(&self) -> bool {
187        self.is_always_set
188    }
189}
190
191fn is_options_header(input: u8) -> bool {
192    input == OPTIONS_HEADER
193}
194
195#[derive(Debug)]
196#[cfg_attr(feature = "serde", derive(Serialize, Deserialize))]
197pub struct OptionsHeader {
198    /// Mobile id
199    pub mobile_id: Option<MobileID>,
200
201    /// Mobile id type
202    pub mobile_id_type: Option<MobileIDType>,
203
204    /// Authentication World
205    pub authentication_world: Option<bool>,
206
207    /// Routing
208    pub routing: Option<bool>,
209
210    /// Forwarding
211    pub forwarding: Option<bool>,
212
213    /// Response Redirection
214    pub response_redirection: Option<bool>,
215
216    /// Options Extension
217    pub options_extension: Option<bool>,
218}
219
220impl OptionsHeader {
221    pub fn parse(input: &[u8]) -> IResult<&[u8], Option<Self>> {
222        // FIX: return error
223        if !is_options_header(input[0]) {
224            panic!("");
225        }
226
227        let (i, opt_status) = parse_options_status(input)?;
228        let mut opt_header = OptionsHeader {
229            mobile_id: None,
230            mobile_id_type: None,
231            authentication_world: None,
232            routing: None,
233            forwarding: None,
234            response_redirection: None,
235            options_extension: None,
236        };
237        let mut inp: &[u8] = &[];
238
239        // check mobile id
240        if opt_status.is_mobile_id() {
241            let (i, mob_id) = MobileID::parse(i)?;
242            opt_header.mobile_id = Some(mob_id);
243            inp = i;
244        }
245
246        // check mobile id type
247        if opt_status.is_mobile_id_type() {
248            let (i, mob_id_tp) = MobileIDType::parse(inp)?;
249            opt_header.mobile_id_type = Some(mob_id_tp);
250            inp = i;
251        }
252
253        if opt_status.is_authentication_world() {
254            unimplemented!()
255        }
256        if opt_status.is_routing() {
257            unimplemented!()
258        }
259
260        if opt_status.is_forwarding() {
261            unimplemented!()
262        }
263
264        if opt_status.is_response_redirection() {
265            unimplemented!()
266        }
267
268        if opt_status.is_options_extension() {
269            unimplemented!()
270        }
271
272        Ok((inp, Some(opt_header)))
273    }
274}
275
276fn parse_options_status(input: &[u8]) -> IResult<&[u8], OptionsStatus> {
277    #[allow(clippy::type_complexity)]
278    let (i, b): (&[u8], (u8, u8, u8, u8, u8, u8, u8, u8)) =
279        bits::<_, _, Error<(&[u8], usize)>, _, _>(nom::sequence::tuple((
280            streaming::take(1u8),
281            streaming::take(1u8),
282            streaming::take(1u8),
283            streaming::take(1u8),
284            streaming::take(1u8),
285            streaming::take(1u8),
286            streaming::take(1u8),
287            streaming::take(1u8),
288        )))(input)?;
289    Ok((
290        i,
291        OptionsStatus {
292            is_mobile_id: b.7 == 1,
293            is_mobile_id_type: b.6 == 1,
294            is_authentication_world: b.5 == 1,
295            is_routing: b.4 == 1,
296            is_forwarding: b.3 == 1,
297            is_response_redirection: b.2 == 1,
298            is_options_extension: b.1 == 1,
299            is_always_set: b.0 == 1,
300        },
301    ))
302}
303
304#[cfg(test)]
305mod tests {
306    use super::{MobileIDType, OptionsHeader};
307
308    #[test]
309    fn test_parse_options_headers() {
310        let data: [u8; 117] = [
311            0x83, 0x05, 0x46, 0x34, 0x66, 0x32, 0x35, 0x01, 0x01, 0x01, 0x02,
312            0x3a, 0x86, 0x5f, 0xf1, 0x3a, 0x54, 0x5f, 0xf1, 0x3a, 0x57, 0xf1,
313            0xe2, 0x85, 0x78, 0xe4, 0x22, 0xd6, 0x40, 0x00, 0x01, 0x36, 0xf8,
314            0x00, 0x00, 0x00, 0x0b, 0x00, 0x00, 0x06, 0x20, 0x00, 0x00, 0xff,
315            0x8d, 0x02, 0x1e, 0x1e, 0x00, 0x7b, 0x21, 0x10, 0x00, 0x00, 0x00,
316            0x31, 0xe0, 0x00, 0x00, 0x10, 0x1a, 0x00, 0x00, 0x00, 0x00, 0x00,
317            0x22, 0x2a, 0x32, 0x00, 0x00, 0x03, 0xf1, 0x00, 0x00, 0x00, 0x00,
318            0x00, 0x00, 0x00, 0x00, 0x01, 0xc8, 0x2d, 0x3f, 0x01, 0xc8, 0x2d,
319            0x3f, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
320            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x40, 0x01, 0x00,
321            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
322        ];
323
324        let (i, opt_header) = OptionsHeader::parse(&data).unwrap();
325        match opt_header {
326            Some(opt_h) => {
327                assert_eq!(i.len(), 108);
328
329                if let Some(mob_id) = opt_h.mobile_id {
330                    assert_eq!(mob_id.len(), 10);
331                    assert_eq!(mob_id.0, String::from("4634663235"));
332                }
333
334                if let Some(mob_id_tp) = opt_h.mobile_id_type {
335                    assert_eq!(mob_id_tp, MobileIDType::Esn);
336                    assert_eq!(
337                        format!("{}", mob_id_tp),
338                        String::from("MobileIDType::Esn")
339                    );
340                }
341            }
342            None => (),
343        }
344    }
345}