1use crate::errors::RCONError;
2use crate::types::{ExecuteResponse, RCONRequest, RCONResponse};
3use crate::{AuthRequest, AuthResponse};
4use bytes::BufMut;
5use std::io::{Read, Write};
6use std::net::TcpStream;
7use std::time::Duration;
8
9const DEFAULT_READ_TIMEOUT: u64 = 30;
10const DEFAULT_WRITE_TIMEOUT: u64 = 30;
11
12#[derive(Default)]
14pub struct RCONConfig {
15 pub url: String,
18 pub write_timeout: Option<u64>,
21 pub read_timeout: Option<u64>,
24}
25
26#[derive(Debug)]
28pub struct RCONClient {
29 pub url: String,
30 pub(self) socket: TcpStream,
31}
32
33impl RCONClient {
35 pub fn new(config: RCONConfig) -> Result<Self, RCONError> {
37 let socket = TcpStream::connect(&config.url).map_err(|err| {
38 RCONError::TcpConnectionError(format!("TCP connection error: {}", err))
39 })?;
40
41 socket
42 .set_write_timeout(Some(Duration::new(
43 config.write_timeout.unwrap_or(DEFAULT_WRITE_TIMEOUT),
44 0,
45 )))
46 .map_err(|err| {
47 RCONError::TcpConnectionError(format!("Cannot set socket write_timeout: {}", err))
48 })?;
49 socket
50 .set_read_timeout(Some(Duration::new(
51 config.read_timeout.unwrap_or(DEFAULT_READ_TIMEOUT),
52 0,
53 )))
54 .map_err(|err| {
55 RCONError::TcpConnectionError(format!("Cannot set socket read_timeout: {}", err))
56 })?;
57
58 Ok(Self {
59 url: config.url,
60 socket,
61 })
62 }
63
64 pub fn auth(&mut self, auth: AuthRequest) -> Result<AuthResponse, RCONError> {
66 let response = execute(
67 &mut self.socket,
68 auth.id as i32,
69 auth.request_type as i32,
70 auth.password,
71 )?;
72 Ok(AuthResponse {
73 id: response.response_id as isize,
74 response_type: response.response_type as u8,
75 })
76 }
77
78 pub fn execute(&mut self, data: RCONRequest) -> Result<RCONResponse, RCONError> {
80 let response = execute(
81 &mut self.socket,
82 data.id as i32,
83 data.request_type as i32,
84 data.body,
85 )?;
86 Ok(RCONResponse {
87 id: response.response_id as isize,
88 response_type: response.response_type as u8,
89 body: response.response_body,
90 })
91 }
92}
93
94fn execute(
96 socket: &mut TcpStream,
97 id: i32,
98 request_type: i32,
99 data: String,
100) -> Result<ExecuteResponse, RCONError> {
101 let request_length = (data.len() + 10) as i32;
103 let mut request_buffer: Vec<u8> = Vec::with_capacity(request_length as usize);
104 request_buffer.put_slice(&request_length.to_le_bytes());
105 request_buffer.put_slice(&(id).to_le_bytes());
106 request_buffer.put_slice(&(request_type).to_le_bytes());
107 request_buffer.put_slice(data.as_bytes());
108 request_buffer.put_slice(&[0x00, 0x00]);
109 socket
110 .write(&request_buffer[..])
111 .map_err(|err| RCONError::TcpConnectionError(format!("TCP request error {}", err)))?;
112
113 let mut response_buffer = [0u8; 4];
115 socket
116 .read_exact(&mut response_buffer)
117 .map_err(|err| RCONError::TcpConnectionError(format!("TCP response error {}", err)))?;
118 let response_length = i32::from_le_bytes(response_buffer);
119 socket
120 .read_exact(&mut response_buffer)
121 .map_err(|err| RCONError::TcpConnectionError(format!("TCP response error {}", err)))?;
122 let response_id = i32::from_le_bytes(response_buffer);
123 socket
124 .read_exact(&mut response_buffer)
125 .map_err(|err| RCONError::TcpConnectionError(format!("TCP response error {}", err)))?;
126 let response_type = i32::from_le_bytes(response_buffer);
127
128 let response_body_length = response_length - 10;
130 let mut response_body_buffer = Vec::with_capacity(response_body_length as usize);
131 let mut temp_buffer = vec![0; response_body_length as usize];
132 let mut read_so_far = 0;
133
134 while read_so_far < response_body_length as usize {
135 match socket.read(&mut temp_buffer[read_so_far..]) {
136 Ok(0) => break, Ok(n) => read_so_far += n,
138 Err(e) => {
139 eprintln!("Error reading response body: {}", e);
140 break; },
142 }
143 }
144
145 response_body_buffer.extend_from_slice(&temp_buffer[..read_so_far]);
147
148 let response_body = String::from_utf8(response_body_buffer)
149 .map_err(|err| RCONError::TypeError(format!("TypeError: {}", err)))?;
150
151 let mut terminating_nulls = [0u8; 2];
153 match socket.read_exact(&mut terminating_nulls) {
154 Ok(_) => {
155 },
157 Err(e) => {
158 eprintln!("Non-fatal error reading terminating nulls: {}", e);
160 },
161 }
162
163 Ok(ExecuteResponse {
164 response_id,
165 response_type,
166 response_body,
167 })
168}