diff --git a/master/src/master_server.rs b/master/src/master_server.rs index bbf604b..9820b2b 100644 --- a/master/src/master_server.rs +++ b/master/src/master_server.rs @@ -11,7 +11,7 @@ use blake2b_simd::Params; use fastrand::Rng; use log::{error, info, trace, warn}; use thiserror::Error; -use xash3d_protocol::filter::{Filter, Version}; +use xash3d_protocol::filter::{Filter, FilterFlags, Version}; use xash3d_protocol::server::Region; use xash3d_protocol::{admin, game, master, server, Error as ProtocolError, ServerInfo}; @@ -240,7 +240,12 @@ impl MasterServer { .filter(|i| i.1.is_valid(now, self.timeout.server)) .filter(|i| i.1.matches(*i.0, p.region, &p.filter)) .map(|i| *i.0); - self.send_server_list(from, p.filter.key, iter)?; + + self.send_server_list(from, p.filter.key, iter.clone())?; + + if p.filter.flags.contains(FilterFlags::NAT) { + self.send_client_to_nat_servers(from, iter)?; + } } } game::Packet::GetServerInfo(p) => { @@ -416,6 +421,19 @@ impl MasterServer { Ok(()) } + fn send_client_to_nat_servers(&self, to: SocketAddrV4, iter: I) -> Result<(), Error> + where + I: Iterator, + { + let mut buf = [0; 64]; + let n = master::ClientAnnounce::new(to).encode(&mut buf)?; + let buf = &buf[..n]; + for i in iter { + self.sock.send_to(buf, i)?; + } + Ok(()) + } + #[inline] fn is_blocked(&self, ip: &Ipv4Addr) -> bool { self.blocklist.contains(ip) diff --git a/protocol/src/master.rs b/protocol/src/master.rs index d5bdac2..3152eaa 100644 --- a/protocol/src/master.rs +++ b/protocol/src/master.rs @@ -123,6 +123,37 @@ where } } +#[derive(Clone, Debug, PartialEq)] +pub struct ClientAnnounce { + pub addr: SocketAddrV4, +} + +impl ClientAnnounce { + pub const HEADER: &'static [u8] = b"\xff\xff\xff\xffc "; + + pub fn new(addr: SocketAddrV4) -> Self { + Self { addr } + } + + pub fn decode(src: &[u8]) -> Result { + let mut cur = Cursor::new(src); + cur.expect(Self::HEADER)?; + let addr = cur + .get_str(cur.remaining())? + .parse() + .map_err(|_| Error::InvalidPacket)?; + cur.expect_empty()?; + Ok(Self { addr }) + } + + pub fn encode(&self, buf: &mut [u8]) -> Result { + Ok(CursorMut::new(buf) + .put_bytes(Self::HEADER)? + .put_as_str(self.addr)? + .pos()) + } +} + #[derive(Clone, Debug, PartialEq)] pub struct AdminChallengeResponse { pub master_challenge: u32, @@ -212,6 +243,14 @@ mod tests { assert_eq!(e.iter().collect::>(), servers); } + #[test] + fn client_announce() { + let p = ClientAnnounce::new("1.2.3.4:12345".parse().unwrap()); + let mut buf = [0; 512]; + let n = p.encode(&mut buf).unwrap(); + assert_eq!(ClientAnnounce::decode(&buf[..n]), Ok(p)); + } + #[test] fn admin_challenge_response() { let p = AdminChallengeResponse::new(0x12345678, 0x87654321);