1pub mod errors;
55pub mod info;
57#[cfg(feature = "async")]
59pub mod nonblocking;
60pub mod players;
62pub mod rules;
64
65use std::io::Cursor;
66use std::io::Read;
67use std::io::Write;
68use std::net::ToSocketAddrs;
69use std::net::UdpSocket;
70use std::ops::Deref;
71use std::time::Duration;
72
73use bstr::BString;
74use byteorder::LittleEndian;
75use byteorder::ReadBytesExt;
76use byteorder::WriteBytesExt;
77use bzip2::read::BzDecoder;
78use crc::crc32;
79
80use crate::errors::Error;
81use crate::errors::Result;
82
83pub(crate) const SINGLE_PACKET: i32 = -1;
84pub(crate) const MULTI_PACKET: i32 = -2;
85pub(crate) const MAX_CHALLENGE_RETRIES: usize = 3;
86
87pub const HEADER_CHALLENGE: u8 = b'A';
89pub const HEADER_INFO: u8 = b'I';
91pub const HEADER_PLAYER: u8 = b'D';
93pub const HEADER_RULES: u8 = b'E';
95
96pub(crate) const OFS_HEADER: usize = 0;
98pub(crate) const OFS_SP_PAYLOAD: usize = 4;
99pub(crate) const OFS_MP_ID: usize = 4;
100pub(crate) const OFS_MP_SS_TOTAL: usize = 8;
101pub(crate) const OFS_MP_SS_NUMBER: usize = 9;
102pub(crate) const OFS_MP_SS_SIZE: usize = 10;
103pub(crate) const OFS_MP_SS_BZ2_SIZE: usize = 12;
104pub(crate) const OFS_MP_SS_BZ2_CRC: usize = 16;
105pub(crate) const OFS_MP_SS_PAYLOAD: usize = OFS_MP_SS_BZ2_SIZE;
106pub(crate) const OFS_MP_SS_PAYLOAD_BZ2: usize = OFS_MP_SS_BZ2_CRC + 4;
107
108macro_rules! read_buffer_offset {
109 ($buf:expr, $offset:expr, i8) => {
110 $buf[$offset].into()
111 };
112 ($buf:expr, $offset:expr, u8) => {
113 $buf[$offset].into()
114 };
115 ($buf:expr, $offset:expr, i16) => {
116 i16::from_le_bytes([$buf[$offset], $buf[$offset + 1]])
117 };
118 ($buf:expr, $offset:expr, u16) => {
119 u16::from_le_bytes([$buf[$offset], $buf[$offset + 1]])
120 };
121 ($buf:expr, $offset:expr, i32) => {
122 i32::from_le_bytes([
123 $buf[$offset],
124 $buf[$offset + 1],
125 $buf[$offset + 2],
126 $buf[$offset + 3],
127 ])
128 };
129 ($buf:expr, $offset:expr, u32) => {
130 u32::from_le_bytes([
131 $buf[$offset],
132 $buf[$offset + 1],
133 $buf[$offset + 2],
134 $buf[$offset + 3],
135 ])
136 };
137 ($buf:expr, $offset:expr, i64) => {
138 i64::from_le_bytes([
139 $buf[$offset],
140 $buf[$offset + 1],
141 $buf[$offset + 2],
142 $buf[$offset + 3],
143 $buf[$offset + 4],
144 $buf[$offset + 5],
145 $buf[$offset + 6],
146 $buf[$offset + 7],
147 ])
148 };
149 ($buf:expr, $offset:expr, u64) => {
150 u64::from_le_bytes([
151 $buf[$offset],
152 $buf[$offset + 1],
153 $buf[$offset + 2],
154 $buf[$offset + 3],
155 $buf[$offset + 4],
156 $buf[$offset + 5],
157 $buf[$offset + 6],
158 $buf[$offset + 7],
159 ])
160 };
161}
162
163#[cfg(feature = "async")]
164pub(crate) use read_buffer_offset;
165
166#[cfg(feature = "serde")]
167use serde::Deserialize;
168#[cfg(feature = "serde")]
169use serde::Serialize;
170
171#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
173#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
174#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
175pub struct AppId(pub u16);
176
177impl AppId {
178 pub const THE_SHIP: Self = Self(2400);
180}
181
182#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
184#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
185#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
186pub struct SteamId(pub u64);
187
188#[derive(Debug, Clone, Copy, PartialEq, Eq, Hash)]
191#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
192#[cfg_attr(feature = "serde", derive(Deserialize, Serialize))]
193pub struct GameId(pub u64);
194
195#[cfg(feature = "arbitrary")]
196pub(crate) fn arbitrary_bstring(u: &mut arbitrary::Unstructured<'_>) -> arbitrary::Result<BString> {
197 let bytes: Vec<u8> = arbitrary::Arbitrary::arbitrary(u)?;
198 Ok(BString::new(bytes))
199}
200
201#[cfg(feature = "arbitrary")]
202pub(crate) fn arbitrary_option_bstring(
203 u: &mut arbitrary::Unstructured<'_>,
204) -> arbitrary::Result<Option<BString>> {
205 if arbitrary::Arbitrary::arbitrary(u)? {
206 Ok(Some(arbitrary_bstring(u)?))
207 } else {
208 Ok(None)
209 }
210}
211
212#[derive(Debug, Clone, Default)]
230#[cfg_attr(feature = "arbitrary", derive(arbitrary::Arbitrary))]
231pub struct DeOptions {
232 pub the_ship: bool,
235}
236
237impl DeOptions {
238 pub fn from_app_id(app_id: AppId) -> Self {
241 Self {
242 the_ship: app_id == AppId::THE_SHIP,
243 }
244 }
245}
246
247#[derive(Debug)]
248pub(crate) struct PacketFragment {
249 pub number: u8,
250 pub payload: Vec<u8>,
251}
252
253pub struct Client {
278 socket: UdpSocket,
279 max_size: usize,
280 pub(crate) de_options: DeOptions,
281}
282
283impl Client {
284 pub fn new(timeout: Duration) -> Result<Client> {
286 let socket = UdpSocket::bind("0.0.0.0:0")?;
287
288 socket.set_read_timeout(Some(timeout))?;
289 socket.set_write_timeout(Some(timeout))?;
290
291 Ok(Client {
292 socket,
293 max_size: 1400,
294 de_options: DeOptions::default(),
295 })
296 }
297
298 pub fn max_size(&mut self, size: usize) -> &mut Self {
300 self.max_size = size;
301 self
302 }
303
304 #[deprecated(since = "0.6.2", note = "use de_options")]
305 pub fn app_id(&mut self, app_id: AppId) -> &mut Self {
306 self.de_options = DeOptions::from_app_id(app_id);
307 self
308 }
309
310 pub fn de_options(&mut self, de_options: DeOptions) -> &mut Self {
313 self.de_options = de_options;
314 self
315 }
316
317 pub fn set_timeout(&mut self, timeout: Duration) -> Result<&mut Self> {
319 self.socket.set_read_timeout(Some(timeout))?;
320 self.socket.set_write_timeout(Some(timeout))?;
321 Ok(self)
322 }
323
324 #[doc(hidden)]
325 pub fn send<A: ToSocketAddrs>(&self, payload: &[u8], addr: A) -> Result<Vec<u8>> {
326 self.socket.send_to(payload, addr)?;
327
328 let mut data = vec![0; self.max_size];
329
330 let read = self.socket.recv(&mut data)?;
331 data.truncate(read);
332
333 let header = read_buffer_offset!(&data, OFS_HEADER, i32);
334
335 if header == SINGLE_PACKET {
336 Ok(data[OFS_SP_PAYLOAD..].to_vec())
337 } else if header == MULTI_PACKET {
338 let id = read_buffer_offset!(&data, OFS_MP_ID, i32);
339 let total_packets: usize = data[OFS_MP_SS_TOTAL].into();
340 let switching_size: usize = read_buffer_offset!(&data, OFS_MP_SS_SIZE, u16).into();
341
342 if (switching_size > self.max_size) || (total_packets > 32) {
343 return Err(Error::MultiPacketTooLarge);
344 }
345
346 let mut packets: Vec<PacketFragment> = Vec::with_capacity(0);
347 packets.try_reserve(total_packets)?;
348 packets.push(PacketFragment {
349 number: data[OFS_MP_SS_NUMBER],
350 payload: Vec::from(&data[OFS_MP_SS_PAYLOAD + 4..]),
351 });
352
353 loop {
354 let mut data: Vec<u8> = Vec::with_capacity(0);
355 data.try_reserve(switching_size)?;
356 data.resize(switching_size, 0);
357
358 let read = self.socket.recv(&mut data)?;
359 data.truncate(read);
360
361 if data.len() <= 9 {
362 return Err(Error::PacketTooShort {
363 expected: 10,
364 actual: data.len(),
365 });
366 }
367
368 let packet_id = read_buffer_offset!(&data, OFS_MP_ID, i32);
369
370 if packet_id != id {
371 return Err(Error::MismatchPacketId);
372 }
373
374 if id as u32 & 0x80000000 == 0 {
375 packets.push(PacketFragment {
376 number: data[OFS_MP_SS_NUMBER],
377 payload: Vec::from(&data[OFS_MP_SS_PAYLOAD..]),
378 });
379 } else {
380 packets.push(PacketFragment {
381 number: data[OFS_MP_SS_NUMBER],
382 payload: Vec::from(&data[OFS_MP_SS_PAYLOAD_BZ2..]),
383 });
384 }
385
386 if packets.len() == total_packets {
387 break;
388 }
389 }
390
391 packets.sort_by_key(|p| p.number);
392
393 let mut aggregation = Vec::with_capacity(0);
394 aggregation.try_reserve(total_packets * self.max_size)?;
395
396 for p in packets {
397 aggregation.extend(p.payload);
398 }
399
400 if id as u32 & 0x80000000 != 0 {
401 let decompressed_size = read_buffer_offset!(&data, OFS_MP_SS_BZ2_SIZE, u32);
402 let checksum = read_buffer_offset!(&data, OFS_MP_SS_BZ2_CRC, u32);
403
404 if decompressed_size > (1024 * 1024) {
405 return Err(Error::InvalidBz2Size);
406 }
407
408 let mut decompressed = Vec::with_capacity(0);
409 decompressed.try_reserve(decompressed_size as usize)?;
410 decompressed.resize(decompressed_size as usize, 0);
411
412 BzDecoder::new(aggregation.deref()).read_exact(&mut decompressed)?;
413
414 if crc32::checksum_ieee(&decompressed) != checksum {
415 return Err(Error::ChecksumMismatch);
416 }
417
418 Ok(decompressed)
419 } else {
420 Ok(aggregation)
421 }
422 } else {
423 Err(Error::UnexpectedHeader {
424 expected: SINGLE_PACKET as u8,
425 actual: data[0],
426 })
427 }
428 }
429
430 #[doc(hidden)]
431 pub fn do_challenge_request<A: ToSocketAddrs>(
432 &self,
433 addr: A,
434 header: &[u8],
435 ) -> Result<Vec<u8>> {
436 let packet = Vec::with_capacity(9);
437 let mut packet = Cursor::new(packet);
438
439 packet.write_all(header)?;
440 packet.write_i32::<LittleEndian>(-1)?;
441
442 let mut data = self.send(packet.get_ref(), &addr)?;
443
444 for _ in 0..MAX_CHALLENGE_RETRIES {
445 if data.first() != Some(&HEADER_CHALLENGE) {
446 return Ok(data);
447 }
448
449 let mut cursor = Cursor::new(&data);
450 cursor.read_u8()?; let challenge = cursor.read_i32::<LittleEndian>()?;
452
453 packet.set_position(5);
454 packet.write_i32::<LittleEndian>(challenge)?;
455 data = self.send(packet.get_ref(), &addr)?;
456 }
457
458 Ok(data)
459 }
460}
461
462pub(crate) trait ReadCString: Read {
463 fn read_cstring(&mut self) -> Result<BString> {
464 let mut buf = Vec::with_capacity(256);
465 while let Ok(byte) = self.read_u8() {
466 if byte == 0 {
467 break;
468 }
469
470 buf.push(byte);
471 }
472
473 Ok(BString::new(buf))
474 }
475}
476
477impl<R: Read + ?Sized> ReadCString for R {}