diff --git a/Documentation/extensions/xrcon-protocol.md b/Documentation/extensions/xrcon-protocol.md new file mode 100644 index 00000000..5ac8c7af --- /dev/null +++ b/Documentation/extensions/xrcon-protocol.md @@ -0,0 +1,181 @@ +# XRCON Protocol Specification + +## Overview + +XRCON is a TCP-based remote console protocol implemented as a server within the engine. It provides an alternative to the legacy RCON (which operates over UDP as out-of-band packets). XRCON uses a framed binary protocol over a single persistent TCP connection, accepting at most one client at a time. + +Key characteristics: + +- Based on TCP (stream-oriented, reliable) +- Default port is 27000 +- Maximum only one concurrent client +- Binary framed protocol with a 12-byte header + variable-length payload + + + +## Configuration + +The XRCON server is controlled by two configuration variables, both restricted to privileged users: + +| Variable | Default | Description | +|---|---|---| +| `xrcon_enable` | `0` (disabled) | Master switch; when enabled, the server starts listening for connections | +| `xrcon_address` | `127.0.0.1:27000` | Bind address and port; supports both IPv4 and IPv6. Changing this at runtime triggers a XRCON server restart (stop + rebind) | + +By default, XRCON binds only to localhost (`127.0.0.1`), providing a degree of access control. The server will not start listening until explicitly enabled. + + + +## Frame Format + +All XRCON messages are encapsulated in a framed binary format. Each frame consists of a fixed 12-byte header followed by a variable-length payload. + +### Header (12 bytes) + +| Offset | Size | Field | Description | +|---|---|---|---| +| 0 | 4 | Type | 4-character message type string (null-terminated, occupies 5 bytes in the structure but the 5th byte is padding) | +| 4 | 4 | Version | Protocol version (32-bit unsigned integer, network byte order) | +| 8 | 2 | Length | Total frame length in bytes, including the header (16-bit unsigned integer, network byte order) | +| 10 | 2 | Handle | Handle or sequence number (16-bit unsigned integer, network byte order; currently always zero) | + + +### Protocol Version + +The current protocol version is `0x000000D4` (212 decimal). The server also accepts `0x00D40000` for compatibility with certain third-party clients (e.g., CS2RemoteConsole). + +### Maximum Frame Size + +- Maximum payload per frame: 4096 bytes +- Maximum total packet size (header + payload): approximately 4136 bytes +- Receive buffer size: 4096 + 64 bytes +- Transmit buffer size: 16384 bytes (4 × maximum frame size) + +### Byte Order + +Multi-byte integer fields (version, length, handle) are transmitted in **network byte order** (big-endian). + + + +## Message Types + +| Type | Direction | Description | +|---|---|---| +| `CMND` | Client → Server | Execute a console command | +| `PRNT` | Server → Client | Console output / print message | +| `CHAN` | Server → Client | Channel list (channel metadata) | +| `AINF` | Server → Client | Application / server info | +| `ADON` | Server → Client | Additional info / server name | + +### CMND — Client Command + +**Direction**: Client → Server + +Sent by the client to execute a console command on the server. + +| Section | Size | Description | +|---|---|---| +| Header | 12 bytes | Type = `"CMND"`, version, length, handle | +| Payload | Variable | Raw command string (null-terminated) | + +The command string is injected into the engine's command buffer for execution. There is **no authentication** — any connected client may execute arbitrary commands. + +### PRNT — Server Print + +**Direction**: Server → Client + +Sent by the server to stream console output to the connected client. + +| Section | Size | Description | +|---|---|---| +| Header | 12 bytes | Type = `"PRNT"`, version, length, handle | +| Channel ID | 4 bytes | Channel identifier (unsigned 32-bit integer; always 0 for "Console") | +| Padding | 20 bytes | Reserved (5 × 32-bit unsigned integers, all zero) | +| Red | 1 byte | Red color component (always 255) | +| Green | 1 byte | Green color component (always 255) | +| Blue | 1 byte | Blue color component (always 255) | +| Alpha | 1 byte | Alpha / opacity component (always 255) | +| Text | Variable | The console output text (up to 4096 bytes) | + + +### CHAN — Channel List + +**Direction**: Server → Client + +Sent by the server during the initial handshake (and potentially later) to describe the available console output channels. + +| Section | Size | Description | +|---|---|---| +| Header | 12 bytes | Type = `"CHAN"`, version, length, handle | +| Channel Count | 2 bytes | Number of channel records (unsigned 16-bit integer; always 1) | + +Each channel record (58 bytes): + +| Field | Size | Description | +|---|---|---| +| Channel ID | 4 bytes | Unique channel identifier (unsigned 32-bit integer; always 0) | +| Unknown 1 | 4 bytes | Reserved (unsigned 32-bit integer; always 0) | +| Unknown 2 | 4 bytes | Reserved (unsigned 32-bit integer; always 0) | +| Default Verbosity | 4 bytes | Default verbosity level (unsigned 32-bit integer; always 5) | +| Current Verbosity | 4 bytes | Current verbosity level (unsigned 32-bit integer; always 5) | +| Red | 1 byte | Red color component for the channel (always 255) | +| Green | 1 byte | Green color component (always 255) | +| Blue | 1 byte | Blue color component (always 255) | +| Alpha | 1 byte | Alpha component (always 255) | +| Name | 34 bytes | Channel name string, null-padded (always `"Console"`) | + +Total payload size for this frame: 2 + 58 = 60 bytes. + +### AINF — Application Info + +**Direction**: Server → Client + +Sent by the server immediately after a client connects, before `ADON` and `CHAN`. + +| Section | Size | Description | +|---|---|---| +| Header | 12 bytes | Type = `"AINF"`, version, length, handle | +| Payload | 77 bytes | All zeros (placeholder / reserved for structured application information) | + +This packet currently serves as a placeholder and contains no meaningful data. It may be extended in future versions to carry structured metadata about the server application. + +### ADON — Additional Info + +**Direction**: Server → Client + +Sent by the server immediately after `AINF` during the connection handshake. + +| Section | Size | Description | +|---|---|---| +| Header | 12 bytes | Type = `"ADON"`, version, length, handle | +| Unknown | 2 bytes | Reserved (unsigned 16-bit integer; always 0) | +| Name Length | 2 bytes | Length of the name string (unsigned 16-bit integer) | +| Name | Variable | Server name / identifier string (e.g., `"HLDS"`) | + +This packet carries the server application name, which is a short string identifying the server type (e.g., `"HLDS"` for Half-Life Dedicated Server). + + + +## Security + +XRCON has **no built-in authentication or encryption**. There is no password validation or transport-layer security. Any client that can establish a TCP connection to the XRCON port has full command execution access to the engine console. + +The only security measures are: + +1. **Default localhost binding**: The server binds to `127.0.0.1` by default, accepting connections only from the local machine. +2. **Disabled by default**: The `xrcon_enable` variable defaults to `0`, so the server does not listen unless explicitly enabled. +3. **Privileged variables**: Both configuration variables are restricted to privileged users and cannot be modified through unprivileged console commands or external access. + + + +## Comparison with Legacy RCON + +| Feature | XRCON | Legacy RCON | +|---|---|---| +| Transport | TCP (stream) | UDP (datagram, out-of-band) | +| Port | Configurable, default 27000 | Game port (default 27015) | +| Framing | Binary frame header | Plain text over UDP | +| Authentication | None | Password-based | +| Max clients | 1 | Multiple | +| Interaction format | Full console access | Request-response only for submitted command | +| Supported environments | Client and server | Server only |