1use tokio::io::{AsyncRead, AsyncReadExt, AsyncWrite, AsyncWriteExt};
11
12use crate::error::{Result, SyncwebError};
13
14use super::xdr::{Reader, push_bool, push_bytes, push_u16, push_u32};
15
16pub const PROTOCOL_NAME: &[u8] = b"bep-relay";
18pub const MAGIC: u32 = 0x9E79_BC40;
20pub const MAX_MESSAGE_LENGTH: usize = 1024;
22pub const MAX_DATA_FRAME: usize = 8192;
24pub const MAX_BYTE_FIELD: usize = 32;
26
27const TYPE_PING: u8 = 0;
28const TYPE_PONG: u8 = 1;
29const TYPE_JOIN_RELAY_REQUEST: u8 = 2;
30const TYPE_JOIN_SESSION_REQUEST: u8 = 3;
31const TYPE_RESPONSE: u8 = 4;
32const TYPE_CONNECT_REQUEST: u8 = 5;
33const TYPE_SESSION_INVITATION: u8 = 6;
34const TYPE_RELAY_FULL: u8 = 7;
35
36#[derive(Clone, Debug, Eq, PartialEq)]
38#[non_exhaustive]
39pub enum Message {
40 Ping,
42 Pong,
44 JoinRelayRequest { token: String },
46 JoinSessionRequest { key: Vec<u8> },
48 Response { code: i32, message: String },
50 ConnectRequest { id: Vec<u8> },
52 SessionInvitation {
56 from: Vec<u8>,
57 key: Vec<u8>,
58 address: Vec<u8>,
59 port: u16,
60 server_socket: bool,
61 },
62 RelayFull,
64}
65
66impl Message {
67 pub const RESPONSE_SUCCESS: i32 = 0;
69 pub const RESPONSE_NOT_FOUND: i32 = 1;
71 pub const RESPONSE_ALREADY_CONNECTED: i32 = 2;
73 pub const RESPONSE_WRONG_TOKEN: i32 = 3;
75 pub const RESPONSE_UNEXPECTED: i32 = 100;
77
78 const fn type_id(&self) -> u8 {
79 match self {
80 Self::Ping => TYPE_PING,
81 Self::Pong => TYPE_PONG,
82 Self::JoinRelayRequest { .. } => TYPE_JOIN_RELAY_REQUEST,
83 Self::JoinSessionRequest { .. } => TYPE_JOIN_SESSION_REQUEST,
84 Self::Response { .. } => TYPE_RESPONSE,
85 Self::ConnectRequest { .. } => TYPE_CONNECT_REQUEST,
86 Self::SessionInvitation { .. } => TYPE_SESSION_INVITATION,
87 Self::RelayFull => TYPE_RELAY_FULL,
88 }
89 }
90
91 pub fn encode_frame(&self) -> Result<Vec<u8>> {
97 let mut payload = Vec::new();
98 match self {
99 Self::Ping | Self::Pong | Self::RelayFull => {}
100 Self::JoinRelayRequest { token } => {
101 push_bytes(&mut payload, token.as_bytes()).map_err(decode_error)?;
102 }
103 Self::JoinSessionRequest { key } => {
104 push_bytes(&mut payload, key).map_err(decode_error)?;
105 }
106 Self::Response { code, message } => {
107 push_u32(&mut payload, u32::from_le_bytes(code.to_le_bytes()));
108 push_bytes(&mut payload, message.as_bytes()).map_err(decode_error)?;
109 }
110 Self::ConnectRequest { id } => {
111 push_bytes(&mut payload, id).map_err(decode_error)?;
112 }
113 Self::SessionInvitation {
114 from,
115 key,
116 address,
117 port,
118 server_socket,
119 } => {
120 push_bytes(&mut payload, from).map_err(decode_error)?;
121 push_bytes(&mut payload, key).map_err(decode_error)?;
122 push_bytes(&mut payload, address).map_err(decode_error)?;
123 push_u16(&mut payload, *port);
124 push_bool(&mut payload, *server_socket);
125 }
126 }
127 if payload.len() > MAX_MESSAGE_LENGTH {
128 return Err(SyncwebError::RelayFrameTooLarge {
129 max: MAX_MESSAGE_LENGTH,
130 });
131 }
132 let mut frame = Vec::with_capacity(payload.len().saturating_add(12));
133 push_u32(&mut frame, MAGIC);
134 push_u32(&mut frame, u32::from(self.type_id()));
135 push_u32(
136 &mut frame,
137 u32::try_from(payload.len())
138 .map_err(|error| SyncwebError::RelayDecode(format!("payload length exceeds u32::MAX: {error}")))?,
139 );
140 frame.extend_from_slice(&payload);
141 Ok(frame)
142 }
143
144 pub fn decode_frame(frame: &[u8]) -> Result<Self> {
151 let (rest, payload) = frame
152 .split_at_checked(12)
153 .ok_or_else(|| SyncwebError::RelayDecode("frame is shorter than the header".to_owned()))?;
154 let header: [u8; 12] = rest
155 .try_into()
156 .map_err(|error| SyncwebError::RelayDecode(format!("frame header is not twelve bytes: {error}")))?;
157 let (magic, message_type, length_words) = read_header_words(header);
158 if magic != MAGIC {
159 return Err(SyncwebError::RelayDecode(format!("bad frame magic {magic:#x}")));
160 }
161 let length = usize::try_from(length_words)
162 .map_err(|error| SyncwebError::RelayDecode(format!("frame length exceeds usize: {error}")))?;
163 if length > MAX_MESSAGE_LENGTH {
164 return Err(SyncwebError::RelayFrameTooLarge {
165 max: MAX_MESSAGE_LENGTH,
166 });
167 }
168 if payload.len() != length {
169 return Err(SyncwebError::RelayDecode(format!(
170 "frame payload is {} bytes but the header announces {length}",
171 payload.len()
172 )));
173 }
174 Self::decode_payload(message_type, payload)
175 }
176
177 fn decode_payload(message_type: u8, payload: &[u8]) -> Result<Self> {
178 let mut reader = Reader::new(payload);
179 let message = match message_type {
180 TYPE_PING => Message::Ping,
181 TYPE_PONG => Message::Pong,
182 TYPE_RELAY_FULL => Message::RelayFull,
183 TYPE_JOIN_RELAY_REQUEST => {
184 let token = reader.take_bytes(MAX_MESSAGE_LENGTH).map_err(decode_error)?;
185 Message::JoinRelayRequest {
186 token: utf8_field(token, "token")?,
187 }
188 }
189 TYPE_JOIN_SESSION_REQUEST => Message::JoinSessionRequest {
190 key: reader.take_bytes(MAX_BYTE_FIELD).map_err(decode_error)?,
191 },
192 TYPE_RESPONSE => {
193 let code = i32::from_ne_bytes(reader.take_u32().map_err(decode_error)?.to_ne_bytes());
194 let message = reader.take_bytes(MAX_MESSAGE_LENGTH).map_err(decode_error)?;
195 Message::Response {
196 code,
197 message: utf8_field(message, "response message")?,
198 }
199 }
200 TYPE_CONNECT_REQUEST => Message::ConnectRequest {
201 id: reader.take_bytes(MAX_BYTE_FIELD).map_err(decode_error)?,
202 },
203 TYPE_SESSION_INVITATION => {
204 let from = reader.take_bytes(MAX_BYTE_FIELD).map_err(decode_error)?;
205 let key = reader.take_bytes(MAX_BYTE_FIELD).map_err(decode_error)?;
206 let address = reader.take_bytes(MAX_BYTE_FIELD).map_err(decode_error)?;
207 let port_words = reader.take_u32().map_err(decode_error)?;
208 let port_bytes = port_words.to_be_bytes();
209 let port = u16::from_be_bytes([port_bytes[2], port_bytes[3]]);
210 let server_socket = reader.take_bool().map_err(decode_error)?;
211 Message::SessionInvitation {
212 from,
213 key,
214 address,
215 port,
216 server_socket,
217 }
218 }
219 other => {
220 return Err(SyncwebError::RelayDecode(format!("unknown message type {other}")));
221 }
222 };
223 reader.finish().map_err(decode_error)?;
224 Ok(message)
225 }
226}
227
228fn decode_error(detail: impl Into<String>) -> SyncwebError {
230 SyncwebError::RelayDecode(detail.into())
231}
232
233fn utf8_field(bytes: Vec<u8>, field: &str) -> Result<String> {
234 String::from_utf8(bytes).map_err(|error| SyncwebError::RelayDecode(format!("{field} is not UTF-8: {error}")))
235}
236
237pub async fn write_message<W>(writer: &mut W, message: &Message) -> Result<()>
243where
244 W: AsyncWrite + Unpin,
245{
246 let frame = message.encode_frame()?;
247 writer.write_all(&frame).await?;
248 writer.flush().await?;
249 Ok(())
250}
251
252pub async fn read_message<R>(reader: &mut R) -> Result<Message>
258where
259 R: AsyncRead + Unpin,
260{
261 let mut header = [0_u8; 12];
262 reader.read_exact(&mut header).await?;
263 let (magic, message_type, length_words) = read_header_words(header);
264 if magic != MAGIC {
265 return Err(SyncwebError::RelayDecode(format!("bad frame magic {magic:#x}")));
266 }
267 let length = usize::try_from(length_words)
268 .map_err(|error| SyncwebError::RelayDecode(format!("frame length exceeds usize: {error}")))?;
269 if length > MAX_MESSAGE_LENGTH {
270 return Err(SyncwebError::RelayFrameTooLarge {
271 max: MAX_MESSAGE_LENGTH,
272 });
273 }
274 let mut payload = vec![0_u8; length];
275 reader.read_exact(&mut payload).await?;
276 Message::decode_payload(message_type, &payload)
277}
278
279fn read_header_words(header: [u8; 12]) -> (u32, u8, u32) {
281 let magic = u32::from_be_bytes([header[0], header[1], header[2], header[3]]);
282 let type_words = u32::from_be_bytes([header[4], header[5], header[6], header[7]]);
283 let length = u32::from_be_bytes([header[8], header[9], header[10], header[11]]);
284 let message_type = u8::try_from(type_words).unwrap_or(u8::MAX);
287 (magic, message_type, length)
288}
289
290pub async fn write_data_frame<W>(writer: &mut W, data: &[u8]) -> Result<()>
296where
297 W: AsyncWrite + Unpin,
298{
299 if data.len() > MAX_DATA_FRAME {
300 return Err(SyncwebError::RelayFrameTooLarge { max: MAX_DATA_FRAME });
301 }
302 let length = u32::try_from(data.len())
303 .map_err(|error| SyncwebError::operation("data frame length exceeds u32::MAX", error))?;
304 writer.write_all(&length.to_be_bytes()).await?;
305 writer.write_all(data).await?;
306 Ok(())
307}
308
309pub async fn read_data_frame<R>(reader: &mut R) -> Result<Vec<u8>>
318where
319 R: AsyncRead + Unpin,
320{
321 let mut length_bytes = [0_u8; 4];
322 reader.read_exact(&mut length_bytes).await?;
323 let length = usize::try_from(u32::from_be_bytes(length_bytes))
324 .map_err(|error| SyncwebError::operation("data frame length exceeds usize", error))?;
325 if length > MAX_DATA_FRAME {
326 return Err(SyncwebError::RelayFrameTooLarge { max: MAX_DATA_FRAME });
327 }
328 let mut data = vec![0_u8; length];
329 reader.read_exact(&mut data).await?;
330 Ok(data)
331}
332
333#[cfg(test)]
334mod tests {
335 use super::*;
336
337 #[test]
338 fn every_message_round_trips() {
339 let messages = [
340 Message::Ping,
341 Message::Pong,
342 Message::RelayFull,
343 Message::JoinRelayRequest {
344 token: "thunder".to_owned(),
345 },
346 Message::JoinSessionRequest { key: vec![1_u8; 32] },
347 Message::Response {
348 code: 0,
349 message: "success".to_owned(),
350 },
351 Message::ConnectRequest { id: vec![7_u8; 32] },
352 Message::SessionInvitation {
353 from: vec![9_u8; 32],
354 key: vec![3_u8; 32],
355 address: vec![10, 0, 0, 1],
356 port: 22_067,
357 server_socket: true,
358 },
359 ];
360 for message in messages {
361 let frame = message.encode_frame().expect("encode");
362 assert_eq!(Message::decode_frame(&frame).expect("decode"), message);
363 }
364 }
365
366 #[test]
367 fn session_invitation_padding_round_trips_odd_lengths() {
368 let message = Message::SessionInvitation {
369 from: vec![9_u8; 32],
370 key: vec![1_u8; 32],
371 address: vec![10, 0, 0],
372 port: 1,
373 server_socket: false,
374 };
375 let frame = message.encode_frame().expect("encode");
376 assert_eq!(Message::decode_frame(&frame).expect("decode"), message);
377 }
378
379 #[test]
380 fn bad_magic_is_rejected() {
381 let mut frame = Message::Ping.encode_frame().expect("encode");
382 if let Some(first) = frame.first_mut() {
383 *first = 0;
384 }
385 assert!(Message::decode_frame(&frame).is_err());
386 }
387
388 #[test]
389 fn truncated_frame_is_rejected() {
390 let frame = Message::Ping.encode_frame().expect("encode");
391 let truncated = frame.get(..10).unwrap_or(&frame);
392 assert!(Message::decode_frame(truncated).is_err());
393 }
394
395 #[test]
396 fn trailing_bytes_are_rejected() {
397 let mut frame = Message::Ping.encode_frame().expect("encode");
398 frame.push(0);
399 assert!(Message::decode_frame(&frame).is_err());
400 }
401
402 #[tokio::test]
403 async fn tokio_framing_round_trips() {
404 let mut buffer = Vec::new();
405 for message in [
406 Message::JoinRelayRequest { token: "x".to_owned() },
407 Message::ConnectRequest { id: vec![5_u8; 32] },
408 ] {
409 write_message(&mut buffer, &message).await.expect("write");
410 assert_eq!(read_message(&mut &buffer[..]).await.expect("read"), message);
411 buffer.clear();
412 }
413 }
414}