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:convert';
import 'dart:io'; import 'dart:io';
import 'dart:typed_data';
class RCONPacket { import 'RconDecoder.dart';
int id;
int type;
String body;
RCONPacket(this.id, this.type, this.body); abstract class RconClient {
void exit();
List<int> toBytes() { void send(String cmd);
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;
}
static RCONPacket fromBytes(List<int> bytes) { factory RconClient() => RconClientImpl();
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);
}
static List<int> _intToBytes(int value) { Stream<String> connect(String host, int port, String password);
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);
}
} }
class RCONClient { class RconType {
String host; /// RCON Packet Type 0: Server response
int port; static const int SERVERDATA_RESPONSE_VALUE = 0;
String password;
/// 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 Socket _socket;
late int _requestId;
RCONClient(this.host, this.port, this.password) { /// Disconnect from the a RCON server.
_requestId = 0; ///
/// 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); _socket = await Socket.connect(host, port);
if (await _authenticate()) {
print('Authenticated successfully');
} else {
print('Authentication failed');
}
}
Future<bool> _authenticate() async { print("Connected.");
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;
}
Future<RCONPacket> _readPacket() async { final Stream<String> stream = _socket.transform(RconDecoder());
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);
}
Future<String> sendCommand(String command) async { print("Trying to authenticate");
RCONPacket packet = RCONPacket(_requestId++, 2, command);
_socket.add(packet.toBytes());
await _socket.flush();
RCONPacket response = await _readPacket();
return response.body;
}
void close() { _authenticate(password);
_socket.close();
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 name: libac_dart
description: "Aria's Creations code library" description: "Aria's Creations code library"
version: 1.0.28 version: 1.0.29
homepage: "https://zontreck.com" homepage: "https://zontreck.com"