1use std::fmt;
7
8use bitflags::bitflags;
9
10use super::cursor::{Cursor, CursorMut, GetKeyValue, PutKeyValue};
11use super::filter::Version;
12use super::wrappers::Str;
13use super::{CursorError, Error};
14
15#[derive(Clone, Debug, PartialEq)]
17pub struct Challenge {
18 pub server_challenge: Option<u32>,
20}
21
22impl Challenge {
23 pub const HEADER: &'static [u8] = b"q\xff";
25
26 pub fn new(server_challenge: Option<u32>) -> Self {
28 Self { server_challenge }
29 }
30
31 pub fn decode(src: &[u8]) -> Result<Self, Error> {
33 let mut cur = Cursor::new(src);
34 cur.expect(Self::HEADER)?;
35 let server_challenge = if cur.remaining() == 4 {
36 Some(cur.get_u32_le()?)
37 } else {
38 None
39 };
40 cur.expect_empty()?;
41 Ok(Self { server_challenge })
42 }
43
44 pub fn encode<const N: usize>(&self, buf: &mut [u8; N]) -> Result<usize, Error> {
46 let mut cur = CursorMut::new(buf);
47 cur.put_bytes(Self::HEADER)?;
48 if let Some(server_challenge) = self.server_challenge {
49 cur.put_u32_le(server_challenge)?;
50 }
51 Ok(cur.pos())
52 }
53}
54
55#[derive(Copy, Clone, Debug, PartialEq, Eq)]
57#[repr(u8)]
58pub enum Os {
59 Linux,
61 Windows,
63 Mac,
65 Unknown,
67}
68
69impl Default for Os {
70 fn default() -> Os {
71 Os::Unknown
72 }
73}
74
75impl TryFrom<&[u8]> for Os {
76 type Error = CursorError;
77
78 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
79 match value {
80 b"l" => Ok(Os::Linux),
81 b"w" => Ok(Os::Windows),
82 b"m" => Ok(Os::Mac),
83 _ => Ok(Os::Unknown),
84 }
85 }
86}
87
88impl GetKeyValue<'_> for Os {
89 fn get_key_value(cur: &mut Cursor) -> Result<Self, CursorError> {
90 cur.get_key_value_raw()?.try_into()
91 }
92}
93
94impl PutKeyValue for Os {
95 fn put_key_value<'a, 'b>(
96 &self,
97 cur: &'b mut CursorMut<'a>,
98 ) -> Result<&'b mut CursorMut<'a>, CursorError> {
99 match self {
100 Self::Linux => cur.put_str("l"),
101 Self::Windows => cur.put_str("w"),
102 Self::Mac => cur.put_str("m"),
103 Self::Unknown => cur.put_str("?"),
104 }
105 }
106}
107
108impl fmt::Display for Os {
109 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
110 let s = match self {
111 Os::Linux => "Linux",
112 Os::Windows => "Windows",
113 Os::Mac => "Mac",
114 Os::Unknown => "Unknown",
115 };
116 write!(fmt, "{}", s)
117 }
118}
119
120#[derive(Copy, Clone, Debug, PartialEq)]
122#[repr(u8)]
123pub enum ServerType {
124 Dedicated,
126 Local,
128 Proxy,
130 Unknown,
132}
133
134impl Default for ServerType {
135 fn default() -> Self {
136 Self::Unknown
137 }
138}
139
140impl TryFrom<&[u8]> for ServerType {
141 type Error = CursorError;
142
143 fn try_from(value: &[u8]) -> Result<Self, Self::Error> {
144 match value {
145 b"d" => Ok(Self::Dedicated),
146 b"l" => Ok(Self::Local),
147 b"p" => Ok(Self::Proxy),
148 _ => Ok(Self::Unknown),
149 }
150 }
151}
152
153impl GetKeyValue<'_> for ServerType {
154 fn get_key_value(cur: &mut Cursor) -> Result<Self, CursorError> {
155 cur.get_key_value_raw()?.try_into()
156 }
157}
158
159impl PutKeyValue for ServerType {
160 fn put_key_value<'a, 'b>(
161 &self,
162 cur: &'b mut CursorMut<'a>,
163 ) -> Result<&'b mut CursorMut<'a>, CursorError> {
164 match self {
165 Self::Dedicated => cur.put_str("d"),
166 Self::Local => cur.put_str("l"),
167 Self::Proxy => cur.put_str("p"),
168 Self::Unknown => cur.put_str("?"),
169 }
170 }
171}
172
173impl fmt::Display for ServerType {
174 fn fmt(&self, fmt: &mut fmt::Formatter) -> fmt::Result {
175 use ServerType as E;
176
177 let s = match self {
178 E::Dedicated => "dedicated",
179 E::Local => "local",
180 E::Proxy => "proxy",
181 E::Unknown => "unknown",
182 };
183
184 write!(fmt, "{}", s)
185 }
186}
187
188#[derive(Copy, Clone, Debug, PartialEq, Eq)]
190#[repr(u8)]
191pub enum Region {
192 USEastCoast = 0x00,
194 USWestCoast = 0x01,
196 SouthAmerica = 0x02,
198 Europe = 0x03,
200 Asia = 0x04,
202 Australia = 0x05,
204 MiddleEast = 0x06,
206 Africa = 0x07,
208 RestOfTheWorld = 0xff,
210}
211
212impl Default for Region {
213 fn default() -> Self {
214 Self::RestOfTheWorld
215 }
216}
217
218impl TryFrom<u8> for Region {
219 type Error = CursorError;
220
221 fn try_from(value: u8) -> Result<Self, Self::Error> {
222 match value {
223 0x00 => Ok(Region::USEastCoast),
224 0x01 => Ok(Region::USWestCoast),
225 0x02 => Ok(Region::SouthAmerica),
226 0x03 => Ok(Region::Europe),
227 0x04 => Ok(Region::Asia),
228 0x05 => Ok(Region::Australia),
229 0x06 => Ok(Region::MiddleEast),
230 0x07 => Ok(Region::Africa),
231 0xff => Ok(Region::RestOfTheWorld),
232 _ => Err(CursorError::InvalidNumber),
233 }
234 }
235}
236
237impl GetKeyValue<'_> for Region {
238 fn get_key_value(cur: &mut Cursor) -> Result<Self, CursorError> {
239 cur.get_key_value::<u8>()?.try_into()
240 }
241}
242
243bitflags! {
244 #[derive(Copy, Clone, Debug, Default, PartialEq, Eq)]
246 pub struct ServerFlags: u8 {
247 const BOTS = 1 << 0;
249 const PASSWORD = 1 << 1;
251 const SECURE = 1 << 2;
253 const LAN = 1 << 3;
255 const NAT = 1 << 4;
257 }
258}
259
260#[derive(Clone, Debug, PartialEq, Default)]
262pub struct ServerAdd<T> {
263 pub gamedir: T,
273 pub map: T,
275 pub version: Version,
277 pub challenge: u32,
279 pub server_type: ServerType,
281 pub os: Os,
283 pub region: Region,
285 pub protocol: u8,
287 pub players: u8,
289 pub max: u8,
291 pub flags: ServerFlags,
293}
294
295impl ServerAdd<()> {
296 pub const HEADER: &'static [u8] = b"0\n";
298}
299
300impl<'a, T> ServerAdd<T>
301where
302 T: 'a + Default + GetKeyValue<'a>,
303{
304 pub fn decode(src: &'a [u8]) -> Result<Self, Error> {
306 trait Helper<'a> {
307 fn get<T: GetKeyValue<'a>>(&mut self, key: &'static str) -> Result<T, Error>;
308 }
309
310 impl<'a> Helper<'a> for Cursor<'a> {
311 fn get<T: GetKeyValue<'a>>(&mut self, key: &'static str) -> Result<T, Error> {
312 T::get_key_value(self).map_err(|e| Error::InvalidServerValue(key, e))
313 }
314 }
315
316 let mut cur = Cursor::new(src);
317 cur.expect(ServerAdd::HEADER)?;
318
319 let mut ret = Self::default();
320 let mut challenge = None;
321 loop {
322 let key = match cur.get_key_raw() {
323 Ok(s) => s,
324 Err(CursorError::TableEnd) => break,
325 Err(e) => Err(e)?,
326 };
327
328 match key {
329 b"protocol" => ret.protocol = cur.get("protocol")?,
330 b"challenge" => challenge = Some(cur.get("challenge")?),
331 b"players" => ret.players = cur.get("players")?,
332 b"max" => ret.max = cur.get("max")?,
333 b"gamedir" => ret.gamedir = cur.get("gamedir")?,
334 b"product" => cur.skip_key_value::<&[u8]>()?, b"map" => ret.map = cur.get("map")?,
336 b"type" => ret.server_type = cur.get("type")?,
337 b"os" => ret.os = cur.get("os")?,
338 b"version" => {
339 ret.version = cur
340 .get_key_value()
341 .map_err(|e| {
342 debug!("invalid server version");
343 e
344 })
345 .unwrap_or_default()
346 }
347 b"region" => ret.region = cur.get("region")?,
348 b"bots" => ret
349 .flags
350 .set(ServerFlags::BOTS, cur.get::<u8>("bots")? != 0),
351 b"password" => ret.flags.set(ServerFlags::PASSWORD, cur.get("password")?),
352 b"secure" => ret.flags.set(ServerFlags::SECURE, cur.get("secure")?),
353 b"lan" => ret.flags.set(ServerFlags::LAN, cur.get("lan")?),
354 b"nat" => ret.flags.set(ServerFlags::NAT, cur.get("nat")?),
355 _ => {
356 let value = cur.get_key_value::<Str<&[u8]>>()?;
358 debug!("Invalid ServerInfo field \"{}\" = \"{}\"", Str(key), value);
359 }
360 }
361 }
362
363 match challenge {
364 Some(c) => {
365 ret.challenge = c;
366 Ok(ret)
367 }
368 None => Err(Error::InvalidServerValue("challenge", CursorError::Expect)),
369 }
370 }
371}
372
373impl<T> ServerAdd<T>
374where
375 T: PutKeyValue,
376{
377 pub fn encode(&self, buf: &mut [u8]) -> Result<usize, Error> {
379 Ok(CursorMut::new(buf)
380 .put_bytes(ServerAdd::HEADER)?
381 .put_key("protocol", self.protocol)?
382 .put_key("challenge", self.challenge)?
383 .put_key("players", self.players)?
384 .put_key("max", self.max)?
385 .put_key("gamedir", &self.gamedir)?
386 .put_key("map", &self.map)?
387 .put_key("type", self.server_type)?
388 .put_key("os", self.os)?
389 .put_key("version", self.version)?
390 .put_key("region", self.region as u8)?
391 .put_key("bots", self.flags.contains(ServerFlags::BOTS))?
392 .put_key("password", self.flags.contains(ServerFlags::PASSWORD))?
393 .put_key("secure", self.flags.contains(ServerFlags::SECURE))?
394 .put_key("lan", self.flags.contains(ServerFlags::LAN))?
395 .put_key("nat", self.flags.contains(ServerFlags::NAT))?
396 .pos())
397 }
398}
399
400#[derive(Clone, Debug, PartialEq)]
402pub struct ServerRemove;
403
404impl ServerRemove {
405 pub const HEADER: &'static [u8] = b"b\n";
407
408 pub fn decode(src: &[u8]) -> Result<Self, Error> {
410 let mut cur = Cursor::new(src);
411 cur.expect(Self::HEADER)?;
412 cur.expect_empty()?;
413 Ok(Self)
414 }
415
416 pub fn encode<const N: usize>(&self, buf: &mut [u8; N]) -> Result<usize, Error> {
418 Ok(CursorMut::new(buf).put_bytes(Self::HEADER)?.pos())
419 }
420}
421
422#[derive(Clone, Debug, PartialEq, Default)]
424pub struct GetServerInfoResponse<T> {
425 pub gamedir: T,
435 pub map: T,
437 pub host: T,
439 pub protocol: u8,
441 pub numcl: u8,
443 pub maxcl: u8,
445 pub dm: bool,
447 pub team: bool,
449 pub coop: bool,
451 pub password: bool,
453 pub dedicated: bool,
455}
456
457impl GetServerInfoResponse<()> {
458 pub const HEADER: &'static [u8] = b"\xff\xff\xff\xffinfo\n";
460}
461
462impl<'a, T> GetServerInfoResponse<T>
463where
464 T: 'a + Default + GetKeyValue<'a>,
465{
466 pub fn decode(src: &'a [u8]) -> Result<Self, Error> {
468 let mut cur = Cursor::new(src);
469 cur.expect(GetServerInfoResponse::HEADER)?;
470
471 if !cur.as_slice().starts_with(b"\\") {
472 let s = cur.get_bytes(cur.remaining())?;
473 let p = s
474 .iter()
475 .rev()
476 .position(|c| *c == b':')
477 .ok_or(Error::InvalidPacket)?;
478 let msg = &s[s.len() - p..];
479 return match msg {
480 b" wrong version\n" => Err(Error::InvalidProtocolVersion),
481 _ => Err(Error::InvalidPacket),
482 };
483 }
484
485 let mut ret = Self::default();
486 loop {
487 let key = match cur.get_key_raw() {
488 Ok(s) => s,
489 Err(CursorError::TableEnd) => break,
490 Err(e) => Err(e)?,
491 };
492
493 match key {
494 b"p" => ret.protocol = cur.get_key_value()?,
495 b"map" => ret.map = cur.get_key_value()?,
496 b"dm" => ret.dm = cur.get_key_value()?,
497 b"team" => ret.team = cur.get_key_value()?,
498 b"coop" => ret.coop = cur.get_key_value()?,
499 b"numcl" => ret.numcl = cur.get_key_value()?,
500 b"maxcl" => ret.maxcl = cur.get_key_value()?,
501 b"gamedir" => ret.gamedir = cur.get_key_value()?,
502 b"password" => ret.password = cur.get_key_value()?,
503 b"host" => ret.host = cur.get_key_value()?,
504 b"dedicated" => ret.dedicated = cur.get_key_value()?,
505 _ => {
506 let value = cur.get_key_value::<Str<&[u8]>>()?;
508 debug!(
509 "Invalid GetServerInfo field \"{}\" = \"{}\"",
510 Str(key),
511 value
512 );
513 }
514 }
515 }
516
517 Ok(ret)
518 }
519}
520
521impl<T> GetServerInfoResponse<T>
522where
523 T: PutKeyValue,
524{
525 pub fn encode(&self, buf: &mut [u8]) -> Result<usize, Error> {
527 Ok(CursorMut::new(buf)
528 .put_bytes(GetServerInfoResponse::HEADER)?
529 .put_key("p", self.protocol)?
530 .put_key("map", &self.map)?
531 .put_key("dm", self.dm)?
532 .put_key("team", self.team)?
533 .put_key("coop", self.coop)?
534 .put_key("numcl", self.numcl)?
535 .put_key("maxcl", self.maxcl)?
536 .put_key("gamedir", &self.gamedir)?
537 .put_key("password", self.password)?
538 .put_key("dedicated", self.dedicated)?
539 .put_key("host", &self.host)?
540 .pos())
541 }
542}
543
544#[derive(Clone, Debug, PartialEq)]
546pub enum Packet<'a> {
547 Challenge(Challenge),
549 ServerAdd(ServerAdd<Str<&'a [u8]>>),
551 ServerRemove,
553 GetServerInfoResponse(GetServerInfoResponse<Str<&'a [u8]>>),
555}
556
557impl<'a> Packet<'a> {
558 pub fn decode(src: &'a [u8]) -> Result<Option<Self>, Error> {
560 if src.starts_with(Challenge::HEADER) {
561 Challenge::decode(src).map(Self::Challenge)
562 } else if src.starts_with(ServerAdd::HEADER) {
563 ServerAdd::decode(src).map(Self::ServerAdd)
564 } else if src.starts_with(ServerRemove::HEADER) {
565 ServerRemove::decode(src).map(|_| Self::ServerRemove)
566 } else if src.starts_with(GetServerInfoResponse::HEADER) {
567 GetServerInfoResponse::decode(src).map(Self::GetServerInfoResponse)
568 } else {
569 return Ok(None);
570 }
571 .map(Some)
572 }
573}
574
575#[cfg(test)]
576mod tests {
577 use super::*;
578
579 #[test]
580 fn challenge() {
581 let p = Challenge::new(Some(0x12345678));
582 let mut buf = [0; 128];
583 let n = p.encode(&mut buf).unwrap();
584 assert_eq!(Packet::decode(&buf[..n]), Ok(Some(Packet::Challenge(p))));
585 }
586
587 #[test]
588 fn challenge_old() {
589 let s = b"q\xff";
590 assert_eq!(
591 Packet::decode(s),
592 Ok(Some(Packet::Challenge(Challenge::new(None))))
593 );
594
595 let p = Challenge::new(None);
596 let mut buf = [0; 128];
597 let n = p.encode(&mut buf).unwrap();
598 assert_eq!(&buf[..n], b"q\xff");
599 }
600
601 #[test]
602 fn server_add() {
603 let p = ServerAdd {
604 gamedir: Str(&b"valve"[..]),
605 map: Str(&b"crossfire"[..]),
606 version: Version::new(0, 20),
607 challenge: 0x12345678,
608 server_type: ServerType::Dedicated,
609 os: Os::Linux,
610 region: Region::RestOfTheWorld,
611 protocol: 49,
612 players: 4,
613 max: 32,
614 flags: ServerFlags::all(),
615 };
616 let mut buf = [0; 512];
617 let n = p.encode(&mut buf).unwrap();
618 assert_eq!(Packet::decode(&buf[..n]), Ok(Some(Packet::ServerAdd(p))));
619 }
620
621 #[test]
622 fn server_remove() {
623 let p = ServerRemove;
624 let mut buf = [0; 64];
625 let n = p.encode(&mut buf).unwrap();
626 assert_eq!(Packet::decode(&buf[..n]), Ok(Some(Packet::ServerRemove)));
627 }
628
629 #[test]
630 fn get_server_info_response() {
631 let p = GetServerInfoResponse {
632 protocol: 49,
633 map: Str("crossfire".as_bytes()),
634 dm: true,
635 team: true,
636 coop: true,
637 numcl: 4,
638 maxcl: 32,
639 gamedir: Str("valve".as_bytes()),
640 password: true,
641 dedicated: true,
642 host: Str("Test".as_bytes()),
643 };
644 let mut buf = [0; 512];
645 let n = p.encode(&mut buf).unwrap();
646 assert_eq!(
647 Packet::decode(&buf[..n]),
648 Ok(Some(Packet::GetServerInfoResponse(p)))
649 );
650 }
651
652 #[test]
653 fn get_server_info_response_wrong_version() {
654 let s = b"\xff\xff\xff\xffinfo\nfoobar: wrong version\n";
655 assert_eq!(Packet::decode(s), Err(Error::InvalidProtocolVersion));
656
657 let s = b"\xff\xff\xff\xffinfo\nfoobar\xff: wrong version\n";
658 assert_eq!(Packet::decode(s), Err(Error::InvalidProtocolVersion));
659 }
660
661 #[test]
662 fn server_add_bots_is_a_number() {
663 let s = b"0\n\\protocol\\48\\challenge\\4161802725\\players\\0\\max\\32\\bots\\3\\gamedir\\valve\\map\\rats_bathroom\\type\\d\\password\\0\\os\\l\\secure\\0\\lan\\0\\version\\0.19.4\\region\\255\\product\\valve\\nat\\0";
664 ServerAdd::<&[u8]>::decode(s).unwrap();
665 }
666
667 #[test]
668 fn server_add_legacy() {
669 let s = b"0\n\\protocol\\48\\challenge\\1680337211\\players\\1\\max\\8\\bots\\0\\gamedir\\cstrike\\map\\cs_assault\\type\\d\\password\\0\\os\\l\\secure\\0\\lan\\0\\version\\0.17.1\\region\\255\\product\\cstrike\n";
670 ServerAdd::<&[u8]>::decode(s).unwrap();
671 }
672}