1use crate::errors::RconProtocolError;
2use bytes::{Buf, BufMut, Bytes, BytesMut};
3use std::mem::size_of;
4
5use super::{MAX_LEN_CLIENTBOUND, MAX_LEN_SERVERBOUND};
6
7#[derive(Debug)]
8pub(super) enum RconPacketType {
9 Response,
10 Login,
11 RunCommand,
12}
13
14impl From<RconPacketType> for i32 {
15 fn from(packet_type: RconPacketType) -> Self {
16 match packet_type {
17 RconPacketType::Response => 0,
18 RconPacketType::RunCommand => 2,
19 RconPacketType::Login => 3,
20 }
21 }
22}
23
24impl TryFrom<i32> for RconPacketType {
25 type Error = RconProtocolError;
26
27 fn try_from(value: i32) -> Result<Self, Self::Error> {
28 match value {
29 0 => Ok(RconPacketType::Response),
30 2 => Ok(RconPacketType::RunCommand),
31 3 => Ok(RconPacketType::Login),
32 _ => Err(RconProtocolError::InvalidPacketType),
33 }
34 }
35}
36
37#[derive(Debug)]
38pub(super) struct RconPacket {
39 pub request_id: i32,
40 pub packet_type: RconPacketType,
41 pub payload: String,
42}
43
44impl RconPacket {
45 pub fn new(
46 request_id: i32,
47 packet_type: RconPacketType,
48 payload: String,
49 ) -> Result<Self, RconProtocolError> {
50 if !payload.is_ascii() {
51 return Err(RconProtocolError::NonAsciiPayload);
52 }
53
54 if payload.len() > Ord::max(MAX_LEN_CLIENTBOUND, MAX_LEN_SERVERBOUND) {
55 return Err(RconProtocolError::PayloadTooLong);
56 }
57
58 Ok(Self {
59 request_id,
60 packet_type,
61 payload,
62 })
63 }
64
65 pub fn bytes(self) -> Bytes {
66 Bytes::from(self)
67 }
68}
69
70impl TryFrom<Bytes> for RconPacket {
71 type Error = RconProtocolError;
72
73 fn try_from(mut bytes: Bytes) -> Result<Self, Self::Error> {
74 let len = bytes.get_i32_le(); let request_id = bytes.get_i32_le();
76 let packet_type = bytes.get_i32_le();
77
78 let mut payload = String::new();
79 loop {
80 let current = bytes.get_u8();
81 if current == 0 {
82 break;
84 }
85
86 payload.push(current as char);
87 }
88
89 if !payload.is_ascii() {
92 for c in payload.chars() {
93 if !c.is_ascii() && (c as u8) != 0xa7 {
95 return Err(RconProtocolError::NonAsciiPayload);
96 }
97 }
98 }
99
100 let pad = bytes.get_u8(); if pad != 0 {
102 return Err(RconProtocolError::InvalidRconResponse);
103 }
104
105 if get_remaining_length(&payload) != len {
107 return Err(RconProtocolError::InvalidRconResponse);
108 }
109
110 Self::new(request_id, packet_type.try_into()?, payload)
111 }
112}
113
114impl From<RconPacket> for Bytes {
115 fn from(packet: RconPacket) -> Self {
116 let len = get_remaining_length(&packet.payload);
117 let packet_type: i32 = packet.packet_type.into();
118
119 let mut bytes = BytesMut::new();
120
121 bytes.put_i32_le(len);
122 bytes.put_i32_le(packet.request_id);
123 bytes.put_i32_le(packet_type);
124 bytes.put(packet.payload.as_bytes());
125 bytes.put_u16(0x00_00);
126
127 bytes.freeze()
128 }
129}
130
131fn get_remaining_length(payload: &str) -> i32 {
141 (payload.len() + size_of::<i32>() * 2 + 2) as i32
142}