You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
261 lines
7.2 KiB
261 lines
7.2 KiB
// SPDX-License-Identifier: GPL-3.0-only |
|
// SPDX-FileCopyrightText: 2023 Denis Drakhnia <numas13@gmail.com> |
|
|
|
use std::net::{Ipv4Addr, SocketAddrV4}; |
|
|
|
use super::cursor::{Cursor, CursorMut}; |
|
use super::Error; |
|
|
|
#[derive(Clone, Debug, PartialEq)] |
|
pub struct ChallengeResponse { |
|
pub master_challenge: u32, |
|
pub server_challenge: u32, |
|
} |
|
|
|
impl ChallengeResponse { |
|
pub const HEADER: &'static [u8] = b"\xff\xff\xff\xffs\n"; |
|
|
|
pub fn new(master_challenge: u32, server_challenge: u32) -> Self { |
|
Self { |
|
master_challenge, |
|
server_challenge, |
|
} |
|
} |
|
|
|
pub fn decode(src: &[u8]) -> Result<Self, Error> { |
|
let mut cur = Cursor::new(src); |
|
cur.expect(Self::HEADER)?; |
|
let master_challenge = cur.get_u32_le()?; |
|
let server_challenge = cur.get_u32_le()?; |
|
cur.expect_empty()?; |
|
Ok(Self { |
|
master_challenge, |
|
server_challenge, |
|
}) |
|
} |
|
|
|
pub fn encode<const N: usize>(&self, buf: &mut [u8; N]) -> Result<usize, Error> { |
|
Ok(CursorMut::new(buf) |
|
.put_bytes(Self::HEADER)? |
|
.put_u32_le(self.master_challenge)? |
|
.put_u32_le(self.server_challenge)? |
|
.pos()) |
|
} |
|
} |
|
|
|
#[derive(Clone, Debug, PartialEq)] |
|
pub struct QueryServersResponse<I> { |
|
inner: I, |
|
pub key: Option<u32>, |
|
} |
|
|
|
impl QueryServersResponse<()> { |
|
pub const HEADER: &'static [u8] = b"\xff\xff\xff\xfff\n"; |
|
} |
|
|
|
impl<'a> QueryServersResponse<&'a [u8]> { |
|
pub fn decode(src: &'a [u8]) -> Result<Self, Error> { |
|
let mut cur = Cursor::new(src); |
|
cur.expect(QueryServersResponse::HEADER)?; |
|
if cur.remaining() % 6 != 0 { |
|
return Err(Error::InvalidPacket); |
|
} |
|
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[5] == 8 { |
|
(&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]) { |
|
&s[..s.len() - 6] |
|
} else { |
|
s |
|
}; |
|
Ok(Self { inner, key }) |
|
} |
|
|
|
pub fn iter(&self) -> impl 'a + Iterator<Item = SocketAddrV4> { |
|
let mut cur = Cursor::new(self.inner); |
|
(0..self.inner.len() / 6).map(move |_| { |
|
let ip = Ipv4Addr::from(cur.get_array().unwrap()); |
|
let port = cur.get_u16_be().unwrap(); |
|
SocketAddrV4::new(ip, port) |
|
}) |
|
} |
|
|
|
pub fn is_empty(&self) -> bool { |
|
self.inner.is_empty() |
|
} |
|
} |
|
|
|
impl<I> QueryServersResponse<I> |
|
where |
|
I: Iterator<Item = SocketAddrV4>, |
|
{ |
|
pub fn new(iter: I, key: Option<u32>) -> Self { |
|
Self { inner: iter, key } |
|
} |
|
|
|
pub fn encode(&mut self, buf: &mut [u8]) -> Result<(usize, bool), Error> { |
|
let mut cur = CursorMut::new(buf); |
|
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; |
|
while cur.remaining() >= 12 { |
|
match self.inner.next() { |
|
Some(i) => { |
|
cur.put_array(&i.ip().octets())?.put_u16_be(i.port())?; |
|
} |
|
None => { |
|
is_end = true; |
|
break; |
|
} |
|
} |
|
} |
|
Ok((cur.put_array(&[0; 6])?.pos(), is_end)) |
|
} |
|
} |
|
|
|
#[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<Self, Error> { |
|
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<usize, Error> { |
|
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, |
|
pub hash_challenge: u32, |
|
} |
|
|
|
impl AdminChallengeResponse { |
|
pub const HEADER: &'static [u8] = b"\xff\xff\xff\xffadminchallenge"; |
|
|
|
pub fn new(master_challenge: u32, hash_challenge: u32) -> Self { |
|
Self { |
|
master_challenge, |
|
hash_challenge, |
|
} |
|
} |
|
|
|
pub fn decode(src: &[u8]) -> Result<Self, Error> { |
|
let mut cur = Cursor::new(src); |
|
cur.expect(Self::HEADER)?; |
|
let master_challenge = cur.get_u32_le()?; |
|
let hash_challenge = cur.get_u32_le()?; |
|
cur.expect_empty()?; |
|
Ok(Self { |
|
master_challenge, |
|
hash_challenge, |
|
}) |
|
} |
|
|
|
pub fn encode(&self, buf: &mut [u8]) -> Result<usize, Error> { |
|
Ok(CursorMut::new(buf) |
|
.put_bytes(Self::HEADER)? |
|
.put_u32_le(self.master_challenge)? |
|
.put_u32_le(self.hash_challenge)? |
|
.pos()) |
|
} |
|
} |
|
|
|
#[derive(Clone, Debug, PartialEq)] |
|
pub enum Packet<'a> { |
|
ChallengeResponse(ChallengeResponse), |
|
QueryServersResponse(QueryServersResponse<&'a [u8]>), |
|
AdminChallengeResponse(AdminChallengeResponse), |
|
} |
|
|
|
impl<'a> Packet<'a> { |
|
pub fn decode(src: &'a [u8]) -> Result<Self, Error> { |
|
if let Ok(p) = ChallengeResponse::decode(src) { |
|
return Ok(Self::ChallengeResponse(p)); |
|
} |
|
|
|
if let Ok(p) = QueryServersResponse::decode(src) { |
|
return Ok(Self::QueryServersResponse(p)); |
|
} |
|
|
|
if let Ok(p) = AdminChallengeResponse::decode(src) { |
|
return Ok(Self::AdminChallengeResponse(p)); |
|
} |
|
|
|
Err(Error::InvalidPacket) |
|
} |
|
} |
|
|
|
#[cfg(test)] |
|
mod tests { |
|
use super::*; |
|
|
|
#[test] |
|
fn challenge_response() { |
|
let p = ChallengeResponse::new(0x12345678, 0x87654321); |
|
let mut buf = [0; 512]; |
|
let n = p.encode(&mut buf).unwrap(); |
|
assert_eq!(ChallengeResponse::decode(&buf[..n]), Ok(p)); |
|
} |
|
|
|
#[test] |
|
fn query_servers_response() { |
|
let servers: &[SocketAddrV4] = &[ |
|
"1.2.3.4:27001".parse().unwrap(), |
|
"1.2.3.4:27002".parse().unwrap(), |
|
"1.2.3.4:27003".parse().unwrap(), |
|
"1.2.3.4:27004".parse().unwrap(), |
|
]; |
|
let mut p = QueryServersResponse::new(servers.iter().cloned(), Some(0xdeadbeef)); |
|
let mut buf = [0; 512]; |
|
let (n, _) = p.encode(&mut buf).unwrap(); |
|
let e = QueryServersResponse::decode(&buf[..n]).unwrap(); |
|
assert_eq!(e.iter().collect::<Vec<_>>(), 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); |
|
let mut buf = [0; 64]; |
|
let n = p.encode(&mut buf).unwrap(); |
|
assert_eq!(AdminChallengeResponse::decode(&buf[..n]), Ok(p)); |
|
} |
|
}
|
|
|