1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
use super::*;
use crate::tx_ack::Data;
use std::convert::TryFrom;

const PROTOCOL_VERSION_INDEX: usize = 0;
const IDENTIFIER_INDEX: usize = 3;
const PACKET_PAYLOAD_START: usize = 8;

fn random_token(buffer: &[u8]) -> u16 {
    (buffer[1] as u16) << 8 | buffer[2] as u16
}

pub fn gateway_mac(buffer: &[u8]) -> MacAddress {
    MacAddress::new(
        buffer[0], buffer[1], buffer[2], buffer[3], buffer[4], buffer[5], buffer[6], buffer[7],
    )
}

pub trait Parser {
    fn parse(buffer: &[u8]) -> std::result::Result<Packet, ParseError>;
}

impl Packet {
    pub fn parse_uplink(buffer: &[u8]) -> std::result::Result<Up, ParseError> {
        match Self::parse(buffer)? {
            Packet::Up(up) => Ok(up),
            Packet::Down(down) => Err(ParseError::UnexpectedDownlink(down)),
        }
    }
    pub fn parse_downlink(buffer: &[u8]) -> std::result::Result<Down, ParseError> {
        match Self::parse(buffer)? {
            Packet::Down(down) => Ok(down),
            Packet::Up(up) => Err(ParseError::UnexpectedUplink(Box::new(up))),
        }
    }
}

impl Parser for Packet {
    fn parse(buffer: &[u8]) -> std::result::Result<Packet, ParseError> {
        if buffer[PROTOCOL_VERSION_INDEX] != PROTOCOL_VERSION {
            return Err(ParseError::InvalidProtocolVersion);
        };

        match Identifier::try_from(buffer[IDENTIFIER_INDEX]) {
            Err(_) => Err(ParseError::InvalidIdentifier),
            Ok(id) => {
                let random_token = random_token(buffer);
                let buffer = &buffer[4..];
                Ok(match id {
                    // up packets
                    Identifier::PullData => {
                        let gateway_mac = gateway_mac(&buffer[..PACKET_PAYLOAD_START]);
                        pull_data::Packet {
                            random_token,
                            gateway_mac,
                        }
                        .into()
                    }
                    Identifier::PushData => {
                        let gateway_mac = gateway_mac(&buffer[..PACKET_PAYLOAD_START]);
                        let json_str =
                            std::str::from_utf8(&buffer[PACKET_PAYLOAD_START..terminate(buffer)])?;
                        let data = serde_json::from_str(json_str).map_err(|json_error| {
                            ParseError::InvalidJson {
                                identifier: id,
                                json_str: json_str.into(),
                                json_error,
                            }
                        })?;
                        push_data::Packet {
                            random_token,
                            gateway_mac,
                            data,
                        }
                        .into()
                    }
                    Identifier::TxAck => {
                        let gateway_mac = gateway_mac(&buffer[..PACKET_PAYLOAD_START]);
                        let data = if buffer.len() > PACKET_PAYLOAD_START {
                            // guard against some packet forwarders that put a 0 byte as the last byte
                            if buffer.len() == PACKET_PAYLOAD_START + 1
                                && buffer[PACKET_PAYLOAD_START] == 0
                            {
                                Data::default()
                            } else {
                                let json_str = std::str::from_utf8(
                                    &buffer[PACKET_PAYLOAD_START..terminate(buffer)],
                                )?;
                                serde_json::from_str(json_str).map_err(|json_error| {
                                    ParseError::InvalidJson {
                                        identifier: id,
                                        json_str: json_str.into(),
                                        json_error,
                                    }
                                })?
                            }
                        } else {
                            Data::default()
                        };
                        tx_ack::Packet {
                            random_token,
                            gateway_mac,
                            data,
                        }
                        .into()
                    }
                    // down packets
                    Identifier::PushAck => push_ack::Packet { random_token }.into(),
                    Identifier::PullAck => pull_ack::Packet { random_token }.into(),
                    Identifier::PullResp => {
                        let json_str = std::str::from_utf8(&buffer[..terminate(buffer)])?;
                        let data = serde_json::from_str(json_str).map_err(|json_error| {
                            ParseError::InvalidJson {
                                identifier: id,
                                json_str: json_str.into(),
                                json_error,
                            }
                        })?;
                        pull_resp::Packet { random_token, data }.into()
                    }
                })
            }
        }
    }
}

// deals with null byte terminated json
fn terminate(buf: &[u8]) -> usize {
    if buf[buf.len() - 1] == 0 {
        buf.len() - 1
    } else {
        buf.len()
    }
}