use crate::connect::ConnectManager;
use crate::error::RconError;
use crate::packet::{PacketWithoutSize, ReceivedBPacketList};
use crate::rand::gen_rand_i32;
#[derive(Debug)]
pub struct Rcon {
host: String,
port: u32,
password: String,
timeout: u64,
last_id: i32,
buffer_size: usize,
}
impl Rcon {
pub fn builder() -> RconBuilder {
RconBuilder {
host: None,
port: Some(25575),
password: None,
timeout: Some(3),
buffer_size: Some(2900),
}
}
fn auth(&self) -> Result<bool, RconError> {
let random_id: i32 = gen_rand_i32(1, 1000);
let mut socket =
create_rcon_connection(self.host.clone(), self.port, self.timeout, self.buffer_size)?;
socket.send_auth(&self.password, random_id as usize)?;
let response_list = parser_response(&mut socket)?;
socket.shutdown()?;
match PacketWithoutSize::check_auth(random_id, &response_list[0]) {
Err(RconError::IncorrectPasswordError) => Ok(false),
Err(e) => Err(e),
Ok(_) => Ok(true),
}
}
fn auth_for_exec(&self, socket: &mut ConnectManager) -> Result<bool, RconError> {
let random_id: i32 = gen_rand_i32(1, 1000);
socket.send_auth(&self.password, random_id as usize)?;
let response_list = parser_response(socket)?;
match PacketWithoutSize::check_auth(random_id, &response_list[0]) {
Err(RconError::IncorrectPasswordError) => Ok(false),
Err(e) => Err(e),
Ok(_) => Ok(true),
}
}
pub fn exec(&mut self, command: String) -> Result<String, RconError> {
self.last_id += 1;
let mut socket =
create_rcon_connection(self.host.clone(), self.port, self.timeout, self.buffer_size)?;
match self.auth_for_exec(&mut socket)? {
true => {}
false => return Err(RconError::IncorrectPasswordError),
}
socket.send_command(&command, self.last_id as usize)?;
let response_list = parser_response(&mut socket)?;
socket.shutdown()?;
let feedback =
PacketWithoutSize::get_payload(&response_list[0]).ok_or(RconError::FeedbackIsNone)?;
let feedback_id = response_list[0].get_id();
if feedback_id != self.last_id {
return Err(RconError::MismatchedResponsePacketID);
}
Ok(feedback)
}
}
pub struct RconBuilder {
host: Option<String>,
port: Option<u32>,
password: Option<String>,
timeout: Option<u64>,
buffer_size: Option<usize>,
}
impl RconBuilder {
pub fn host(mut self, host: String) -> Self {
self.host = Some(host);
self
}
pub fn port(mut self, port: u32) -> Self {
self.port = Some(port);
self
}
pub fn password(mut self, password: String) -> Self {
self.password = Some(password);
self
}
pub fn timeout(mut self, timeout: u64) -> Self {
self.timeout = Some(timeout);
self
}
pub fn buffer_size(mut self, buffer_size: usize) -> Self {
self.buffer_size = Some(buffer_size);
self
}
pub fn build(self) -> Result<Rcon, RconError> {
let host = self.host.ok_or(RconError::MissingField("host"))?;
let port = self.port.ok_or(RconError::MissingField("port"))?;
let password = self.password.ok_or(RconError::MissingField("password"))?;
let timeout = self.timeout.ok_or(RconError::MissingField("timeout"))?;
let buffer_size = self.buffer_size.unwrap_or(2900);
let new_rcon = Rcon {
host,
port,
password,
timeout,
last_id: 0,
buffer_size,
};
match new_rcon.auth()? {
true => Ok(new_rcon),
false => Err(RconError::IncorrectPasswordError),
}
}
}
fn create_rcon_connection(
host: String,
port: u32,
max_timeout: u64,
buff_size: usize,
) -> Result<ConnectManager, RconError> {
Ok(ConnectManager::builder()
.host(host)
.port(port)
.max_timeout(max_timeout)
.buffer_size(buff_size)
.connect()?)
}
fn parser_response(socket: &mut ConnectManager) -> Result<Vec<PacketWithoutSize>, RconError> {
let response_b_raw = socket.receive_packet()?;
let response_list = ReceivedBPacketList::new(response_b_raw.as_slice())?;
Ok(response_list.into_packet_without_size()?)
}