185 lines
6.0 KiB
Rust
185 lines
6.0 KiB
Rust
use std::{net::SocketAddr, sync::atomic::Ordering};
|
|
|
|
use packet::{ClientboundPacket, Packet, PacketError, ServerboundPacket};
|
|
use pumpkin_config::RCONConfig;
|
|
use std::sync::Arc;
|
|
use tokio::{
|
|
io::{AsyncReadExt, AsyncWriteExt},
|
|
select,
|
|
};
|
|
use tracing::{debug, error, info};
|
|
|
|
use crate::command::CommandSender;
|
|
use crate::{SHOULD_STOP, STOP_INTERRUPT, server::Server};
|
|
|
|
pub mod packet;
|
|
|
|
pub struct RCONServer;
|
|
|
|
impl RCONServer {
|
|
pub async fn run(config: &RCONConfig, server: Arc<Server>) {
|
|
let listener = tokio::net::TcpListener::bind(config.address).await.unwrap();
|
|
|
|
let password = Arc::new(config.password.clone());
|
|
|
|
let mut connections = 0;
|
|
while !SHOULD_STOP.load(Ordering::Relaxed) {
|
|
let await_new_client = || async {
|
|
let t1 = listener.accept();
|
|
let t2 = STOP_INTERRUPT.cancelled();
|
|
|
|
select! {
|
|
client = t1 => Some(client),
|
|
() = t2 => None,
|
|
}
|
|
};
|
|
// Asynchronously wait for an inbound socket.
|
|
|
|
let Some(Ok((connection, address))) = await_new_client().await else {
|
|
break;
|
|
};
|
|
|
|
if config.max_connections != 0 && connections >= config.max_connections {
|
|
continue;
|
|
}
|
|
|
|
connections += 1;
|
|
let mut client = RCONClient::new(connection, address);
|
|
|
|
let password = password.clone();
|
|
let server = server.clone();
|
|
tokio::spawn(async move { while !client.handle(&server, &password).await {} });
|
|
debug!("closed RCON connection");
|
|
connections -= 1;
|
|
}
|
|
}
|
|
}
|
|
|
|
pub struct RCONClient {
|
|
connection: tokio::net::TcpStream,
|
|
address: SocketAddr,
|
|
logged_in: bool,
|
|
incoming: Vec<u8>,
|
|
closed: bool,
|
|
}
|
|
|
|
impl RCONClient {
|
|
#[must_use]
|
|
pub const fn new(connection: tokio::net::TcpStream, address: SocketAddr) -> Self {
|
|
Self {
|
|
connection,
|
|
address,
|
|
logged_in: false,
|
|
incoming: Vec::new(),
|
|
closed: false,
|
|
}
|
|
}
|
|
|
|
/// Returns whether the client is closed or not.
|
|
pub async fn handle(&mut self, server: &Arc<Server>, password: &str) -> bool {
|
|
if !self.closed {
|
|
match self.read_bytes().await {
|
|
// The stream is closed, so we can't reply, so we just close everything.
|
|
Ok(true) => return true,
|
|
Ok(false) => {}
|
|
Err(e) => {
|
|
error!("Could not read packet: {e}");
|
|
return true;
|
|
}
|
|
}
|
|
// If we get a close here, we might have a reply, which we still want to write.
|
|
let _ = self.poll(server, password).await.map_err(|e| {
|
|
error!("RCON error: {e}");
|
|
self.closed = true;
|
|
});
|
|
}
|
|
self.closed
|
|
}
|
|
|
|
async fn poll(&mut self, server: &Arc<Server>, password: &str) -> Result<(), PacketError> {
|
|
let Some(packet) = self.receive_packet()? else {
|
|
return Ok(());
|
|
};
|
|
let config = &server.advanced_config.networking.rcon;
|
|
match packet.get_type() {
|
|
ServerboundPacket::Auth => {
|
|
if packet.get_body() == password {
|
|
self.send(ClientboundPacket::AuthResponse, packet.get_id(), "")
|
|
.await?;
|
|
if config.logging.logged_successfully {
|
|
info!("RCON ({}): Client logged in successfully", self.address);
|
|
}
|
|
self.logged_in = true;
|
|
} else {
|
|
if config.logging.wrong_password {
|
|
info!("RCON ({}): Client tried the wrong password", self.address);
|
|
}
|
|
self.send(ClientboundPacket::AuthResponse, -1, "").await?;
|
|
self.closed = true;
|
|
}
|
|
}
|
|
ServerboundPacket::ExecCommand => {
|
|
if self.logged_in {
|
|
let output = Arc::new(tokio::sync::Mutex::new(Vec::<String>::new()));
|
|
|
|
let server_clone = server.clone();
|
|
let output_clone = output.clone();
|
|
let packet_body = packet.get_body().to_owned();
|
|
|
|
let command_source =
|
|
CommandSender::Rcon(output_clone).into_source(server).await;
|
|
|
|
// Wait task complete before send output
|
|
let _ = tokio::spawn(async move {
|
|
server_clone
|
|
.command_dispatcher
|
|
.read()
|
|
.await
|
|
.handle_command(&command_source, &packet_body)
|
|
.await;
|
|
})
|
|
.await;
|
|
|
|
let output = output.lock().await;
|
|
for line in output.iter() {
|
|
if config.logging.commands {
|
|
info!("RCON ({}): {}", self.address, line);
|
|
}
|
|
self.send(ClientboundPacket::Output, packet.get_id(), line)
|
|
.await?;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
Ok(())
|
|
}
|
|
|
|
async fn read_bytes(&mut self) -> std::io::Result<bool> {
|
|
let mut buf = [0; 1460];
|
|
let n = self.connection.read(&mut buf).await?;
|
|
if n == 0 {
|
|
return Ok(true);
|
|
}
|
|
self.incoming.extend_from_slice(&buf[..n]);
|
|
Ok(false)
|
|
}
|
|
|
|
async fn send(
|
|
&mut self,
|
|
packet: ClientboundPacket,
|
|
id: i32,
|
|
body: &str,
|
|
) -> Result<(), PacketError> {
|
|
let buf = packet.write_buf(id, body);
|
|
self.connection
|
|
.write(&buf)
|
|
.await
|
|
.map_err(PacketError::FailedSend)?;
|
|
Ok(())
|
|
}
|
|
|
|
fn receive_packet(&mut self) -> Result<Option<Packet>, PacketError> {
|
|
Packet::deserialize(&mut self.incoming)
|
|
}
|
|
}
|