This commit is contained in:
Sergey Yarkov 2023-05-31 22:33:48 +03:00
commit baf102b9ad
No known key found for this signature in database
GPG Key ID: E91CE1FABC619B6F
4 changed files with 213 additions and 0 deletions

6
config.json Normal file
View File

@ -0,0 +1,6 @@
{
"host": "0.0.0.0",
"port": 27015,
"password": "password",
"readlineEnabled": true
}

186
index.js Normal file
View File

@ -0,0 +1,186 @@
import { Buffer } from 'node:buffer';
import net from 'node:net';
import crypto from 'node:crypto';
import config from './config.json' assert { type: 'json' };
var PACKET_TYPES = {
SERVERDATA_AUTH: 3,
SERVERDATA_AUTH_RESPONSE: 2,
SERVERDATA_EXECCOMMAND: 2,
SERVERDATA_RESPONSE_VALUE: 0,
};
var rcon = await rcon_connect(config.host, config.port);
await rcon.auth(config.password);
/**
* Send commands using user input
*/
if (config.readlineEnabled) {
var readline = await import('node:readline/promises');
var rl = readline.createInterface({ input: process.stdin, output: process.stdout });
rl.on('SIGINT', exit);
while (1) {
var command = await rl.question('> ');
if (command == '.exit') exit();
console.log(`[RCON]: Executing "${command}"`);
var response = await rcon.command(command);
console.log(`[RCON]: Response:`, response);
}
}
/**
* Creates Basic Packet Structure
* Link: https://developer.valvesoftware.com/wiki/Source_RCON_Protocol#Basic_Packet_Structure
*
* @param {number} id
* @param {number} type
* @param {string} body
* @returns {Buffer}
*/
function rcon_create_packet_structure(id, type, body) {
var size = Buffer.byteLength(body) + 14;
var buffer = Buffer.alloc(size);
buffer.writeInt32LE(size - 4, 0);
buffer.writeInt32LE(id, 4);
buffer.writeInt32LE(type, 8);
buffer.write(body, 12, size - 2, 'ascii');
buffer.writeInt16LE(0x00, size - 2);
return buffer;
}
/**
* The complete Triforce, or one or more components of the Triforce.
* @typedef {Object} PacketStructure
* @property {number} size
* @property {number} id
* @property {number} type
* @property {string} body
*/
/**
* Return object that contains packet data
*
* @param {Buffer} buffer
* @returns {PacketStructure}
*/
function rcon_get_packet_structure(buffer) {
return {
size: buffer.readInt32LE(0),
id: buffer.readInt32LE(4),
type: buffer.readInt32LE(8),
body: buffer.toString('ascii', 12, buffer.length - 2),
};
}
/**
* Will create a new socket
*
* @param {string} host
* @param {number} port
* @returns {Promise<net.Socket>}
*/
async function rcon_create_connection(host, port) {
return new Promise((resolve, reject) => {
var socket = net
.createConnection(
{
host,
port,
},
() => {
console.log('[RCON]: Successfully connected to the server!');
resolve(socket);
}
)
.on('error', (error) => reject(error));
});
}
/**
* Creates a new connection
*
* @param {string} host
* @param {number} port
*/
async function rcon_connect(host, port) {
try {
var connection = await rcon_create_connection(host, port);
return {
connection,
close() {
connection.end();
},
/**
* Send RCON command
*
* @param {string} name
* @returns
*/
async command(name) {
var response = await this.send(
rcon_create_packet_structure(
crypto.randomInt(10000),
PACKET_TYPES.SERVERDATA_EXECCOMMAND,
name
)
);
return response;
},
/**
* Auhtenticate RCON client
*
* @param {string} password
* @returns
*/
async auth(password) {
var response = await this.send(
rcon_create_packet_structure(10, PACKET_TYPES.SERVERDATA_AUTH, password)
);
return response;
},
/**
* Send RCON packet
*
* @param {PacketStructure} packet
* @returns {Promise<PacketStructure>}
*/
async send(packet) {
connection.write(packet);
return new Promise((resolve, reject) => {
connection.removeAllListeners('data');
connection.removeAllListeners('error');
connection.on('data', (data) => {
var packet = rcon_get_packet_structure(data);
/**
* Check on password valid
*/
if (packet.type === 2 && packet.id === -1) {
throw new Error('Wrong RCON password!');
}
resolve(packet);
});
connection.on('error', (error) => reject(error));
});
},
};
} catch (error) {
throw error;
}
}
function exit() {
console.log('\n[RCON]: Closing connection...');
rcon.close();
process.exit(1);
}

13
package.json Normal file
View File

@ -0,0 +1,13 @@
{
"name": "rcon",
"version": "1.0.0",
"description": "",
"main": "index.js",
"type": "module",
"scripts": {
"start": "node --no-warnings index.js"
},
"keywords": [],
"author": "Sergey Yarkov",
"license": "ISC"
}

8
utils.js Normal file
View File

@ -0,0 +1,8 @@
export async function wait(ms = 1000, cb = () => undefined) {
return new Promise((resolve) => {
setTimeout(() => {
cb();
resolve();
}, ms);
});
}