rcon_client/
client.rs

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
use crate::errors::RCONError;
use crate::types::{ExecuteResponse, RCONRequest, RCONResponse};
use crate::{AuthRequest, AuthResponse};
use bytes::BufMut;
use std::io::{Read, Write};
use std::net::TcpStream;
use std::time::Duration;

const DEFAULT_READ_TIMEOUT: u64 = 30;
const DEFAULT_WRITE_TIMEOUT: u64 = 30;

/// Configuration for RCON client
#[derive(Default)]
pub struct RCONConfig {
    /// URL to server listening RCON
    /// example: `0.0.0.0:25575` or `donkey-engine.host:1337`
    pub url: String,
    /// Timeout in secs for commands sending to server
    /// Default: 30 secs
    pub write_timeout: Option<u64>,
    /// Timeout in secs for response waiting from server
    /// Default: 30 secs
    pub read_timeout: Option<u64>,
}

/// Simple RCON client
#[derive(Debug)]
pub struct RCONClient {
    pub url: String,
    pub(self) socket: TcpStream,
}

/// RCON client
impl RCONClient {
    /// Create new connection
    pub fn new(config: RCONConfig) -> Result<Self, RCONError> {
        let socket = TcpStream::connect(&config.url).map_err(|err| {
            RCONError::TcpConnectionError(format!("TCP connection error: {}", err))
        })?;

        socket
            .set_write_timeout(Some(Duration::new(
                config.write_timeout.unwrap_or(DEFAULT_WRITE_TIMEOUT),
                0,
            )))
            .map_err(|err| {
                RCONError::TcpConnectionError(format!("Cannot set socket write_timeout: {}", err))
            })?;
        socket
            .set_read_timeout(Some(Duration::new(
                config.read_timeout.unwrap_or(DEFAULT_READ_TIMEOUT),
                0,
            )))
            .map_err(|err| {
                RCONError::TcpConnectionError(format!("Cannot set socket read_timeout: {}", err))
            })?;

        Ok(Self {
            url: config.url,
            socket,
        })
    }

    /// Auth on game server
    pub fn auth(&mut self, auth: AuthRequest) -> Result<AuthResponse, RCONError> {
        let response = execute(
            &mut self.socket,
            auth.id as i32,
            auth.request_type as i32,
            auth.password,
        )?;
        Ok(AuthResponse {
            id: response.response_id as isize,
            response_type: response.response_type as u8,
        })
    }

    /// Execute request
    pub fn execute(&mut self, data: RCONRequest) -> Result<RCONResponse, RCONError> {
        let response = execute(
            &mut self.socket,
            data.id as i32,
            data.request_type as i32,
            data.body,
        )?;
        Ok(RCONResponse {
            id: response.response_id as isize,
            response_type: response.response_type as u8,
            body: response.response_body,
        })
    }
}

/// Make TCP request
fn execute(
    socket: &mut TcpStream,
    id: i32,
    request_type: i32,
    data: String,
) -> Result<ExecuteResponse, RCONError> {
    // Make request
    let request_length = (data.len() + 10) as i32;
    let mut request_buffer: Vec<u8> = Vec::with_capacity(request_length as usize);
    request_buffer.put_slice(&request_length.to_le_bytes());
    request_buffer.put_slice(&(id).to_le_bytes());
    request_buffer.put_slice(&(request_type).to_le_bytes());
    request_buffer.put_slice(data.as_bytes());
    request_buffer.put_slice(&[0x00, 0x00]);
    socket
        .write(&request_buffer[..])
        .map_err(|err| RCONError::TcpConnectionError(format!("TCP request error {}", err)))?;

    // Await response
    let mut response_buffer = [0u8; 4];
    socket
        .read_exact(&mut response_buffer)
        .map_err(|err| RCONError::TcpConnectionError(format!("TCP response error {}", err)))?;
    let response_length = i32::from_le_bytes(response_buffer);
    socket
        .read_exact(&mut response_buffer)
        .map_err(|err| RCONError::TcpConnectionError(format!("TCP response error {}", err)))?;
    let response_id = i32::from_le_bytes(response_buffer);
    socket
        .read_exact(&mut response_buffer)
        .map_err(|err| RCONError::TcpConnectionError(format!("TCP response error {}", err)))?;
    let response_type = i32::from_le_bytes(response_buffer);
    let response_body_length = response_length - 10;
    let mut response_body_buffer = Vec::with_capacity(response_body_length as usize);
    socket
        .take(response_body_length as u64)
        .read_to_end(&mut response_body_buffer)
        .map_err(|err| RCONError::TcpConnectionError(format!("TCP response error {}", err)))?;
    let response_body = String::from_utf8(response_body_buffer)
        .map_err(|err| RCONError::TypeError(format!("TypeError {}", err)))?;

    let mut terminating_nulls = [0u8; 2];
    socket
        .read_exact(&mut terminating_nulls)
        .map_err(|err| RCONError::TcpConnectionError(format!("TCP response error {}", err)))?;

    Ok(ExecuteResponse {
        response_id,
        response_type,
        response_body,
    })
}