Browse Source

protocol: add challenge key to game requests

ipv6
Denis Drakhnia 1 year ago
parent
commit
e6b1d866eb
  1. 1
      Cargo.lock
  2. 8
      master/src/master_server.rs
  3. 11
      protocol/src/filter.rs
  4. 1
      protocol/src/game.rs
  5. 22
      protocol/src/master.rs
  6. 1
      query/Cargo.toml
  7. 9
      query/src/cli.rs
  8. 54
      query/src/main.rs

1
Cargo.lock generated

@ -509,6 +509,7 @@ dependencies = [
name = "xash3d-query" name = "xash3d-query"
version = "0.1.0" version = "0.1.0"
dependencies = [ dependencies = [
"fastrand",
"getopts", "getopts",
"libc", "libc",
"serde", "serde",

8
master/src/master_server.rs

@ -231,7 +231,7 @@ impl MasterServer {
trace!("{}: recv {:?}", from, p); trace!("{}: recv {:?}", from, p);
if p.filter.clver.map_or(false, |v| v < self.clver) { if p.filter.clver.map_or(false, |v| v < self.clver) {
let iter = std::iter::once(self.update_addr); let iter = std::iter::once(self.update_addr);
self.send_server_list(from, iter)?; self.send_server_list(from, p.filter.key, iter)?;
} else { } else {
let now = self.now(); let now = self.now();
let iter = self let iter = self
@ -240,7 +240,7 @@ impl MasterServer {
.filter(|i| i.1.is_valid(now, self.timeout.server)) .filter(|i| i.1.is_valid(now, self.timeout.server))
.filter(|i| i.1.matches(*i.0, p.region, &p.filter)) .filter(|i| i.1.matches(*i.0, p.region, &p.filter))
.map(|i| *i.0); .map(|i| *i.0);
self.send_server_list(from, iter)?; self.send_server_list(from, p.filter.key, iter)?;
} }
} }
game::Packet::GetServerInfo(p) => { game::Packet::GetServerInfo(p) => {
@ -399,12 +399,12 @@ impl MasterServer {
} }
} }
fn send_server_list<A, I>(&self, to: A, iter: I) -> Result<(), Error> fn send_server_list<A, I>(&self, to: A, key: Option<u32>, iter: I) -> Result<(), Error>
where where
A: ToSocketAddrs, A: ToSocketAddrs,
I: Iterator<Item = SocketAddrV4>, I: Iterator<Item = SocketAddrV4>,
{ {
let mut list = master::QueryServersResponse::new(iter); let mut list = master::QueryServersResponse::new(iter, key);
loop { loop {
let mut buf = [0; MAX_PACKET_SIZE]; let mut buf = [0; MAX_PACKET_SIZE];
let (n, is_end) = list.encode(&mut buf)?; let (n, is_end) = list.encode(&mut buf)?;

11
protocol/src/filter.rs

@ -141,6 +141,7 @@ pub struct Filter<'a> {
pub map: Option<Str<&'a [u8]>>, pub map: Option<Str<&'a [u8]>>,
/// Client version. /// Client version.
pub clver: Option<Version>, pub clver: Option<Version>,
pub key: Option<u32>,
pub flags: FilterFlags, pub flags: FilterFlags,
pub flags_mask: FilterFlags, pub flags_mask: FilterFlags,
@ -192,6 +193,13 @@ impl<'a> TryFrom<&'a [u8]> for Filter<'a> {
b"nat" => filter.insert_flag(FilterFlags::NAT, cur.get_key_value()?), b"nat" => filter.insert_flag(FilterFlags::NAT, cur.get_key_value()?),
b"lan" => filter.insert_flag(FilterFlags::LAN, cur.get_key_value()?), b"lan" => filter.insert_flag(FilterFlags::LAN, cur.get_key_value()?),
b"bots" => filter.insert_flag(FilterFlags::BOTS, cur.get_key_value()?), b"bots" => filter.insert_flag(FilterFlags::BOTS, cur.get_key_value()?),
b"key" => {
filter.key = {
let s = cur.get_key_value::<&str>()?;
let x = u32::from_str_radix(s, 16).map_err(|_| Error::InvalidPacket)?;
Some(x)
}
}
_ => { _ => {
// skip unknown fields // skip unknown fields
let value = Str(cur.get_key_value_raw()?); let value = Str(cur.get_key_value_raw()?);
@ -234,6 +242,9 @@ impl fmt::Display for &Filter<'_> {
display_flag!("nat", FilterFlags::NAT); display_flag!("nat", FilterFlags::NAT);
display_flag!("lan", FilterFlags::LAN); display_flag!("lan", FilterFlags::LAN);
display_flag!("bots", FilterFlags::BOTS); display_flag!("bots", FilterFlags::BOTS);
if let Some(x) = self.key {
write!(fmt, "\\key\\{:x}", x)?;
}
Ok(()) Ok(())
} }

1
protocol/src/game.rs

@ -120,6 +120,7 @@ mod tests {
filter: Filter { filter: Filter {
gamedir: Some(Str(&b"valve"[..])), gamedir: Some(Str(&b"valve"[..])),
map: Some(Str(&b"crossfire"[..])), map: Some(Str(&b"crossfire"[..])),
key: Some(0xdeadbeef),
clver: Some(Version::new(0, 20)), clver: Some(Version::new(0, 20)),
flags: FilterFlags::all(), flags: FilterFlags::all(),
flags_mask: FilterFlags::all(), flags_mask: FilterFlags::all(),

22
protocol/src/master.rs

@ -46,6 +46,7 @@ impl ChallengeResponse {
#[derive(Clone, Debug, PartialEq)] #[derive(Clone, Debug, PartialEq)]
pub struct QueryServersResponse<I> { pub struct QueryServersResponse<I> {
inner: I, inner: I,
pub key: Option<u32>,
} }
impl QueryServersResponse<()> { impl QueryServersResponse<()> {
@ -60,12 +61,20 @@ impl<'a> QueryServersResponse<&'a [u8]> {
return Err(Error::InvalidPacket); return Err(Error::InvalidPacket);
} }
let s = cur.get_bytes(cur.remaining())?; let s = cur.get_bytes(cur.remaining())?;
// extra header for key sent in QueryServers packet
let (s, key) = if s.len() >= 6 && s[0] == 0x7f {
(&s[6..], Some(u32::from_le_bytes([s[1], s[2], s[3], s[4]])))
} else {
(s, None)
};
let inner = if s.ends_with(&[0; 6]) { let inner = if s.ends_with(&[0; 6]) {
&s[..s.len() - 6] &s[..s.len() - 6]
} else { } else {
s s
}; };
Ok(Self { inner }) Ok(Self { inner, key })
} }
pub fn iter(&self) -> impl 'a + Iterator<Item = SocketAddrV4> { pub fn iter(&self) -> impl 'a + Iterator<Item = SocketAddrV4> {
@ -86,13 +95,18 @@ impl<I> QueryServersResponse<I>
where where
I: Iterator<Item = SocketAddrV4>, I: Iterator<Item = SocketAddrV4>,
{ {
pub fn new(iter: I) -> Self { pub fn new(iter: I, key: Option<u32>) -> Self {
Self { inner: iter } Self { inner: iter, key }
} }
pub fn encode(&mut self, buf: &mut [u8]) -> Result<(usize, bool), Error> { pub fn encode(&mut self, buf: &mut [u8]) -> Result<(usize, bool), Error> {
let mut cur = CursorMut::new(buf); let mut cur = CursorMut::new(buf);
cur.put_bytes(QueryServersResponse::HEADER)?; cur.put_bytes(QueryServersResponse::HEADER)?;
if let Some(key) = self.key {
cur.put_u8(0x7f)?;
cur.put_u32_le(key)?;
cur.put_u8(8)?;
}
let mut is_end = false; let mut is_end = false;
while cur.remaining() >= 12 { while cur.remaining() >= 12 {
match self.inner.next() { match self.inner.next() {
@ -191,7 +205,7 @@ mod tests {
"1.2.3.4:27003".parse().unwrap(), "1.2.3.4:27003".parse().unwrap(),
"1.2.3.4:27004".parse().unwrap(), "1.2.3.4:27004".parse().unwrap(),
]; ];
let mut p = QueryServersResponse::new(servers.iter().cloned()); let mut p = QueryServersResponse::new(servers.iter().cloned(), Some(0xdeadbeef));
let mut buf = [0; 512]; let mut buf = [0; 512];
let (n, _) = p.encode(&mut buf).unwrap(); let (n, _) = p.encode(&mut buf).unwrap();
let e = QueryServersResponse::decode(&buf[..n]).unwrap(); let e = QueryServersResponse::decode(&buf[..n]).unwrap();

1
query/Cargo.toml

@ -14,6 +14,7 @@ color = ["termion"]
libc = "0.2.148" libc = "0.2.148"
thiserror = "1.0.49" thiserror = "1.0.49"
getopts = "0.2.21" getopts = "0.2.21"
fastrand = "2.0.1"
serde = { version = "1.0.188", features = ["derive"] } serde = { version = "1.0.188", features = ["derive"] }
serde_json = "1.0.107" serde_json = "1.0.107"
termion = { version = "2", optional = true } termion = { version = "2", optional = true }

9
query/src/cli.rs

@ -25,6 +25,7 @@ pub struct Cli {
pub debug: bool, pub debug: bool,
pub force_color: bool, pub force_color: bool,
pub filter: String, pub filter: String,
pub key: Option<u32>,
} }
impl Default for Cli { impl Default for Cli {
@ -43,6 +44,7 @@ impl Default for Cli {
force_color: false, force_color: false,
// if changed do not forget to update cli parsing // if changed do not forget to update cli parsing
filter: format!("\\gamedir\\valve\\clver\\{}", proto::CLIENT_VERSION), filter: format!("\\gamedir\\valve\\clver\\{}", proto::CLIENT_VERSION),
key: None,
} }
} }
} }
@ -99,6 +101,7 @@ pub fn parse() -> Cli {
opts.optflag("j", "json", "output JSON"); opts.optflag("j", "json", "output JSON");
opts.optflag("d", "debug", "output debug"); opts.optflag("d", "debug", "output debug");
opts.optflag("F", "force-color", "force colored output"); opts.optflag("F", "force-color", "force colored output");
opts.optflag("k", "key", "send challenge key to master");
let help = format!("query filter [default: {:?}]", cli.filter); let help = format!("query filter [default: {:?}]", cli.filter);
opts.optopt("f", "filter", &help, "FILTER"); opts.optopt("f", "filter", &help, "FILTER");
@ -180,6 +183,12 @@ pub fn parse() -> Cli {
cli.filter = filter; cli.filter = filter;
} }
if matches.opt_present("key") {
let key = fastrand::u32(..);
cli.key = Some(key);
cli.filter.push_str(&format!("\\key\\{:x}", key));
}
cli.json = matches.opt_present("json"); cli.json = matches.opt_present("json");
cli.debug = matches.opt_present("debug"); cli.debug = matches.opt_present("debug");
cli.force_color = matches.opt_present("force-color"); cli.force_color = matches.opt_present("force-color");

54
query/src/main.rs

@ -36,8 +36,13 @@ enum ServerResultKind {
#[serde(flatten)] #[serde(flatten)]
info: ServerInfo, info: ServerInfo,
}, },
Error { message: String }, Error {
Invalid { message: String, response: String }, message: String,
},
Invalid {
message: String,
response: String,
},
Timeout, Timeout,
Protocol, Protocol,
} }
@ -298,7 +303,9 @@ impl<'a> Scan<'a> {
if self.is_master(&from) { if self.is_master(&from) {
if let Ok(packet) = master::QueryServersResponse::decode(&buf[..n]) { if let Ok(packet) = master::QueryServersResponse::decode(&buf[..n]) {
set.extend(packet.iter()); if self.check_key(&from, packet.key) {
set.extend(packet.iter());
}
} else { } else {
eprintln!("warn: invalid packet from master {}", from); eprintln!("warn: invalid packet from master {}", from);
} }
@ -367,19 +374,22 @@ impl<'a> Scan<'a> {
if self.is_master(&from) { if self.is_master(&from) {
if let Ok(packet) = master::QueryServersResponse::decode(raw) { if let Ok(packet) = master::QueryServersResponse::decode(raw) {
for addr in packet.iter().filter(|i| set.insert(*i)) { if self.check_key(&from, packet.key) {
let mut buf = [0; 512]; for addr in packet.iter().filter(|i| set.insert(*i)) {
let n = game::GetServerInfo::new(self.cli.protocol[0]).encode(&mut buf)?; let mut buf = [0; 512];
let n =
match self.sock.send_to(&buf[..n], addr) { game::GetServerInfo::new(self.cli.protocol[0]).encode(&mut buf)?;
Ok(_) => {
let query = ServerQuery::new(0); match self.sock.send_to(&buf[..n], addr) {
server_end = query.start + server_timeout; Ok(_) => {
active.insert(addr, query); let query = ServerQuery::new(0);
} server_end = query.start + server_timeout;
Err(e) => { active.insert(addr, query);
let res = ServerResult::error(addr, e); }
out.insert(addr, res); Err(e) => {
let res = ServerResult::error(addr, e);
out.insert(addr, res);
}
} }
} }
} }
@ -426,6 +436,18 @@ impl<'a> Scan<'a> {
Ok(out) Ok(out)
} }
fn check_key(&self, from: &SocketAddrV4, key: Option<u32>) -> bool {
let res = match (self.cli.key, key) {
(Some(a), Some(b)) => a == b,
(None, None) => true,
_ => false,
};
if !res {
eprintln!("error: invalid key from master({})", from);
}
res
}
} }
fn query_server_info(cli: &Cli, servers: &[String]) -> Result<(), Error> { fn query_server_info(cli: &Cli, servers: &[String]) -> Result<(), Error> {

Loading…
Cancel
Save