]> code.octet-stream.net Git - m17rt/blob - m17core/src/reflector.rs
Reflector packet definitions
[m17rt] / m17core / src / reflector.rs
1 // Based on https://github.com/n7tae/mrefd/blob/master/Packet-Description.md
2 // and the main M17 specification
3
4 use crate::protocol::LsfFrame;
5
6 macro_rules! impl_stream_id {
7 ($t:ty, $from:tt) => {
8 impl $t {
9 pub fn stream_id(&self) -> u16 {
10 u16::from_be_bytes([self.0[$from], self.0[$from + 1]])
11 }
12 }
13 };
14 }
15
16 macro_rules! impl_link_setup {
17 ($t:ty, $from:tt) => {
18 impl $t {
19 pub fn link_setup_frame(&self) -> LsfFrame {
20 let mut frame = LsfFrame([0; 30]);
21 frame.0[0..28].copy_from_slice(&self.0[$from..($from + 28)]);
22 frame.recalculate_crc();
23 frame
24 }
25 }
26 };
27 }
28
29 macro_rules! impl_link_setup_frame {
30 ($t:ty, $from:tt) => {
31 impl $t {
32 pub fn link_setup_frame(&self) -> LsfFrame {
33 let mut frame = LsfFrame([0; 30]);
34 frame.0[..].copy_from_slice(&self.0[$from..($from + 30)]);
35 frame
36 }
37 }
38 };
39 }
40
41 macro_rules! impl_frame_number {
42 ($t:ty, $from:tt) => {
43 impl $t {
44 pub fn frame_number(&self) -> u16 {
45 let frame_num = u16::from_be_bytes([self.0[$from], self.0[$from + 1]]);
46 frame_num & 0x7fff
47 }
48
49 pub fn is_end_of_stream(&self) -> bool {
50 let frame_num = u16::from_be_bytes([self.0[$from], self.0[$from + 1]]);
51 (frame_num & 0x8000) > 0
52 }
53 }
54 };
55 }
56
57 macro_rules! impl_payload {
58 ($t:ty, $from:tt, $to:tt) => {
59 impl $t {
60 pub fn payload(&self) -> &[u8] {
61 &self.0[$from..$to]
62 }
63 }
64 };
65 }
66
67 macro_rules! impl_modules {
68 ($t:ty, $from:tt, $to:tt) => {
69 impl $t {
70 pub fn modules(&self) -> ModulesIterator {
71 ModulesIterator::new(&self.0[$from..$to])
72 }
73 }
74 };
75 }
76
77 macro_rules! impl_module {
78 ($t:ty, $at:tt) => {
79 impl $t {
80 pub fn module(&self) -> char {
81 self.0[$at] as char
82 }
83 }
84 };
85 }
86
87 macro_rules! impl_address {
88 ($t:ty, $from:tt) => {
89 impl $t {
90 pub fn address(&self) -> crate::address::Address {
91 crate::address::decode_address(self.0[$from..($from + 6)].try_into().unwrap())
92 }
93 }
94 };
95 }
96
97 macro_rules! impl_trailing_crc_verify {
98 ($t:ty) => {
99 impl $t {
100 pub fn verify_integrity(&self) -> bool {
101 crate::crc::m17_crc(&self.0) == 0
102 }
103 }
104 };
105 }
106
107 macro_rules! impl_internal_crc {
108 ($t:ty, $from:tt, $to:tt) => {
109 impl $t {
110 pub fn verify_integrity(&self) -> bool {
111 crate::crc::m17_crc(&self.0[$from..$to]) == 0
112 }
113 }
114 };
115 }
116
117 macro_rules! impl_is_relayed {
118 ($t:ty) => {
119 impl $t {
120 pub fn is_relayed(&self) -> bool {
121 self.0[self.0.len() - 1] != 0
122 }
123 }
124 };
125 }
126
127 pub struct ModulesIterator<'a> {
128 modules: &'a [u8],
129 idx: usize,
130 }
131
132 impl<'a> ModulesIterator<'a> {
133 fn new(modules: &'a [u8]) -> Self {
134 Self { modules, idx: 0 }
135 }
136 }
137
138 impl Iterator for ModulesIterator<'_> {
139 type Item = char;
140
141 fn next(&mut self) -> Option<Self::Item> {
142 if self.modules[self.idx] == 0 {
143 return None;
144 }
145 if self.idx < self.modules.len() {
146 self.idx += 1;
147 return Some(self.modules[self.idx - 1] as char);
148 }
149 None
150 }
151 }
152
153 pub const MAGIC_VOICE: &[u8] = b"M17 ";
154 pub const MAGIC_VOICE_HEADER: &[u8] = b"M17H";
155 pub const MAGIC_VOICE_DATA: &[u8] = b"M17D";
156 pub const MAGIC_PACKET: &[u8] = b"M17P";
157 pub const MAGIC_ACKNOWLEDGE: &[u8] = b"ACKN";
158 pub const MAGIC_CONNECT: &[u8] = b"CONN";
159 pub const MAGIC_DISCONNECT: &[u8] = b"DISC";
160 pub const MAGIC_LISTEN: &[u8] = b"LSTN";
161 pub const MAGIC_NACK: &[u8] = b"NACK";
162 pub const MAGIC_PING: &[u8] = b"PING";
163 pub const MAGIC_PONG: &[u8] = b"PONG";
164
165 /// Messages sent from a station/client to a reflector
166 #[allow(clippy::large_enum_variant)]
167 pub enum ClientMessage {
168 VoiceFull(VoiceFull),
169 VoiceHeader(VoiceHeader),
170 VoiceData(VoiceData),
171 Packet(Packet),
172 Pong(Pong),
173 Connect(Connect),
174 Listen(Listen),
175 Disconnect(Disconnect),
176 }
177
178 /// Messages sent from a reflector to a station/client
179 #[allow(clippy::large_enum_variant)]
180 pub enum ServerMessage {
181 VoiceFull(VoiceFull),
182 VoiceHeader(VoiceHeader),
183 VoiceData(VoiceData),
184 Packet(Packet),
185 Ping(Ping),
186 DisconnectAcknowledge(DisconnectAcknowledge),
187 ForceDisconnect(ForceDisconnect),
188 ConnectAcknowledge(ConnectAcknowledge),
189 ConnectNack(ConnectNack),
190 }
191
192 /// Messages sent and received between reflectors
193 #[allow(clippy::large_enum_variant)]
194 pub enum InterlinkMessage {
195 VoiceInterlink(VoiceInterlink),
196 VoiceHeaderInterlink(VoiceHeaderInterlink),
197 VoiceDataInterlink(VoiceDataInterlink),
198 PacketInterlink(PacketInterlink),
199 Ping(Ping),
200 ConnectInterlink(ConnectInterlink),
201 ConnectInterlinkAcknowledge(ConnectInterlinkAcknowledge),
202 ConnectNack(ConnectNack),
203 DisconnectInterlink(DisconnectInterlink),
204 }
205
206 pub struct VoiceFull(pub [u8; 54]);
207 impl_stream_id!(VoiceFull, 4);
208 impl_link_setup!(VoiceFull, 6);
209 impl_frame_number!(VoiceFull, 34);
210 impl_payload!(VoiceFull, 36, 52);
211 impl_trailing_crc_verify!(VoiceFull);
212
213 pub struct VoiceHeader(pub [u8; 36]);
214 impl_stream_id!(VoiceHeader, 4);
215 impl_link_setup!(VoiceHeader, 6);
216 impl_trailing_crc_verify!(VoiceHeader);
217
218 pub struct VoiceData(pub [u8; 26]);
219 impl_stream_id!(VoiceData, 4);
220 impl_frame_number!(VoiceData, 6);
221 impl_payload!(VoiceData, 8, 24);
222 impl_trailing_crc_verify!(VoiceData);
223
224 pub struct Packet(pub [u8; 859]);
225 impl_link_setup_frame!(Packet, 4);
226
227 impl Packet {
228 pub fn payload(&self) -> &[u8] {
229 &self.0[34..]
230 }
231
232 pub fn verify_integrity(&self) -> bool {
233 self.link_setup_frame().check_crc() == 0
234 && self.payload().len() >= 4
235 && crate::crc::m17_crc(self.payload()) == 0
236 }
237 }
238
239 pub struct Pong(pub [u8; 10]);
240 impl_address!(Pong, 4);
241
242 pub struct Connect(pub [u8; 11]);
243 impl_address!(Connect, 4);
244 impl_module!(Connect, 10);
245
246 pub struct Listen(pub [u8; 11]);
247 impl_address!(Listen, 4);
248 impl_module!(Listen, 10);
249
250 pub struct Disconnect(pub [u8; 10]);
251 impl_address!(Disconnect, 4);
252
253 pub struct Ping(pub [u8; 10]);
254 impl_address!(Ping, 4);
255
256 pub struct DisconnectAcknowledge(pub [u8; 4]);
257
258 pub struct ForceDisconnect(pub [u8; 10]);
259 impl_address!(ForceDisconnect, 4);
260
261 pub struct ConnectAcknowledge(pub [u8; 4]);
262
263 pub struct ConnectNack(pub [u8; 4]);
264
265 pub struct VoiceInterlink(pub [u8; 55]);
266 impl_stream_id!(VoiceInterlink, 4);
267 impl_link_setup!(VoiceInterlink, 6);
268 impl_frame_number!(VoiceInterlink, 34);
269 impl_payload!(VoiceInterlink, 36, 52);
270 impl_internal_crc!(VoiceInterlink, 0, 54);
271 impl_is_relayed!(VoiceInterlink);
272
273 pub struct VoiceHeaderInterlink(pub [u8; 37]);
274 impl_stream_id!(VoiceHeaderInterlink, 4);
275 impl_link_setup!(VoiceHeaderInterlink, 6);
276 impl_internal_crc!(VoiceHeaderInterlink, 0, 36);
277 impl_is_relayed!(VoiceHeaderInterlink);
278
279 pub struct VoiceDataInterlink(pub [u8; 27]);
280 impl_stream_id!(VoiceDataInterlink, 4);
281 impl_frame_number!(VoiceDataInterlink, 6);
282 impl_payload!(VoiceDataInterlink, 8, 24);
283 impl_internal_crc!(VoiceDataInterlink, 0, 24);
284 impl_is_relayed!(VoiceDataInterlink);
285
286 pub struct PacketInterlink(pub [u8; 860]);
287 impl_link_setup_frame!(PacketInterlink, 4);
288 impl_is_relayed!(PacketInterlink);
289
290 impl PacketInterlink {
291 pub fn payload(&self) -> &[u8] {
292 &self.0[34..(self.0.len() - 1)]
293 }
294
295 pub fn verify_integrity(&self) -> bool {
296 self.link_setup_frame().check_crc() == 0
297 && self.payload().len() >= 4
298 && crate::crc::m17_crc(self.payload()) == 0
299 }
300 }
301
302 pub struct ConnectInterlink(pub [u8; 37]);
303 impl_address!(ConnectInterlink, 4);
304 impl_modules!(ConnectInterlink, 10, 37);
305
306 pub struct ConnectInterlinkAcknowledge(pub [u8; 37]);
307 impl_address!(ConnectInterlinkAcknowledge, 4);
308 impl_modules!(ConnectInterlinkAcknowledge, 10, 37);
309
310 pub struct DisconnectInterlink(pub [u8; 10]);
311 impl_address!(DisconnectInterlink, 4);