1 // Copyright (C) 2020 Jordan Petridis <jordan@centricular.com>
2 //
3 // This library is free software; you can redistribute it and/or
4 // modify it under the terms of the GNU Library General Public
5 // License as published by the Free Software Foundation; either
6 // version 2 of the License, or (at your option) any later version.
7 //
8 // This library is distributed in the hope that it will be useful,
9 // but WITHOUT ANY WARRANTY; without even the implied warranty of
10 // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
11 // Library General Public License for more details.
12 //
13 // You should have received a copy of the GNU Library General Public
14 // License along with this library; if not, write to the
15 // Free Software Foundation, Inc., 51 Franklin Street, Suite 500,
16 // Boston, MA 02110-1335, USA.
17 
18 use byteorder::{BigEndian, WriteBytesExt};
19 use nom::{
20     self, bytes::complete::take, combinator::map_res, multi::many0, number::complete::be_u16,
21     number::complete::be_u32, number::complete::be_u8, sequence::tuple, IResult,
22 };
23 use std::borrow::Cow;
24 use std::io::{self, Write};
25 
26 const CRC: crc::Crc<u32> = crc::Crc::<u32>::new(&crc::CRC_32_BZIP2);
27 
28 #[derive(Debug)]
29 struct Prelude {
30     total_bytes: u32,
31     header_bytes: u32,
32     prelude_crc: u32,
33 }
34 
35 #[derive(Debug)]
36 pub struct Header {
37     pub name: Cow<'static, str>,
38     pub value_type: u8,
39     pub value: Cow<'static, str>,
40 }
41 
42 #[derive(Debug)]
43 pub struct Packet<'a> {
44     prelude: Prelude,
45     headers: Vec<Header>,
46     pub payload: &'a [u8],
47     msg_crc: u32,
48 }
49 
write_header<W: Write>(w: &mut W, header: &Header) -> Result<(), io::Error>50 fn write_header<W: Write>(w: &mut W, header: &Header) -> Result<(), io::Error> {
51     w.write_u8(header.name.len() as u8)?;
52     w.write_all(header.name.as_bytes())?;
53     w.write_u8(header.value_type)?;
54     w.write_u16::<BigEndian>(header.value.len() as u16)?;
55     w.write_all(header.value.as_bytes())?;
56     Ok(())
57 }
58 
write_headers<W: Write>(w: &mut W, headers: &[Header]) -> Result<(), io::Error>59 fn write_headers<W: Write>(w: &mut W, headers: &[Header]) -> Result<(), io::Error> {
60     for header in headers {
61         write_header(w, header)?;
62     }
63     Ok(())
64 }
65 
encode_packet(payload: &[u8], headers: &[Header]) -> Result<Vec<u8>, io::Error>66 pub fn encode_packet(payload: &[u8], headers: &[Header]) -> Result<Vec<u8>, io::Error> {
67     let mut res = Vec::with_capacity(1024);
68 
69     // Total length
70     res.write_u32::<BigEndian>(0)?;
71     // Header length
72     res.write_u32::<BigEndian>(0)?;
73     // Prelude CRC32 placeholder
74     res.write_u32::<BigEndian>(0)?;
75 
76     // Write all headers
77     write_headers(&mut res, headers)?;
78 
79     // Rewrite header length
80     let header_length = res.len() - 12;
81     (&mut res[4..8]).write_u32::<BigEndian>(header_length as u32)?;
82 
83     // Write payload
84     res.write_all(payload)?;
85 
86     // Rewrite total length
87     let total_length = res.len() + 4;
88     (&mut res[0..4]).write_u32::<BigEndian>(total_length as u32)?;
89 
90     // Rewrite the prelude crc since we replaced the lengths
91     let prelude_crc = CRC.checksum(&res[0..8]);
92     (&mut res[8..12]).write_u32::<BigEndian>(prelude_crc)?;
93 
94     // Message CRC
95     let message_crc = CRC.checksum(&res);
96     res.write_u32::<BigEndian>(message_crc)?;
97 
98     Ok(res)
99 }
100 
parse_prelude(input: &[u8]) -> IResult<&[u8], Prelude>101 fn parse_prelude(input: &[u8]) -> IResult<&[u8], Prelude> {
102     map_res(
103         tuple((be_u32, be_u32, be_u32)),
104         |(total_bytes, header_bytes, prelude_crc)| {
105             let sum = CRC.checksum(&input[0..8]);
106             if prelude_crc != sum {
107                 return Err(nom::Err::Error((
108                     "Prelude CRC doesn't match",
109                     nom::error::ErrorKind::MapRes,
110                 )));
111             }
112 
113             Ok(Prelude {
114                 total_bytes,
115                 header_bytes,
116                 prelude_crc,
117             })
118         },
119     )(input)
120 }
121 
parse_header(input: &[u8]) -> IResult<&[u8], Header>122 fn parse_header(input: &[u8]) -> IResult<&[u8], Header> {
123     let (input, header_length) = be_u8(input)?;
124     let (input, name) = map_res(take(header_length), std::str::from_utf8)(input)?;
125     let (input, value_type) = be_u8(input)?;
126     let (input, value_length) = be_u16(input)?;
127     let (input, value) = map_res(take(value_length), std::str::from_utf8)(input)?;
128 
129     let header = Header {
130         name: name.to_string().into(),
131         value_type,
132         value: value.to_string().into(),
133     };
134 
135     Ok((input, header))
136 }
137 
packet_is_exception(packet: &Packet) -> bool138 pub fn packet_is_exception(packet: &Packet) -> bool {
139     for header in &packet.headers {
140         if header.name == ":message-type" && header.value == "exception" {
141             return true;
142         }
143     }
144 
145     false
146 }
147 
parse_packet(input: &[u8]) -> IResult<&[u8], Packet>148 pub fn parse_packet(input: &[u8]) -> IResult<&[u8], Packet> {
149     let (remainder, prelude) = parse_prelude(input)?;
150 
151     // Check the crc of the whole input
152     let sum = CRC.checksum(&input[..input.len() - 4]);
153     let (_, msg_crc) = be_u32(&input[input.len() - 4..])?;
154 
155     if msg_crc != sum {
156         return Err(nom::Err::Error(nom::error::Error::new(
157             b"Prelude CRC doesn't match",
158             nom::error::ErrorKind::MapRes,
159         )));
160     }
161 
162     let (remainder, header_input) = take(prelude.header_bytes)(remainder)?;
163     let (_, headers) = many0(parse_header)(header_input)?;
164 
165     let payload_length = prelude.total_bytes - prelude.header_bytes - 4 - 12;
166     let (remainder, payload) = take(payload_length)(remainder)?;
167 
168     // only the message_crc we check before should be remaining now
169     assert_eq!(remainder.len(), 4);
170 
171     Ok((
172         input,
173         Packet {
174             prelude,
175             headers,
176             payload,
177             msg_crc,
178         },
179     ))
180 }
181