Rcon was not working

This commit is contained in:
zontreck 2024-06-04 15:34:07 -07:00
parent 167c27848a
commit 2a5774bd6d
3 changed files with 153 additions and 76 deletions

View file

@ -1,95 +1,124 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
class RCONPacket {
int id;
int type;
String body;
import 'RconDecoder.dart';
RCONPacket(this.id, this.type, this.body);
abstract class RconClient {
void exit();
List<int> toBytes() {
List<int> bodyBytes = utf8.encode(body);
int size = 10 + bodyBytes.length;
List<int> packet = [];
packet.addAll(_intToBytes(size));
packet.addAll(_intToBytes(id));
packet.addAll(_intToBytes(type));
packet.addAll(bodyBytes);
packet.addAll([0, 0]);
return packet;
}
void send(String cmd);
static RCONPacket fromBytes(List<int> bytes) {
int size = _bytesToInt(bytes.sublist(0, 4));
int id = _bytesToInt(bytes.sublist(4, 8));
int type = _bytesToInt(bytes.sublist(8, 12));
String body =
utf8.decode(bytes.sublist(12, size + 2).sublist(0, size - 10));
return RCONPacket(id, type, body);
}
factory RconClient() => RconClientImpl();
static List<int> _intToBytes(int value) {
return [
value & 0xFF,
(value >> 8) & 0xFF,
(value >> 16) & 0xFF,
(value >> 24) & 0xFF
];
}
static int _bytesToInt(List<int> bytes) {
return bytes[0] | (bytes[1] << 8) | (bytes[2] << 16) | (bytes[3] << 24);
}
Stream<String> connect(String host, int port, String password);
}
class RCONClient {
String host;
int port;
String password;
class RconType {
/// RCON Packet Type 0: Server response
static const int SERVERDATA_RESPONSE_VALUE = 0;
/// RCON Packet Type 2: Server auth response
static const int SERVERDATA_AUTH_RESPONSE = 2;
/// RCON Packet Type 2: Client execute command
static const int SERVERDATA_EXECCOMMAND = 2;
/// RCON Packet Type 3: Client send authentication data
static const int SERVERDATA_AUTH = 3;
}
/// Client for the rcon protocol
class RconClientImpl implements RconClient {
/// Socket used to write and read TCP packets.
late Socket _socket;
late int _requestId;
RCONClient(this.host, this.port, this.password) {
_requestId = 0;
/// Disconnect from the a RCON server.
///
/// Usage:
///
/// ```dart
/// RconClient client = RconClient.connect(print, 'localhost', '25575', 'password');
/// client.exit();
/// ```
@override
void exit() => _socket.destroy();
/// Send commands
@override
void send(String cmd) => _send(RconType.SERVERDATA_EXECCOMMAND, cmd);
/// Authenticate to the server
void _authenticate(String password) =>
_send(RconType.SERVERDATA_AUTH, password);
/// Build and send a packet to the rcon server
///
/// | Field | Type | Value |
/// |--------------|-------------------------------------|-------|
/// | Size | 32-bit little-endian Signed Integer | |
/// | ID | 32-bit little-endian Signed Integer | |
/// | Type | 32-bit little-endian Signed Integer | |
/// | Body | Null-terminated ASCII String | |
/// | Empty String | Null-terminated ASCII String | 0x00 |
void _send(int type, String payload) {
final Uint8List outSize = Uint8List(4);
final Uint8List outId = Uint8List(4); // Empty: 00 00 00 00
final Uint8List outType = Uint8List(4);
// Write type in the Type field
final ByteData typeData = ByteData.view(outType.buffer);
typeData.setInt32(0, type, Endian.little);
// Build the body
final List<int> outBody = utf8.encode(payload);
// Build ID + Type + Body + Empty String
final Uint8List outPacketBody =
Uint8List.fromList(outId + outType + outBody + Uint8List(2));
// Calculate the Size field
final ByteData sizeData = ByteData.view(outSize.buffer);
sizeData.setInt32(0, outPacketBody.length, Endian.little);
// Build the packet
final Uint8List packet = Uint8List.fromList(outSize + outPacketBody);
// View packet
// print(packet);
// print(packet.map((int i) => i.toRadixString(16)).toList());
// print(utf8.decode(packet));
// Send the packet
_socket.add(packet);
}
Future<void> connect() async {
/// Connect to the a RCON server.
///
/// Usage:
///
/// ```dart
/// final RconClient client = RconClient();
/// final Stream<String> response = client.connect(
/// 'localhost',
/// 25575,
/// 'password',
/// );
/// ```
@override
Stream<String> connect(String host, int port, String password) async* {
print("Trying to connect...");
_socket = await Socket.connect(host, port);
if (await _authenticate()) {
print('Authenticated successfully');
} else {
print('Authentication failed');
}
}
Future<bool> _authenticate() async {
RCONPacket packet = RCONPacket(_requestId++, 3, password);
_socket.add(packet.toBytes());
await _socket.flush();
RCONPacket response = await _readPacket();
return response.id == packet.id && response.type == 2;
}
print("Connected.");
Future<RCONPacket> _readPacket() async {
List<int> sizeBytes = await _socket.first;
int size = RCONPacket._bytesToInt(sizeBytes);
List<int> dataBytes = [];
while (dataBytes.length < size) {
dataBytes.addAll(await _socket.first);
}
return RCONPacket.fromBytes(sizeBytes + dataBytes);
}
final Stream<String> stream = _socket.transform(RconDecoder());
Future<String> sendCommand(String command) async {
RCONPacket packet = RCONPacket(_requestId++, 2, command);
_socket.add(packet.toBytes());
await _socket.flush();
RCONPacket response = await _readPacket();
return response.body;
}
print("Trying to authenticate");
void close() {
_socket.close();
_authenticate(password);
yield* stream;
}
}

View file

@ -0,0 +1,48 @@
import 'dart:convert';
import 'dart:io';
import 'dart:typed_data';
import 'Rcon.dart';
class RconDecoder extends Converter<Uint8List, String> {
/// Transform packets into a String.
///
/// | Field | Type | Value |
/// |--------------|-------------------------------------|-------|
/// | Size | 32-bit little-endian Signed Integer | |
/// | ID | 32-bit little-endian Signed Integer | |
/// | Type | 32-bit little-endian Signed Integer | |
/// | Body | Null-terminated ASCII String | |
/// | Empty String | Null-terminated ASCII String | 0x00 |
@override
String convert(Uint8List data) {
final byteData = ByteData.sublistView(data);
// final inLength = byteData.getInt32(0, Endian.little);
final inId = byteData.getInt32(4, Endian.little);
final inType = byteData.getInt32(8, Endian.little);
final inBody = data.sublist(12);
if (inType == RconType.SERVERDATA_AUTH_RESPONSE) {
if (inId == -1) {
throw const SocketException('Bad login.');
} else if (inId == 0) {
return 'Authentication successful. You can now write commands.';
}
}
return utf8.decode(inBody);
}
@override
Sink<Uint8List> startChunkedConversion(Sink<String> sink) {
return ChunkedConversionSink.withCallback((chunk) {
for (final data in chunk) {
sink.add(convert(data));
}
});
}
// Override the base class's bind, to provide a better type.
@override
Stream<String> bind(Stream<Uint8List> stream) => super.bind(stream);
}

View file

@ -1,6 +1,6 @@
name: libac_dart
description: "Aria's Creations code library"
version: 1.0.28
version: 1.0.29
homepage: "https://zontreck.com"