tsproto_packets/
packets.rs

1#![allow(clippy::new_ret_no_self)]
2use std::convert::TryInto;
3use std::io::prelude::*;
4use std::{fmt, io, str};
5
6use bitflags::bitflags;
7use num_derive::{FromPrimitive, ToPrimitive};
8use num_traits::{FromPrimitive as _, ToPrimitive as _};
9use omnom::{ReadExt, WriteExt};
10use serde::{Deserialize, Serialize};
11
12use crate::{Error, HexSlice, Result};
13
14#[derive(
15	Clone, Copy, Debug, Deserialize, Eq, FromPrimitive, Hash, PartialEq, ToPrimitive, Serialize,
16)]
17#[repr(u8)]
18pub enum PacketType {
19	Voice,
20	VoiceWhisper,
21	Command,
22	CommandLow,
23	Ping,
24	Pong,
25	Ack,
26	AckLow,
27	Init,
28}
29
30#[derive(Clone, Copy, Deserialize, Debug, Eq, PartialEq, Hash, Serialize)]
31pub enum Direction {
32	/// Going from the server to the client.
33	S2C,
34	/// Going from the client to the server.
35	C2S,
36}
37
38bitflags! {
39	pub struct Flags: u8 {
40		const UNENCRYPTED = 0x80;
41		const COMPRESSED  = 0x40;
42		const NEWPROTOCOL = 0x20;
43		const FRAGMENTED  = 0x10;
44	}
45}
46
47impl PacketType {
48	pub fn is_command(self) -> bool {
49		self == PacketType::Command || self == PacketType::CommandLow
50	}
51	pub fn is_ack(self) -> bool {
52		self == PacketType::Ack || self == PacketType::AckLow || self == PacketType::Pong
53	}
54	pub fn is_voice(self) -> bool { self == PacketType::Voice || self == PacketType::VoiceWhisper }
55}
56
57#[repr(u8)]
58#[derive(Debug, PartialEq, Eq, Clone, Copy, FromPrimitive, ToPrimitive)]
59pub enum CodecType {
60	/// Mono,   16 bit,  8 kHz, bitrate dependent on the quality setting
61	SpeexNarrowband,
62	/// Mono,   16 bit, 16 kHz, bitrate dependent on the quality setting
63	SpeexWideband,
64	/// Mono,   16 bit, 32 kHz, bitrate dependent on the quality setting
65	SpeexUltrawideband,
66	/// Mono,   16 bit, 48 kHz, bitrate dependent on the quality setting
67	CeltMono,
68	/// Mono,   16 bit, 48 kHz, bitrate dependent on the quality setting, optimized for voice
69	OpusVoice,
70	/// Stereo, 16 bit, 48 kHz, bitrate dependent on the quality setting, optimized for music
71	OpusMusic,
72}
73
74macro_rules! create_buf {
75	($($name:ident, $borrow_name:ident, $convert:ident);*) => {
76		rental! {
77			pub mod rentals {
78				$(
79				#[rental(covariant, debug)]
80				pub struct $name {
81					data: Vec<u8>,
82					content: super::$borrow_name<'data>,
83				}
84				)*
85			}
86		}
87
88		$(
89		#[derive(Debug)]
90		pub struct $name(pub rentals::$name);
91		impl $name {
92			/// `InPacket::try_new` is not checked and must succeed. Otherwise, it
93			/// panics.
94			#[inline]
95			pub fn try_new(direction: Direction, data: Vec<u8>) -> Result<Self> {
96				Ok(Self(rentals::$name::try_new(data, |d| {
97					InPacket::new(direction, d).$convert()
98				}).map_err(|e| e.0)?))
99			}
100			#[inline]
101			pub fn raw_data(&self) -> &[u8] { self.0.head() }
102			#[inline]
103			pub fn data(&self) -> &$borrow_name { self.0.suffix() }
104			#[inline]
105			pub fn into_buffer(self) -> Vec<u8> { self.0.into_head() }
106		}
107		)*
108	}
109}
110
111create_buf!(InAudioBuf, InAudio, into_audio;
112	InCommandBuf, InCommand, into_command;
113	InC2SInitBuf, InC2SInit, into_c2sinit;
114	InS2CInitBuf, InS2CInit, into_s2cinit);
115
116/// Used for debugging.
117#[derive(Clone)]
118pub struct InUdpPacket<'a>(pub InPacket<'a>);
119
120impl<'a> InUdpPacket<'a> {
121	pub fn new(packet: InPacket<'a>) -> Self { Self(packet) }
122}
123
124#[derive(Clone)]
125pub struct InHeader<'a> {
126	direction: Direction,
127	data: &'a [u8],
128}
129
130#[derive(Clone)]
131pub struct InPacket<'a> {
132	header: InHeader<'a>,
133	content: &'a [u8],
134}
135
136#[derive(Clone, Debug)]
137pub struct InCommand<'a> {
138	packet: InPacket<'a>,
139}
140
141#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
142pub struct OutUdpPacket {
143	generation_id: u32,
144	data: OutPacket,
145}
146
147#[derive(Clone, Debug, Deserialize, Eq, PartialEq, Hash, Serialize)]
148pub struct OutPacket {
149	dir: Direction,
150	data: Vec<u8>,
151}
152
153/// The mac has to be `b"TS3INIT1"`.
154///
155/// `version` always contains the Teamspeak version as timestamp.
156///
157/// `timestamp` contains a current timestamp.
158#[derive(Clone)]
159pub enum C2SInitData<'a> {
160	Init0 {
161		version: u32,
162		timestamp: u32,
163		random0: &'a [u8; 4],
164	},
165	Init2 {
166		version: u32,
167		random1: &'a [u8; 16],
168		random0_r: &'a [u8; 4],
169	},
170	Init4 {
171		version: u32,
172		x: &'a [u8; 64],
173		n: &'a [u8; 64],
174		level: u32,
175		random2: &'a [u8; 100],
176		/// y = x ^ (2 ^ level) % n
177		y: &'a [u8; 64],
178		/// Has to be a `clientinitiv alpha=… omega=…` command.
179		command: &'a [u8],
180	},
181}
182
183#[derive(Clone)]
184pub enum S2CInitData<'a> {
185	Init1 { random1: &'a [u8; 16], random0_r: &'a [u8; 4] },
186	Init3 { x: &'a [u8; 64], n: &'a [u8; 64], level: u32, random2: &'a [u8; 100] },
187	Init127 {},
188}
189
190#[derive(Clone, Debug)]
191pub struct InS2CInit<'a> {
192	packet: InPacket<'a>,
193	data: S2CInitData<'a>,
194}
195
196#[derive(Clone, Debug)]
197pub struct InC2SInit<'a> {
198	packet: InPacket<'a>,
199	data: C2SInitData<'a>,
200}
201
202#[derive(Clone, Debug, Eq, PartialEq)]
203pub enum AudioData<'a> {
204	C2S {
205		id: u16,
206		codec: CodecType,
207		data: &'a [u8],
208	},
209	C2SWhisper {
210		id: u16,
211		codec: CodecType,
212		channels: Vec<u64>,
213		clients: Vec<u16>,
214		data: &'a [u8],
215	},
216	/// When the `Flags::NEWPROTOCOL` is set.
217	C2SWhisperNew {
218		id: u16,
219		codec: CodecType,
220		whisper_type: u8,
221		target: u8,
222		target_id: u64,
223		data: &'a [u8],
224	},
225
226	S2C {
227		id: u16,
228		from: u16,
229		codec: CodecType,
230		data: &'a [u8],
231	},
232	S2CWhisper {
233		id: u16,
234		from: u16,
235		codec: CodecType,
236		data: &'a [u8],
237	},
238}
239
240#[derive(Clone)]
241pub struct InAudio<'a> {
242	packet: InPacket<'a>,
243	data: AudioData<'a>,
244}
245
246#[must_use]
247pub struct OutCommand(pub OutPacket);
248pub struct OutC2SInit0;
249pub struct OutC2SInit2;
250pub struct OutC2SInit4;
251pub struct OutS2CInit1;
252pub struct OutS2CInit3;
253pub struct OutAck;
254pub struct OutAudio;
255
256/// A helper to escape data while writing into a buffer.
257struct EscapedWriter<'a>(&'a mut Vec<u8>);
258
259impl Direction {
260	#[inline]
261	pub fn reverse(self) -> Self {
262		match self {
263			Direction::S2C => Direction::C2S,
264			Direction::C2S => Direction::S2C,
265		}
266	}
267}
268
269impl<'a> InPacket<'a> {
270	/// Do some sanity checks before creating the object.
271	#[inline]
272	pub fn try_new(direction: Direction, data: &'a [u8]) -> Result<Self> {
273		let header_len =
274			if direction == Direction::S2C { crate::S2C_HEADER_LEN } else { crate::C2S_HEADER_LEN };
275		if data.len() < header_len {
276			return Err(Error::PacketTooShort(data.len()));
277		}
278
279		// Check packet type
280		let p_type = data[header_len - 1] & 0xf;
281		if p_type > 8 {
282			return Err(Error::UnknownPacketType(p_type));
283		}
284
285		Ok(Self::new(direction, data))
286	}
287
288	/// This method expects that `data` holds a valid packet.
289	///
290	/// If not, further function calls may panic.
291	#[inline]
292	pub fn new(direction: Direction, data: &'a [u8]) -> Self {
293		let header = InHeader::new(direction, data);
294		let header_len = header.data.len();
295
296		Self { header, content: &data[header_len..] }
297	}
298
299	#[inline]
300	pub fn header(&self) -> &InHeader<'a> { &self.header }
301	#[inline]
302	pub fn content(&self) -> &[u8] { self.content }
303
304	/// Get the acknowledged packet id if this is an ack packet.
305	#[inline]
306	pub fn ack_packet(&self) -> Result<Option<u16>> {
307		let p_type = self.header().packet_type();
308		if p_type.is_ack() {
309			Ok(Some(
310				self.content()
311					.read_be()
312					.map_err(|_| Error::PacketContentTooShort(self.content().len()))?,
313			))
314		} else if p_type == PacketType::Init {
315			if self.header.direction == Direction::S2C {
316				Ok(Some(
317					self.content
318						.get(0)
319						.ok_or_else(|| Error::PacketContentTooShort(self.content().len()))
320						.and_then(|i| match u16::from(*i) {
321							1 => Ok(0),
322							3 => Ok(2),
323							127 => Ok(2), // Have to restart sending Init0, remove Init2 anyway
324							_ => Err(Error::InvalidInitStep(*i)),
325						})?,
326				))
327			} else {
328				Ok(self
329					.content
330					.get(4)
331					.ok_or_else(|| Error::PacketContentTooShort(self.content().len()))
332					.and_then(|i| match u16::from(*i) {
333						0 => Ok(None),
334						2 => Ok(Some(1)),
335						4 => Ok(Some(3)),
336						_ => Err(Error::InvalidInitStep(*i)),
337					})?)
338			}
339		} else {
340			Ok(None)
341		}
342	}
343
344	/// Parse this packet into a voice packet.
345	pub fn into_audio(self) -> Result<InAudio<'a>> {
346		let p_type = self.header().packet_type();
347		let newprotocol = self.header().flags().contains(Flags::NEWPROTOCOL);
348		let data = AudioData::parse(p_type, newprotocol, self.header.direction, self.content)?;
349
350		Ok(InAudio { packet: self, data })
351	}
352
353	/// Put this packet into a command packet.
354	pub fn into_command(self) -> Result<InCommand<'a>> { Ok(InCommand { packet: self }) }
355
356	pub fn into_s2cinit(self) -> Result<InS2CInit<'a>> {
357		if self.header.direction != Direction::S2C {
358			return Err(Error::WrongDirection);
359		}
360		let p_type = self.header().packet_type();
361		if p_type != PacketType::Init {
362			return Err(Error::WrongPacketType(p_type));
363		}
364		let mac = self.header().mac();
365		if mac != b"TS3INIT1" {
366			return Err(Error::WrongInitMac(mac.to_vec()));
367		}
368
369		if self.content.is_empty() {
370			return Err(Error::PacketContentTooShort(self.content.len()));
371		}
372
373		let data;
374		if self.content[0] == 1 {
375			if self.content.len() < 21 {
376				return Err(Error::PacketContentTooShort(self.content.len()));
377			}
378			data = S2CInitData::Init1 {
379				random1: (&self.content[1..17]).try_into().unwrap(),
380				random0_r: (&self.content[17..21]).try_into().unwrap(),
381			};
382		} else if self.content[0] == 3 {
383			if self.content.len() < 233 {
384				return Err(Error::PacketContentTooShort(self.content.len()));
385			}
386			data = S2CInitData::Init3 {
387				x: (&self.content[1..65]).try_into().unwrap(),
388				n: (&self.content[65..129]).try_into().unwrap(),
389				level: (&self.content[129..]).read_be()?,
390				random2: (&self.content[133..233]).try_into().unwrap(),
391			};
392		} else if self.content[0] == 127 {
393			data = S2CInitData::Init127 {};
394		} else {
395			return Err(Error::InvalidInitStep(self.content[0]));
396		}
397
398		Ok(InS2CInit { packet: self, data })
399	}
400
401	pub fn into_c2sinit(self) -> Result<InC2SInit<'a>> {
402		if self.header.direction != Direction::C2S {
403			return Err(Error::WrongDirection);
404		}
405		let p_type = self.header().packet_type();
406		if p_type != PacketType::Init {
407			return Err(Error::WrongPacketType(p_type));
408		}
409		let mac = self.header().mac();
410		if mac != b"TS3INIT1" {
411			return Err(Error::WrongInitMac(mac.to_vec()));
412		}
413
414		if self.content.len() < 5 {
415			return Err(Error::PacketContentTooShort(self.content.len()));
416		}
417
418		let data;
419		let version = (&self.content[0..]).read_be()?;
420		if self.content[4] == 0 {
421			if self.content.len() < 13 {
422				return Err(Error::PacketContentTooShort(self.content.len()));
423			}
424			data = C2SInitData::Init0 {
425				version,
426				timestamp: (&self.content[5..]).read_be()?,
427				random0: (&self.content[9..13]).try_into().unwrap(),
428			};
429		} else if self.content[4] == 2 {
430			if self.content.len() < 25 {
431				return Err(Error::PacketContentTooShort(self.content.len()));
432			}
433			data = C2SInitData::Init2 {
434				version,
435				random1: (&self.content[5..21]).try_into().unwrap(),
436				random0_r: (&self.content[21..25]).try_into().unwrap(),
437			};
438		} else if self.content[4] == 4 {
439			let len = 5 + 128 + 4 + 100 + 64;
440			if self.content.len() < len + 20 {
441				return Err(Error::PacketContentTooShort(self.content.len()));
442			}
443			data = C2SInitData::Init4 {
444				version,
445				x: (&self.content[5..69]).try_into().unwrap(),
446				n: (&self.content[69..133]).try_into().unwrap(),
447				level: (&self.content[133..]).read_be()?,
448				random2: (&self.content[137..237]).try_into().unwrap(),
449				y: (&self.content[237..301]).try_into().unwrap(),
450				command: &self.content[len..],
451			};
452		} else {
453			return Err(Error::InvalidInitStep(self.content[0]));
454		}
455
456		Ok(InC2SInit { packet: self, data })
457	}
458}
459
460impl fmt::Debug for InPacket<'_> {
461	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
462		write!(f, "Packet({:?}", self.header())?;
463		let mut success = false;
464		match self.header.packet_type() {
465			PacketType::Voice | PacketType::VoiceWhisper => {
466				if let Ok(packet) = self.clone().into_audio() {
467					success = true;
468					write!(f, ", {:?}", packet)?;
469				}
470			}
471			PacketType::Command | PacketType::CommandLow => {
472				if let Ok(packet) = str::from_utf8(self.content()) {
473					success = true;
474					write!(f, ", {:?}", packet)?;
475				}
476			}
477			PacketType::Ping | PacketType::Pong | PacketType::Ack | PacketType::AckLow => {
478				success = true;
479				if !self.content().is_empty() {
480					write!(f, ", 0x")?;
481				}
482				for b in self.content() {
483					write!(f, "{:02x}", b)?;
484				}
485			}
486			PacketType::Init => {
487				if self.header.direction == Direction::C2S {
488					if let Ok(packet) = self.clone().into_c2sinit() {
489						success = true;
490						write!(f, ", {:?}", packet.data)?;
491					}
492				} else if let Ok(packet) = self.clone().into_s2cinit() {
493					success = true;
494					write!(f, ", {:?}", packet.data)?;
495				}
496			}
497		}
498
499		if !success {
500			write!(f, ", failed to parse, content: {})", HexSlice(self.content()))?;
501		}
502
503		write!(f, ")")?;
504		Ok(())
505	}
506}
507
508impl fmt::Debug for InUdpPacket<'_> {
509	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
510		write!(f, "Packet({:?}, content: {})", self.0.header(), HexSlice(self.0.content()))?;
511		Ok(())
512	}
513}
514
515impl fmt::Debug for InHeader<'_> {
516	#[rustfmt::skip]
517	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
518		write!(f, "Header(")?;
519		if self.mac() != &[0; 8] {
520			write!(f, "mac: {}, ", HexSlice(self.mac()))?;
521		}
522		write!(f, "id: {:#x}, ", self.packet_id())?;
523		if let Some(c_id) = self.client_id() {
524			write!(f, "c_id: {:#x}, ", c_id)?;
525		}
526		write!(f, "{:?}, ", self.packet_type())?;
527		write!(f, "")?;
528		let flags = self.flags();
529		write!(f, "{}", if flags.contains(Flags::UNENCRYPTED) { "u" } else { "-" })?;
530		write!(f, "{}", if flags.contains(Flags::COMPRESSED) { "c" } else { "-" })?;
531		write!(f, "{}", if flags.contains(Flags::NEWPROTOCOL) { "n" } else { "-" })?;
532		write!(f, "{}", if flags.contains(Flags::FRAGMENTED) { "f" } else { "-" })?;
533
534		write!(f, ")")?;
535		Ok(())
536	}
537}
538
539impl<'a> InHeader<'a> {
540	#[inline]
541	pub fn new(direction: Direction, data: &'a [u8]) -> Self {
542		let header_len =
543			if direction == Direction::S2C { crate::S2C_HEADER_LEN } else { crate::C2S_HEADER_LEN };
544
545		Self { direction, data: &data[..header_len] }
546	}
547
548	/// The offset to the packet type.
549	#[inline]
550	fn get_off(&self) -> usize { if self.direction == Direction::S2C { 10 } else { 12 } }
551
552	#[inline]
553	pub fn direction(&self) -> Direction { self.direction }
554	#[inline]
555	pub fn data(&self) -> &'a [u8] { self.data }
556	#[inline]
557	pub fn mac(&self) -> &'a [u8; 8] { (&self.data[..8]).try_into().unwrap() }
558	#[inline]
559	pub fn packet_id(&self) -> u16 { (&self.data[8..10]).read_be().unwrap() }
560
561	#[inline]
562	pub fn client_id(&self) -> Option<u16> {
563		if self.direction == Direction::S2C {
564			None
565		} else {
566			Some((&self.data[10..12]).read_be().unwrap())
567		}
568	}
569
570	#[inline]
571	pub fn flags(&self) -> Flags { Flags::from_bits(self.data[self.get_off()] & 0xf0).unwrap() }
572
573	#[inline]
574	pub fn packet_type(&self) -> PacketType {
575		PacketType::from_u8(self.data[self.get_off()] & 0xf).unwrap()
576	}
577
578	pub fn get_meta(&self) -> &'a [u8] { &self.data[8..] }
579}
580
581impl C2SInitData<'_> {
582	pub fn get_step(&self) -> u8 {
583		match self {
584			C2SInitData::Init0 { .. } => 0,
585			C2SInitData::Init2 { .. } => 2,
586			C2SInitData::Init4 { .. } => 4,
587		}
588	}
589}
590
591impl<'a> fmt::Debug for C2SInitData<'a> {
592	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
593		match self {
594			C2SInitData::Init0 { .. } => write!(f, "Init0"),
595			C2SInitData::Init2 { .. } => write!(f, "Init2"),
596			C2SInitData::Init4 { level, command, .. } => {
597				write!(f, "Init4(level: {}, ", level)?;
598				if let Ok(s) = str::from_utf8(command) {
599					write!(f, "{:?}", s)?;
600				} else {
601					write!(f, "{}", HexSlice(command))?;
602				}
603				write!(f, ")")?;
604				Ok(())
605			}
606		}
607	}
608}
609
610impl S2CInitData<'_> {
611	pub fn get_step(&self) -> u8 {
612		match self {
613			S2CInitData::Init1 { .. } => 1,
614			S2CInitData::Init3 { .. } => 3,
615			S2CInitData::Init127 { .. } => 127,
616		}
617	}
618}
619
620impl<'a> fmt::Debug for S2CInitData<'a> {
621	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
622		match self {
623			S2CInitData::Init1 { .. } => write!(f, "Init1"),
624			S2CInitData::Init3 { level, .. } => write!(f, "Init3(level: {})", level),
625			S2CInitData::Init127 { .. } => write!(f, "Init127"),
626		}
627	}
628}
629
630impl<'a> InCommand<'a> {
631	#[inline]
632	pub fn packet(&self) -> &InPacket<'a> { &self.packet }
633}
634
635impl<'a> AudioData<'a> {
636	pub fn parse(
637		p_type: PacketType, newprotocol: bool, dir: Direction, content: &'a [u8],
638	) -> Result<Self> {
639		let id = (&content[..]).read_be()?;
640		if p_type == PacketType::Voice {
641			if dir == Direction::S2C {
642				if content.len() < 5 {
643					return Err(Error::PacketContentTooShort(content.len()));
644				}
645				Ok(AudioData::S2C {
646					id,
647					from: (&content[2..]).read_be()?,
648					codec: CodecType::from_u8(content[4])
649						.ok_or_else(|| Error::InvalidCodec(content[4]))?,
650					data: &content[5..],
651				})
652			} else {
653				if content.len() < 3 {
654					return Err(Error::PacketContentTooShort(content.len()));
655				}
656				Ok(AudioData::C2S {
657					id,
658					codec: CodecType::from_u8(content[2])
659						.ok_or_else(|| Error::InvalidCodec(content[4]))?,
660					data: &content[3..],
661				})
662			}
663		} else if dir == Direction::S2C {
664			if content.len() < 5 {
665				return Err(Error::PacketContentTooShort(content.len()));
666			}
667			Ok(AudioData::S2CWhisper {
668				id,
669				from: (&content[2..]).read_be()?,
670				codec: CodecType::from_u8(content[4])
671					.ok_or_else(|| Error::InvalidCodec(content[4]))?,
672				data: &content[5..],
673			})
674		} else {
675			if content.len() < 3 {
676				return Err(Error::PacketContentTooShort(content.len()));
677			}
678			let codec =
679				CodecType::from_u8(content[2]).ok_or_else(|| Error::InvalidCodec(content[4]))?;
680			if newprotocol {
681				if content.len() < 14 {
682					return Err(Error::PacketContentTooShort(content.len()));
683				}
684				Ok(AudioData::C2SWhisperNew {
685					id,
686					codec,
687					whisper_type: content[3],
688					target: content[4],
689					target_id: (&content[5..]).read_be()?,
690					data: &content[13..],
691				})
692			} else {
693				if content.len() < 5 {
694					return Err(Error::PacketContentTooShort(content.len()));
695				}
696				let channel_count = content[3] as usize;
697				let client_count = content[4] as usize;
698				let channel_off = 5;
699				let client_off = channel_off + channel_count * 8;
700				let off = client_off + client_count * 2;
701				if content.len() < off {
702					return Err(Error::PacketContentTooShort(content.len()));
703				}
704
705				Ok(AudioData::C2SWhisper {
706					id,
707					codec,
708					channels: (0..channel_count)
709						.map(|i| (&content[channel_off + i * 8..]).read_be())
710						.collect::<::std::result::Result<Vec<_>, _>>()?,
711					clients: (0..client_count)
712						.map(|i| (&content[client_off + i * 2..]).read_be())
713						.collect::<::std::result::Result<Vec<_>, _>>()?,
714					data: &content[off..],
715				})
716			}
717		}
718	}
719
720	#[inline]
721	pub fn direction(&self) -> Direction {
722		match self {
723			AudioData::C2S { .. } => Direction::C2S,
724			AudioData::C2SWhisper { .. } => Direction::C2S,
725			AudioData::C2SWhisperNew { .. } => Direction::C2S,
726			AudioData::S2C { .. } => Direction::S2C,
727			AudioData::S2CWhisper { .. } => Direction::S2C,
728		}
729	}
730
731	#[inline]
732	pub fn packet_type(&self) -> PacketType {
733		match self {
734			AudioData::C2S { .. } => PacketType::Voice,
735			AudioData::C2SWhisper { .. } => PacketType::VoiceWhisper,
736			AudioData::C2SWhisperNew { .. } => PacketType::VoiceWhisper,
737			AudioData::S2C { .. } => PacketType::Voice,
738			AudioData::S2CWhisper { .. } => PacketType::VoiceWhisper,
739		}
740	}
741
742	#[inline]
743	pub fn codec(&self) -> CodecType {
744		match self {
745			AudioData::C2S { codec, .. } => *codec,
746			AudioData::C2SWhisper { codec, .. } => *codec,
747			AudioData::C2SWhisperNew { codec, .. } => *codec,
748			AudioData::S2C { codec, .. } => *codec,
749			AudioData::S2CWhisper { codec, .. } => *codec,
750		}
751	}
752
753	#[inline]
754	pub fn id(&self) -> u16 {
755		match self {
756			AudioData::C2S { id, .. } => *id,
757			AudioData::C2SWhisper { id, .. } => *id,
758			AudioData::C2SWhisperNew { id, .. } => *id,
759			AudioData::S2C { id, .. } => *id,
760			AudioData::S2CWhisper { id, .. } => *id,
761		}
762	}
763
764	#[inline]
765	pub fn flags(&self) -> Flags {
766		match self {
767			AudioData::C2S { .. } => Flags::empty(),
768			AudioData::C2SWhisper { .. } => Flags::empty(),
769			AudioData::C2SWhisperNew { .. } => Flags::NEWPROTOCOL,
770			AudioData::S2C { .. } => Flags::empty(),
771			AudioData::S2CWhisper { .. } => Flags::empty(),
772		}
773	}
774
775	#[inline]
776	pub fn data(&self) -> &[u8] {
777		match self {
778			AudioData::C2S { data, .. } => data,
779			AudioData::C2SWhisper { data, .. } => data,
780			AudioData::C2SWhisperNew { data, .. } => data,
781			AudioData::S2C { data, .. } => data,
782			AudioData::S2CWhisper { data, .. } => data,
783		}
784	}
785}
786
787impl<'a> InS2CInit<'a> {
788	#[inline]
789	pub fn packet(&self) -> &InPacket<'a> { &self.packet }
790	#[inline]
791	pub fn data(&self) -> &S2CInitData { &self.data }
792}
793
794impl<'a> InC2SInit<'a> {
795	#[inline]
796	pub fn packet(&self) -> &InPacket<'a> { &self.packet }
797	#[inline]
798	pub fn data(&self) -> &C2SInitData { &self.data }
799}
800
801impl<'a> InAudio<'a> {
802	#[inline]
803	pub fn packet(&self) -> &InPacket<'a> { &self.packet }
804	#[inline]
805	pub fn data(&self) -> &AudioData { &self.data }
806}
807
808impl fmt::Debug for InAudio<'_> {
809	fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result {
810		match &self.data {
811			AudioData::C2S { id, codec, data } => {
812				write!(f, "Audio(id: {}, {:?}, {})", id, codec, HexSlice(data))?;
813			}
814			AudioData::C2SWhisper { id, codec, channels, clients, data } => {
815				write!(
816					f,
817					"Whisper(id: {}, {:?}, channels: {:?}, clients: {:?}, {})",
818					id,
819					codec,
820					channels,
821					clients,
822					HexSlice(data)
823				)?;
824			}
825			AudioData::C2SWhisperNew { id, codec, whisper_type, target, target_id, data } => {
826				write!(
827					f,
828					"WhisperNew(id: {}, {:?}, type: {}, target: {}, target_id: {}, {})",
829					id,
830					codec,
831					whisper_type,
832					target,
833					target_id,
834					HexSlice(data)
835				)?;
836			}
837			AudioData::S2C { id, from, codec, data } => {
838				write!(f, "Audio(id: {}, from: {}, {:?}, {})", id, from, codec, HexSlice(data))?;
839			}
840			AudioData::S2CWhisper { id, from, codec, data } => {
841				write!(f, "Whisper(id: {}, from: {}, {:?}, {})", id, from, codec, HexSlice(data))?;
842			}
843		}
844
845		Ok(())
846	}
847}
848
849impl OutPacket {
850	#[inline]
851	pub fn new(
852		mac: [u8; 8], packet_id: u16, client_id: Option<u16>, flags: Flags, packet_type: PacketType,
853	) -> Self {
854		let dir = if client_id.is_some() { Direction::C2S } else { Direction::S2C };
855		let mut res = Self::new_with_dir(dir, flags, packet_type);
856		res.data[..8].copy_from_slice(&mac);
857		res.packet_id(packet_id);
858		if let Some(cid) = client_id {
859			res.client_id(cid);
860		}
861		res
862	}
863
864	/// Fill packet with known data. The rest gets filled by `packet_codec`.
865	#[inline]
866	pub fn new_with_dir(dir: Direction, flags: Flags, packet_type: PacketType) -> Self {
867		let data =
868			vec![
869				0;
870				if dir == Direction::S2C { crate::S2C_HEADER_LEN } else { crate::C2S_HEADER_LEN }
871			];
872		let mut res = Self { dir, data };
873		res.flags(flags);
874		res.packet_type(packet_type);
875		res
876	}
877
878	#[inline]
879	pub fn new_from_data(dir: Direction, data: Vec<u8>) -> Self { Self { dir, data } }
880
881	#[inline]
882	pub fn into_vec(self) -> Vec<u8> { self.data }
883
884	#[inline]
885	fn content_offset(&self) -> usize {
886		if self.dir == Direction::S2C { crate::S2C_HEADER_LEN } else { crate::C2S_HEADER_LEN }
887	}
888
889	#[inline]
890	pub fn data(&self) -> &[u8] { &self.data }
891	#[inline]
892	pub fn data_mut(&mut self) -> &mut Vec<u8> { &mut self.data }
893	#[inline]
894	pub fn content(&self) -> &[u8] { &self.data[self.content_offset()..] }
895	#[inline]
896	pub fn content_mut(&mut self) -> &mut [u8] {
897		let off = self.content_offset();
898		&mut self.data[off..]
899	}
900	#[inline]
901	pub fn direction(&self) -> Direction { self.dir }
902	#[inline]
903	pub fn header(&self) -> InHeader { InHeader { direction: self.dir, data: self.header_bytes() } }
904	#[inline]
905	pub fn header_bytes(&self) -> &[u8] { &self.data[..self.content_offset()] }
906	#[inline]
907	pub fn packet(&self) -> InPacket { InPacket::new(self.dir, &self.data) }
908
909	#[inline]
910	pub fn mac(&mut self) -> &mut [u8; 8] { (&mut self.data[0..8]).try_into().unwrap() }
911	#[inline]
912	pub fn packet_id(&mut self, packet_id: u16) {
913		(&mut self.data[8..10]).write_be(packet_id).unwrap();
914	}
915	#[inline]
916	pub fn client_id(&mut self, client_id: u16) {
917		assert_eq!(
918			self.dir,
919			Direction::C2S,
920			"Client id is only valid for client to server packets"
921		);
922		(&mut self.data[10..12]).write_be(client_id).unwrap();
923	}
924	#[inline]
925	pub fn flags(&mut self, flags: Flags) {
926		let off = self.header().get_off();
927		self.data[off] = (self.data[off] & 0xf) | flags.bits();
928	}
929	#[inline]
930	pub fn packet_type(&mut self, packet_type: PacketType) {
931		let off = self.header().get_off();
932		self.data[off] = (self.data[off] & 0xf0) | packet_type.to_u8().unwrap();
933	}
934}
935
936impl OutUdpPacket {
937	#[inline]
938	pub fn new(generation_id: u32, data: OutPacket) -> Self { Self { generation_id, data } }
939
940	#[inline]
941	pub fn generation_id(&self) -> u32 { self.generation_id }
942	#[inline]
943	pub fn data(&self) -> &OutPacket { &self.data }
944
945	#[inline]
946	pub fn packet_id(&self) -> u16 {
947		if self.packet_type() == PacketType::Init {
948			if self.data.dir == Direction::S2C {
949				u16::from(self.data.content()[0])
950			} else {
951				u16::from(self.data.content()[4])
952			}
953		} else {
954			self.data.header().packet_id()
955		}
956	}
957	#[inline]
958	pub fn packet_type(&self) -> PacketType { self.data.header().packet_type() }
959}
960
961impl io::Write for EscapedWriter<'_> {
962	fn write(&mut self, buf: &[u8]) -> io::Result<usize> {
963		self.0.reserve(buf.len());
964		for c in buf {
965			match c {
966				b'\x0b' => self.0.extend_from_slice(b"\\v"),
967				b'\x0c' => self.0.extend_from_slice(b"\\f"),
968				b'\\' => self.0.extend_from_slice(b"\\\\"),
969				b'\t' => self.0.extend_from_slice(b"\\t"),
970				b'\r' => self.0.extend_from_slice(b"\\r"),
971				b'\n' => self.0.extend_from_slice(b"\\n"),
972				b'|' => self.0.extend_from_slice(b"\\p"),
973				b' ' => self.0.extend_from_slice(b"\\s"),
974				b'/' => self.0.extend_from_slice(b"\\/"),
975				c => self.0.push(*c),
976			}
977		}
978		Ok(buf.len())
979	}
980
981	#[inline]
982	fn flush(&mut self) -> io::Result<()> { Ok(()) }
983}
984
985impl OutCommand {
986	#[inline]
987	pub fn new(dir: Direction, flags: Flags, p_type: PacketType, name: &str) -> Self {
988		let mut res = Self(OutPacket::new_with_dir(dir, Flags::empty(), p_type));
989		res.0.flags(flags);
990		res.0.data.extend_from_slice(name.as_bytes());
991		res
992	}
993
994	/// For binary arguments. The value will still be escaped.
995	#[inline]
996	pub fn write_bin_arg(&mut self, name: &str, value: &[u8]) {
997		if self.0.content().last() != Some(&b'|') && !self.0.content().is_empty() {
998			self.0.data.push(b' ');
999		}
1000		self.0.data.extend_from_slice(name.as_bytes());
1001		if !value.is_empty() {
1002			self.0.data.push(b'=');
1003			EscapedWriter(&mut self.0.data).write_all(value).unwrap();
1004		}
1005	}
1006
1007	/// The value will be formatted and escaped.
1008	#[inline]
1009	pub fn write_arg(&mut self, name: &str, value: &dyn fmt::Display) {
1010		if self.0.content().last() != Some(&b'|') && !self.0.content().is_empty() {
1011			self.0.data.push(b' ');
1012		}
1013		self.0.data.extend_from_slice(name.as_bytes());
1014		self.0.data.push(b'=');
1015		let len = self.0.data.len();
1016		write!(EscapedWriter(&mut self.0.data), "{}", value).unwrap();
1017		if self.0.data.len() == len {
1018			// Nothing was written, remove =
1019			self.0.data.pop();
1020		}
1021	}
1022
1023	/// Adds a pipe symbol `|` to the command.
1024	#[inline]
1025	pub fn start_new_part(&mut self) { self.0.data.push(b'|'); }
1026	#[inline]
1027	pub fn into_packet(self) -> OutPacket { self.0 }
1028}
1029
1030impl OutC2SInit0 {
1031	pub fn new(version: u32, timestamp: u32, random0: [u8; 4]) -> OutPacket {
1032		let mut res = OutPacket::new_with_dir(Direction::C2S, Flags::empty(), PacketType::Init);
1033		res.mac().copy_from_slice(b"TS3INIT1");
1034		res.packet_id(0x65);
1035		let content = res.data_mut();
1036		content.write_be(version).unwrap();
1037		content.write_be(0u8).unwrap();
1038		content.write_be(timestamp).unwrap();
1039		content.write_all(&random0).unwrap();
1040		// Reserved
1041		content.write_all(&[0u8; 8]).unwrap();
1042		res
1043	}
1044}
1045
1046impl OutC2SInit2 {
1047	pub fn new(version: u32, random1: &[u8; 16], random0_r: [u8; 4]) -> OutPacket {
1048		let mut res = OutPacket::new_with_dir(Direction::C2S, Flags::empty(), PacketType::Init);
1049		res.mac().copy_from_slice(b"TS3INIT1");
1050		res.packet_id(0x65);
1051		let content = res.data_mut();
1052		content.write_be(version).unwrap();
1053		content.write_be(2u8).unwrap();
1054		content.write_all(random1).unwrap();
1055		content.write_all(&random0_r).unwrap();
1056		res
1057	}
1058}
1059
1060impl OutC2SInit4 {
1061	pub fn new(
1062		version: u32, x: &[u8; 64], n: &[u8; 64], level: u32, random2: &[u8; 100], y: &[u8; 64],
1063		alpha: &[u8], omega: &[u8], ip: &str,
1064	) -> OutPacket {
1065		let mut res = OutPacket::new_with_dir(Direction::C2S, Flags::empty(), PacketType::Init);
1066		res.mac().copy_from_slice(b"TS3INIT1");
1067		res.packet_id(0x65);
1068		let content = res.data_mut();
1069		content.write_be(version).unwrap();
1070		content.write_be(4u8).unwrap();
1071		content.write_all(x).unwrap();
1072		content.write_all(n).unwrap();
1073		content.write_be(level).unwrap();
1074		content.write_all(random2).unwrap();
1075		content.write_all(y).unwrap();
1076		let ip = if ip.is_empty() { String::new() } else { format!("={}", ip) };
1077		content
1078			.write_all(
1079				format!(
1080					"clientinitiv alpha={} omega={} ot=1 ip{}",
1081					base64::encode(alpha),
1082					base64::encode(omega),
1083					ip
1084				)
1085				.as_bytes(),
1086			)
1087			.unwrap();
1088		res
1089	}
1090}
1091
1092impl OutS2CInit1 {
1093	pub fn new(random1: &[u8; 16], random0_r: [u8; 4]) -> OutPacket {
1094		let mut res = OutPacket::new_with_dir(Direction::S2C, Flags::empty(), PacketType::Init);
1095		res.mac().copy_from_slice(b"TS3INIT1");
1096		res.packet_id(0x65);
1097		let content = res.data_mut();
1098		content.write_be(1u8).unwrap();
1099		content.write_all(random1).unwrap();
1100		content.write_all(&random0_r).unwrap();
1101		res
1102	}
1103}
1104
1105impl OutS2CInit3 {
1106	pub fn new(x: &[u8; 64], n: &[u8; 64], level: u32, random2: &[u8; 100]) -> OutPacket {
1107		let mut res = OutPacket::new_with_dir(Direction::S2C, Flags::empty(), PacketType::Init);
1108		res.mac().copy_from_slice(b"TS3INIT1");
1109		res.packet_id(0x65);
1110		let content = res.data_mut();
1111		content.write_be(3u8).unwrap();
1112		content.write_all(x).unwrap();
1113		content.write_all(n).unwrap();
1114		content.write_be(level).unwrap();
1115		content.write_all(random2).unwrap();
1116		res
1117	}
1118}
1119
1120impl OutAck {
1121	/// `for_type` is the packet type which gets acknowledged, so e.g. `Command`.
1122	pub fn new(dir: Direction, for_type: PacketType, packet_id: u16) -> OutPacket {
1123		let p_type = if for_type == PacketType::Command {
1124			PacketType::Ack
1125		} else if for_type == PacketType::CommandLow {
1126			PacketType::AckLow
1127		} else if for_type == PacketType::Ping {
1128			PacketType::Pong
1129		} else {
1130			panic!("Invalid packet type to create ack {:?}", for_type);
1131		};
1132
1133		let mut res = OutPacket::new_with_dir(dir, Flags::empty(), p_type);
1134		let content = res.data_mut();
1135		content.write_be(packet_id).unwrap();
1136		res
1137	}
1138}
1139
1140impl OutAudio {
1141	pub fn new(data: &AudioData) -> OutPacket {
1142		let mut res = OutPacket::new_with_dir(data.direction(), data.flags(), data.packet_type());
1143		let content = res.data_mut();
1144
1145		content.write_be(data.id()).unwrap();
1146		match data {
1147			AudioData::C2S { codec, data, .. } => {
1148				content.write_be(codec.to_u8().unwrap()).unwrap();
1149				content.extend_from_slice(data);
1150			}
1151			AudioData::C2SWhisper { codec, channels, clients, data, .. } => {
1152				content.write_be(codec.to_u8().unwrap()).unwrap();
1153				content.write_be(channels.len() as u8).unwrap();
1154				content.write_be(clients.len() as u8).unwrap();
1155
1156				for c in channels {
1157					content.write_be(*c).unwrap();
1158				}
1159				for c in clients {
1160					content.write_be(*c).unwrap();
1161				}
1162				content.extend_from_slice(data);
1163			}
1164			AudioData::C2SWhisperNew { codec, whisper_type, target, target_id, data, .. } => {
1165				content.write_be(codec.to_u8().unwrap()).unwrap();
1166				content.write_be(whisper_type.to_u8().unwrap()).unwrap();
1167				content.write_be(*target).unwrap();
1168				content.write_be(*target_id).unwrap();
1169				content.extend_from_slice(data);
1170			}
1171			AudioData::S2C { from, codec, data, .. }
1172			| AudioData::S2CWhisper { from, codec, data, .. } => {
1173				content.write_be(*from).unwrap();
1174				content.write_be(codec.to_u8().unwrap()).unwrap();
1175				content.extend_from_slice(data);
1176			}
1177		}
1178
1179		res
1180	}
1181}
1182
1183#[cfg(test)]
1184mod tests {
1185	use super::*;
1186
1187	fn test_audio_roundtrip(dir: Direction, data: &AudioData) {
1188		let out_p = OutAudio::new(data);
1189		let in_p = InPacket::try_new(dir, out_p.data()).unwrap();
1190		let audio = in_p.into_audio().unwrap();
1191		assert_eq!(audio.data(), data);
1192	}
1193
1194	#[test]
1195	fn test_audio_c2s() {
1196		let data = AudioData::C2S { codec: CodecType::OpusVoice, id: 0x1234, data: &[1, 2, 3] };
1197		test_audio_roundtrip(Direction::C2S, &data);
1198	}
1199
1200	#[test]
1201	fn test_audio_c2s_whisper() {
1202		let data = AudioData::C2SWhisper {
1203			codec: CodecType::OpusVoice,
1204			channels: vec![4, 5, 6],
1205			clients: vec![7, 8],
1206			id: 0x1234,
1207			data: &[1, 2, 3],
1208		};
1209		test_audio_roundtrip(Direction::C2S, &data);
1210	}
1211
1212	#[test]
1213	fn test_audio_c2s_whisper_new() {
1214		let data = AudioData::C2SWhisperNew {
1215			codec: CodecType::OpusVoice,
1216			whisper_type: 3,
1217			target: 4,
1218			target_id: 5,
1219			id: 0x1234,
1220			data: &[1, 2, 3],
1221		};
1222		test_audio_roundtrip(Direction::C2S, &data);
1223	}
1224
1225	#[test]
1226	fn test_audio_s2c() {
1227		let data = AudioData::S2C {
1228			codec: CodecType::OpusVoice,
1229			id: 0x1234,
1230			from: 0x5678,
1231			data: &[1, 2, 3],
1232		};
1233		test_audio_roundtrip(Direction::S2C, &data);
1234	}
1235
1236	#[test]
1237	fn test_audio_s2c_whisper() {
1238		let data = AudioData::S2CWhisper {
1239			codec: CodecType::OpusVoice,
1240			id: 0x1234,
1241			from: 0x5678,
1242			data: &[1, 2, 3],
1243		};
1244		test_audio_roundtrip(Direction::S2C, &data);
1245	}
1246}