mirror of
https://github.com/GameServerManagers/LinuxGSM.git
synced 2025-01-08 12:07:37 +08:00
Implemented proof of concept RCON command
This commit is contained in:
parent
ad2f81599a
commit
212e02e16e
@ -47,7 +47,7 @@ if [ "$(whoami)" != "root" ]; then
|
||||
done
|
||||
fi
|
||||
|
||||
allowed_commands_array=(BACKUP CONSOLE DEBUG DETAILS MAP-COMPRESSOR FASTDL MODS-INSTALL MODS-REMOVE MODS-UPDATE MONITOR POST-DETAILS RESTART START STOP TEST-ALERT CHANGE-PASSWORD UPDATE UPDATE-LGSM VALIDATE WIPE)
|
||||
allowed_commands_array=(BACKUP CONSOLE DEBUG DETAILS MAP-COMPRESSOR FASTDL MODS-INSTALL MODS-REMOVE MODS-UPDATE MONITOR POST-DETAILS RCON RESTART START STOP TEST-ALERT CHANGE-PASSWORD UPDATE UPDATE-LGSM VALIDATE WIPE)
|
||||
for allowed_command in "${allowed_commands_array[@]}"; do
|
||||
if [ "${allowed_command}" == "${commandname}" ]; then
|
||||
check_logs.sh
|
||||
@ -61,14 +61,14 @@ for allowed_command in "${allowed_commands_array[@]}"; do
|
||||
fi
|
||||
done
|
||||
|
||||
allowed_commands_array=(CONSOLE DEBUG MONITOR START STOP)
|
||||
allowed_commands_array=(CONSOLE DEBUG MONITOR RCON START STOP)
|
||||
for allowed_command in "${allowed_commands_array[@]}"; do
|
||||
if [ "${allowed_command}" == "${commandname}" ]; then
|
||||
check_config.sh
|
||||
fi
|
||||
done
|
||||
|
||||
allowed_commands_array=(DEBUG DETAILS DEV-QUERY-RAW MONITOR POST_DETAILS START STOP POST-DETAILS)
|
||||
allowed_commands_array=(DEBUG DETAILS DEV-QUERY-RAW MONITOR POST_DETAILS RCON START STOP POST-DETAILS)
|
||||
for allowed_command in "${allowed_commands_array[@]}"; do
|
||||
if [ "${allowed_command}" == "${commandname}" ]; then
|
||||
if [ -z "${installflag}" ]; then
|
||||
@ -86,7 +86,7 @@ for allowed_command in "${allowed_commands_array[@]}"; do
|
||||
fi
|
||||
done
|
||||
|
||||
allowed_commands_array=(CHANGE-PASSWORD DETAILS MONITOR START STOP UPDATE VALIDATE POST-DETAILS)
|
||||
allowed_commands_array=(CHANGE-PASSWORD DETAILS MONITOR RCON START STOP UPDATE VALIDATE POST-DETAILS)
|
||||
for allowed_command in "${allowed_commands_array[@]}"; do
|
||||
if [ "${allowed_command}" == "${commandname}" ]; then
|
||||
check_status.sh
|
||||
|
44
lgsm/modules/command_rcon.sh
Normal file
44
lgsm/modules/command_rcon.sh
Normal file
@ -0,0 +1,44 @@
|
||||
#!/bin/bash
|
||||
# LinuxGSM command_rcon.sh module
|
||||
# Author: Daniel Gibbs
|
||||
# Contributors: http://linuxgsm.com/contrib
|
||||
# Website: https://linuxgsm.com
|
||||
# Description: Send rcon commands to different gameservers.
|
||||
|
||||
commandname="RCON"
|
||||
commandaction="Rcon"
|
||||
moduleselfname="$(basename "$(readlink -f "${BASH_SOURCE[0]}")")"
|
||||
fn_firstcommand_set
|
||||
|
||||
check.sh
|
||||
if [ "${status}" == "0" ]; then
|
||||
fn_print_error_nl "Server not running"
|
||||
fn_script_log_error "Failed to access: Server not running"
|
||||
if fn_prompt_yn "Do you want to start the server?" Y; then
|
||||
exitbypass=1
|
||||
command_start.sh
|
||||
fi
|
||||
fi
|
||||
|
||||
|
||||
if [ -n "${userinput2}" ]; then
|
||||
rconcommandtosend="${userinput2}"
|
||||
else
|
||||
fn_print_header
|
||||
fn_print_information_nl "Send a RCON command to the server."
|
||||
echo ""
|
||||
rconcommandtosend=$(fn_prompt_message "RCON command: ")
|
||||
fi
|
||||
|
||||
fn_print_dots "Sending RCON command to server: \"${rconcommandtosend}\""
|
||||
|
||||
if [ ! -f "${modulesdir}/rcon.py" ]; then
|
||||
fn_fetch_file_github "lgsm/modules" "rcon.py" "${modulesdir}" "chmodx" "norun" "noforce" "nohash"
|
||||
fi
|
||||
|
||||
"${modulesdir}"/rcon.py -a "${telnetip}" -p "${rconport}" -P "${rconpassword}" -c "${rconcommandtosend}" > /dev/null 2>&1
|
||||
|
||||
fn_print_ok_nl "Sending RCON command to server: \"${rconcommandtosend}\""
|
||||
fn_script_log_pass "RCON command \"${rconcommandtosend}\" sent to server"
|
||||
|
||||
core_exit.sh
|
@ -24,6 +24,7 @@ cmd_monitor=("m;monitor" "command_monitor.sh" "Check server status and restart i
|
||||
cmd_skeleton=("sk;skeleton" "command_skeleton.sh" "Create a skeleton directory.")
|
||||
cmd_sponsor=("s;sponsor" "command_sponsor.sh" "Sponsorship options.")
|
||||
cmd_send=("sd;send" "command_send.sh" "Send command to game server console.")
|
||||
cmd_rcon=("rc;rcon" "command_rcon.sh" "Send RCON command to game server.")
|
||||
# Console servers only.
|
||||
cmd_console=("c;console" "command_console.sh" "Access server console.")
|
||||
cmd_debug=("d;debug" "command_debug.sh" "Start server directly in your terminal.")
|
||||
@ -92,6 +93,11 @@ if [ "${consoleinteract}" == "yes" ]; then
|
||||
currentopt+=("${cmd_send[@]}")
|
||||
fi
|
||||
|
||||
# RCON command.
|
||||
# TODO: Add RCON type to all _default.cfg files [Source RCON Protocol / Other Protocols?! / None?!]
|
||||
# TODO: then add a check in the rcon command to use the appropriate protocol
|
||||
currentopt+=("${cmd_rcon[@]}")
|
||||
|
||||
## Game server exclusive commands.
|
||||
|
||||
# FastDL command.
|
||||
|
@ -181,6 +181,11 @@ command_send.sh() {
|
||||
fn_fetch_module
|
||||
}
|
||||
|
||||
command_rcon.sh() {
|
||||
modulefile="${FUNCNAME[0]}"
|
||||
fn_fetch_module
|
||||
}
|
||||
|
||||
# Checks
|
||||
|
||||
check.sh() {
|
||||
@ -739,6 +744,11 @@ install_gsquery.sh() {
|
||||
fn_fetch_module
|
||||
}
|
||||
|
||||
install_rcon.sh() {
|
||||
modulefile="${FUNCNAME[0]}"
|
||||
fn_fetch_module
|
||||
}
|
||||
|
||||
install_gslt.sh() {
|
||||
modulefile="${FUNCNAME[0]}"
|
||||
fn_fetch_module
|
||||
|
116
lgsm/modules/rcon.py
Normal file
116
lgsm/modules/rcon.py
Normal file
@ -0,0 +1,116 @@
|
||||
#! /usr/bin/env python3
|
||||
# -*- coding: utf-8 -*-
|
||||
# LinuxGSM rcon.py module
|
||||
# Author: MicLieg
|
||||
# Contributors: http://linuxgsm.com/contrib
|
||||
# Website: https://linuxgsm.com
|
||||
# Description: Allows sending RCON commands to different gameservers.
|
||||
|
||||
import argparse
|
||||
import socket
|
||||
import struct
|
||||
import sys
|
||||
|
||||
|
||||
class PacketTypes:
|
||||
LOGIN = 3
|
||||
COMMAND = 2
|
||||
|
||||
|
||||
class Rcon:
|
||||
|
||||
def __init__(self, arguments):
|
||||
self.arguments = arguments
|
||||
self.connection = None
|
||||
|
||||
def __enter__(self):
|
||||
self.connect_to_server()
|
||||
return self
|
||||
|
||||
def __exit__(self, exc_type, exc_val, exc_tb):
|
||||
if self.connection:
|
||||
self.connection.close()
|
||||
|
||||
@staticmethod
|
||||
def fatal_error(error_message, error_code):
|
||||
sys.stderr.write(f'ERROR: {error_code} {error_message}\n')
|
||||
sys.exit(error_code)
|
||||
|
||||
@staticmethod
|
||||
def exit_success(success_message=''):
|
||||
sys.stdout.write(f'OK: {success_message}\n')
|
||||
sys.exit(0)
|
||||
|
||||
def connect_to_server(self):
|
||||
self.connection = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
|
||||
self.connection.settimeout(self.arguments.timeout)
|
||||
|
||||
try:
|
||||
self.connection.connect((self.arguments.address, self.arguments.port))
|
||||
except socket.timeout:
|
||||
self.fatal_error('Request timed out', 1)
|
||||
except Exception as e:
|
||||
self.fatal_error(f'Unable to connect: {e}', 1)
|
||||
|
||||
def send_packet(self, request_id, packet_type, data):
|
||||
# Packet structure follows the Source RCON Protocol: size, request ID, type, data, two null bytes
|
||||
packet = (
|
||||
struct.pack('<l', request_id)
|
||||
+ struct.pack('<l', packet_type)
|
||||
+ data.encode('utf8') + b'\x00\x00'
|
||||
)
|
||||
try:
|
||||
self.connection.send(struct.pack('<l', len(packet)) + packet)
|
||||
except socket.error as e:
|
||||
self.fatal_error(f'Failed to send packet: {e}', 2)
|
||||
|
||||
def receive_packet(self):
|
||||
try:
|
||||
response = self.connection.recv(self.arguments.buffer)
|
||||
return response
|
||||
except socket.error as e:
|
||||
self.fatal_error(f'Failed to receive response: {e}', 3)
|
||||
|
||||
def login(self):
|
||||
self.send_packet(1, PacketTypes.LOGIN, self.arguments.password)
|
||||
response = self.receive_packet()
|
||||
if response:
|
||||
size, id_response, type_response = struct.unpack('<l', response[:4]), struct.unpack('<l', response[
|
||||
4:8]), struct.unpack(
|
||||
'<l', response[8:12])
|
||||
|
||||
if id_response[0] == -1:
|
||||
self.fatal_error('Login to RCON failed', 4)
|
||||
else:
|
||||
self.fatal_error('No response received for login', 4)
|
||||
|
||||
def send_command(self):
|
||||
self.send_packet(2, PacketTypes.COMMAND, self.arguments.command)
|
||||
response = self.receive_packet()
|
||||
if response:
|
||||
response_message = response[12:-2].decode('utf-8') # Stripping trailing null bytes
|
||||
self.exit_success(str(response_message))
|
||||
else:
|
||||
self.fatal_error('No response received for command', 5)
|
||||
|
||||
|
||||
def parse_args():
|
||||
parser = argparse.ArgumentParser(description='Sends RCON commands to Minecraft servers.')
|
||||
parser.add_argument('-a', '--address', type=str, required=True, help='The server IP address.')
|
||||
parser.add_argument('-p', '--port', type=int, required=True, help='The server port.')
|
||||
parser.add_argument('-P', '--password', type=str, required=True, help='The RCON password.')
|
||||
parser.add_argument('-c', '--command', type=str, required=True, help='The RCON command to send.')
|
||||
parser.add_argument('-t', '--timeout', type=int, default=5, help='The timeout for server response.')
|
||||
parser.add_argument('-b', '--buffer', type=int, default=4096, help='The buffer length for server response.')
|
||||
return parser.parse_args()
|
||||
|
||||
|
||||
def main():
|
||||
arguments = parse_args()
|
||||
with Rcon(arguments) as rcon:
|
||||
rcon.login()
|
||||
rcon.send_command()
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
main()
|
Loading…
Reference in New Issue
Block a user