|
@@ -3,6 +3,7 @@ use crate::crypto::Ocb2Aes128Crypto;
|
|
|
use crate::protocol::connection::{AudioChannel, AudioChannelStats, Error};
|
|
|
use crate::protocol::parser::AudioPacket;
|
|
|
use async_trait::async_trait;
|
|
|
+use std::convert::TryInto;
|
|
|
use std::net::{IpAddr, SocketAddr};
|
|
|
use std::sync::atomic::{AtomicU32, Ordering};
|
|
|
use std::sync::Arc;
|
|
@@ -15,6 +16,8 @@ use tokio::task::JoinHandle;
|
|
|
const MAX_AUDIO_PACKET_SIZE: usize = 1020;
|
|
|
const ENCRYPTION_OVERHEAD: usize = 4;
|
|
|
const MAX_DATAGRAM_SIZE: usize = MAX_AUDIO_PACKET_SIZE + ENCRYPTION_OVERHEAD;
|
|
|
+const INFO_PING_SIZE: usize = 12;
|
|
|
+const RESPONSE_SIZE: usize = 4 + 8 + 4 + 4 + 4;
|
|
|
|
|
|
type Data = Arc<(Vec<u8>, SocketAddr)>;
|
|
|
|
|
@@ -24,6 +27,13 @@ pub struct UdpWorker {
|
|
|
task: JoinHandle<()>,
|
|
|
}
|
|
|
|
|
|
+pub struct ServerInfo {
|
|
|
+ pub version: u32,
|
|
|
+ pub connected_users: Arc<AtomicU32>,
|
|
|
+ pub max_users: u32,
|
|
|
+ pub max_bandwidth: u32,
|
|
|
+}
|
|
|
+
|
|
|
pub struct UdpAudioChannel {
|
|
|
good: AtomicU32,
|
|
|
late: AtomicU32,
|
|
@@ -36,7 +46,7 @@ pub struct UdpAudioChannel {
|
|
|
}
|
|
|
|
|
|
impl UdpWorker {
|
|
|
- pub async fn start(socket: UdpSocket) -> Self {
|
|
|
+ pub async fn start(socket: UdpSocket, info: ServerInfo) -> Self {
|
|
|
let (sender, _) = broadcast::channel(8);
|
|
|
let socket = Arc::new(socket);
|
|
|
let udp_socket = Arc::clone(&socket);
|
|
@@ -44,8 +54,18 @@ impl UdpWorker {
|
|
|
let task = tokio::spawn(async move {
|
|
|
let mut buf = [0; MAX_DATAGRAM_SIZE];
|
|
|
loop {
|
|
|
- if let Ok((len, socket_address)) = udp_socket.recv_from(&mut buf).await {
|
|
|
- broadcast_sender.send(Arc::new((Vec::from(&buf[..len]), socket_address)));
|
|
|
+ if let Ok((len, address)) = udp_socket.recv_from(&mut buf).await {
|
|
|
+ if len == INFO_PING_SIZE {
|
|
|
+ Self::response_to_ping(
|
|
|
+ &buf[..12].try_into().unwrap(),
|
|
|
+ &udp_socket,
|
|
|
+ address,
|
|
|
+ &info,
|
|
|
+ )
|
|
|
+ .await;
|
|
|
+ } else {
|
|
|
+ broadcast_sender.send(Arc::new((Vec::from(&buf[..len]), address)));
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
});
|
|
@@ -84,6 +104,27 @@ impl UdpWorker {
|
|
|
}
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+ async fn response_to_ping(
|
|
|
+ ping: &[u8; INFO_PING_SIZE],
|
|
|
+ socket: &UdpSocket,
|
|
|
+ origin: SocketAddr,
|
|
|
+ info: &ServerInfo,
|
|
|
+ ) {
|
|
|
+ let bytes = Self::create_response(ping, info);
|
|
|
+ socket.send_to(&bytes, origin).await;
|
|
|
+ }
|
|
|
+
|
|
|
+ fn create_response(ping: &[u8; INFO_PING_SIZE], info: &ServerInfo) -> [u8; RESPONSE_SIZE] {
|
|
|
+ let mut response = [0u8; RESPONSE_SIZE];
|
|
|
+ response[..4].copy_from_slice(&info.version.to_be_bytes());
|
|
|
+ response[4..12].copy_from_slice(&ping[4..]);
|
|
|
+ response[12..16]
|
|
|
+ .copy_from_slice(&info.connected_users.load(Ordering::Acquire).to_be_bytes());
|
|
|
+ response[16..20].copy_from_slice(&info.max_users.to_be_bytes());
|
|
|
+ response[20..24].copy_from_slice(&info.max_bandwidth.to_be_bytes());
|
|
|
+ response
|
|
|
+ }
|
|
|
}
|
|
|
|
|
|
#[async_trait]
|
|
@@ -149,3 +190,25 @@ impl From<crypto::Error> for Error {
|
|
|
))
|
|
|
}
|
|
|
}
|
|
|
+
|
|
|
+#[cfg(test)]
|
|
|
+mod tests {
|
|
|
+ use super::*;
|
|
|
+
|
|
|
+ #[test]
|
|
|
+ fn test_create_response() {
|
|
|
+ let ping = [0, 0, 0, 0, 1, 2, 3, 4, 5, 6, 7, 0];
|
|
|
+ let info = ServerInfo {
|
|
|
+ version: 0x0123,
|
|
|
+ connected_users: Arc::new(AtomicU32::from(42)),
|
|
|
+ max_users: 100,
|
|
|
+ max_bandwidth: 100000,
|
|
|
+ };
|
|
|
+
|
|
|
+ let expected: [u8; RESPONSE_SIZE] = [
|
|
|
+ 0, 0, 1, 35, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 42, 0, 0, 0, 100, 0, 1, 134, 160,
|
|
|
+ ];
|
|
|
+ let response = UdpWorker::create_response(&ping, &info);
|
|
|
+ assert_eq!(response, expected);
|
|
|
+ }
|
|
|
+}
|