B&W print-friendly diagrams + tinkerpop-0001 + wave-3 proof sections. Squash of 94 local commits onto remote master.
35 lines
1.3 KiB
Python
35 lines
1.3 KiB
Python
#!/usr/bin/env python3
|
|
"""Minimal RCON client for Minecraft server command injection."""
|
|
import socket, struct, sys, time
|
|
|
|
def rcon(host, port, password, command, timeout=10):
|
|
def packet(pid, ptype, body):
|
|
b = body.encode() + b'\x00\x00'
|
|
return struct.pack('<iii', 4 + 4 + len(b), pid, ptype) + b
|
|
|
|
def recv_packet(s):
|
|
hdr = b''
|
|
while len(hdr) < 4:
|
|
hdr += s.recv(4 - len(hdr))
|
|
length = struct.unpack('<i', hdr)[0]
|
|
data = b''
|
|
while len(data) < length:
|
|
data += s.recv(length - len(data))
|
|
return struct.unpack('<ii', data[:8])[0], struct.unpack('<ii', data[:8])[1], data[8:-2].decode('utf-8', errors='replace')
|
|
|
|
s = socket.socket()
|
|
s.settimeout(timeout)
|
|
s.connect((host, port))
|
|
s.send(packet(1, 3, password)) # auth
|
|
recv_packet(s)
|
|
s.send(packet(2, 2, command)) # command
|
|
_, _, resp = recv_packet(s)
|
|
s.close()
|
|
return resp
|
|
|
|
if __name__ == '__main__':
|
|
host = sys.argv[1] if len(sys.argv) > 1 else 'localhost'
|
|
port = int(sys.argv[2]) if len(sys.argv) > 2 else 25575
|
|
password = sys.argv[3] if len(sys.argv) > 3 else 'benchpass'
|
|
command = ' '.join(sys.argv[4:]) if len(sys.argv) > 4 else 'list'
|
|
print(rcon(host, port, password, command))
|