--- title: "Sending RCON Commands from the Linux Command Line" excerpt: "How the GoldSrc RCON challenge-response protocol works over UDP, and a self-contained Python script to run rcon commands against a CS 1.6 server from a shell." category: "administration" tags: "rcon, cli" image: "" read_time: "9 min read" ---

Being able to fire an RCON command at your CS 1.6 server from a shell — changelevel, amx_ban, a config reload — is enormously handy for scripts, cron jobs and quick fixes without launching the game. But GoldSrc RCON is not the TCP protocol that Source-era tools like mcrcon speak; it is a UDP challenge-response scheme, and using the wrong tool just times out. This explains the protocol and gives you a working client.

1. Know the protocol (why generic tools fail)

GoldSrc RCON runs over the same UDP game port (default 27015) and works in two steps:

  1. The client sends a getchallenge request. The server replies with a one-time challenge number.
  2. The client sends rcon <challenge> "<password>" <command>. The server runs it and returns the console output.

Every packet is prefixed with four 0xFF bytes. Because it is UDP with a challenge, TCP-based RCON tools (built for Source or Minecraft) cannot talk to it — that is the number-one reason people think "RCON is broken."

2. Set it up on the server

RCON must be enabled with a password in server.cfg:

rcon_password "USE-A-LONG-RANDOM-STRING"

Because RCON is brute-forceable over the network, make the password long and random, and never expose the game port to the whole internet if you can restrict it — see which ports to open. On ReHLDS you also get failed-attempt limits worth enabling.

3. A self-contained Python RCON client

This implements the two-step handshake with only the standard library:

#!/usr/bin/env python3
import socket, sys

def rcon(host, port, password, command):
    s = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    s.settimeout(4)
    s.connect((host, port))

    # 1) get a challenge
    s.send(b"\xFF\xFF\xFF\xFFgetchallenge\n")
    reply = s.recv(1400).decode("latin-1", "ignore")
    # reply looks like: \xFF\xFF\xFF\xFFchallenge rcon 1234567890
    challenge = reply.split("rcon")[1].strip()

    # 2) send the command
    pkt = 'rcon %s "%s" %s' % (challenge, password, command)
    s.send(b"\xFF\xFF\xFF\xFF" + pkt.encode("latin-1") + b"\n")
    return s.recv(4096).decode("latin-1", "ignore")

if __name__ == "__main__":
    host, port, pw = sys.argv[1], int(sys.argv[2]), sys.argv[3]
    cmd = " ".join(sys.argv[4:])
    print(rcon(host, port, pw, cmd).lstrip("\xFF").lstrip("l"))

Save it as rcon.py, make it executable, and call it:

chmod +x rcon.py
./rcon.py 127.0.0.1 27015 "your-rcon-password" status
./rcon.py 127.0.0.1 27015 "your-rcon-password" changelevel de_inferno

The reply is prefixed with 0xFF 0xFF 0xFF 0xFF l; the script strips that so you get clean console text.

4. Useful things to run

./rcon.py HOST 27015 PW "status"              // players, map, uptime
./rcon.py HOST 27015 PW "amx_map de_nuke"     // change map via AMXX
./rcon.py HOST 27015 PW "exec server.cfg"     // reload config
./rcon.py HOST 27015 PW "amx_reloadadmins"    // re-read users.ini

Quote a command with arguments as one shell argument if it contains spaces you want kept together, e.g. "say hello everyone". This is the backbone of remote admin scripting — pair it with the uptime monitor to auto-changelevel off a stuck map, or with the log parser to react to events.

5. Keep the password out of your shell history

A password on the command line lands in ~/.bash_history and in ps output. For anything recurring, read it from an environment variable or a mode-600 file instead of typing it inline, and restrict who can run the script.

Troubleshooting

Verification

Run status against the server and confirm you get the player list and current map back:

./rcon.py 127.0.0.1 27015 "your-rcon-password" status

A clean hostname, map and player table means the handshake and password are correct. Then run a harmless state change like exec server.cfg and confirm from the game console that it took. Once the script works locally, you can safely wire it into cron and monitoring.