Source RCON Protocol
The Source RCON Protocol is a TCP/IP-based communication protocol used by Source Dedicated Server, which allows console commands to be issued to the server via a "remote console", or RCON. The most common use of RCON is to allow server owners to control their game servers without direct access to the machine the server is running on. In order for commands to be accepted, the connection must first be authenticated using the server's RCON password, which can be set using the console variable rcon_password.
Contents
Using Source RCON
By default, SRCDS listens for RCON connections on TCP port 27015. If the server's port number is changed using the -port option, the RCON port will change as well. SRCDS will always refuse any RCON connection attempt originating from an IP on its banlist.
Once a connection is established, it must be authenticated with a SERVERDATA_AUTH packet before any commands can be issued (see "Requests and Responses" below).
An RCON connection can have multiple outstanding requests at any given time; it is not necessary to wait for a response to one request before writing another. These requests are guaranteed to be executed in the order they are received, and responses will be sent out in the same order. However, if an implementation of this protocol allows for requests to be executed asynchronously, there is the possibility that the responses could be sent out of order. In this case, responses can be identified using their ID. See "Multiple-packet Responses" and "SERVERDATA_RESPONSE_VALUE"
If the rcon_password cvar is written to for any reason, the server will immediately close all authenticated RCON connections. This happens even if the new value of rcon_password is identical to the old one. These connections will need to be re-opened and re-authenticated before any further commands can be issued. Connections which have not been authenticated yet are not dropped. If the password was changed remotely, the server will not respond to the command which caused the password to change.
Basic Packet Structure
Both requests and responses are sent as TCP packets. Their payload follows the following basic structure:
Field | Type | Value |
---|---|---|
Size | 32-bit little-endian Signed Integer | Varies, see below. |
ID | 32-bit little-endian Signed Integer | Varies, see below. |
Type | 32-bit little-endian Signed Integer | Varies, see below. |
Body | Null-terminated ASCII String | Varies, see below. |
Empty String | Null-terminated ASCII String | 0x00 |
Packet Size
The packet size field is a 32-bit little endian integer, representing the length of the request in bytes. Note that the packet size field itself is not included when determining the size of the packet, so the value of this field is always 4 less than the packet's actual length. The minimum possible value for packet size is 10:
Size | Containing |
---|---|
4 Bytes | ID Field |
4 Bytes | Type Field |
At least 1 Byte | Packet body (potentially empty) |
1 Bytes | Empty string terminator |
Since the only one of these values that can change in length is the body, an easy way to calculate the size of a packet is to find the byte-length of the packet body, then add 10 to it.
The maximum possible value of packet size is 4096. If the response is too large to fit into one packet, it will be split and sent as multiple packets. See "Multiple-packet Responses" below for more information on how to determine when a packet is split.
Packet ID
The packet id field is a 32-bit little endian integer chosen by the client for each request. It may be set to any positive integer. When the server responds to the request, the response packet will have the same packet id as the original request (unless it is a failed SERVERDATA_AUTH_RESPONSE packet - see below.) It need not be unique, but if a unique packet id is assigned, it can be used to match incoming responses to their corresponding requests.
Packet Type
The packet type field is a 32-bit little endian integer, which indicates the purpose of the packet. Its value will always be either 0, 2, or 3, depending on which of the following request/response types the packet represents:
Value | String Descriptor |
---|---|
3 | SERVERDATA_AUTH |
2 | SERVERDATA_AUTH_RESPONSE |
2 | SERVERDATA_EXECCOMMAND |
0 | SERVERDATA_RESPONSE_VALUE |
Note that the repetition in the above table is not an error: SERVERDATA_AUTH_RESPONSE and SERVERDATA_EXECCOMMAND both have a numeric value of 2.
See "Requests and Responses" below for an explanation of each packet type.
Packet Body
The packet body field is a null-terminated string encoded in ASCII (i.e. ASCIIZ). Depending on the packet type, it may contain either the RCON password for the server, the command to be executed, or the server's response to a request. See "Requests and Responses" below for details on what each packet type's body contains.
Empty String
The end of a Source RCON Protocol packet is marked by an empty ASCIIZ string - essentially just 8 bits of 0. In languages that do not null-terminate strings automatically, you can simply write 16 bits of 0 immediately after the packet body (8 to terminate the packet body and 8 for the empty string afterwards).
Requests and Responses
SERVERDATA_AUTH
Typically, the first packet sent by the client will be a SERVERDATA_AUTH packet, which is used to authenticate the connection with the server. The value of the packet's fields are as follows:
Field | Contains |
---|---|
ID | any positive integer, chosen by the client (will be mirrored back in the server's response) |
Type | 3 |
Body | the RCON password of the server (if this matches the server's rcon_password cvar, the auth will succeed) |
If the rcon_password cvar is not set, or if it is set to empty string, all SERVERDATA_AUTH requests will be refused.
SERVERDATA_EXECCOMMAND
This packet type represents a command issued to the server by a client. This can be a ConCommand such as mp_switchteams or changelevel, a command to set a cvar such as sv_cheats 1, or a command to fetch the value of a cvar, such as sv_cheats. The response will vary depending on the command issued.
Field | Contains |
---|---|
ID | any positive integer, chosen by the client (will be mirrored back in the server's response) |
Type | 2 |
Body | the command to be executed on the server |
SERVERDATA_AUTH_RESPONSE
This packet is a notification of the connection's current auth status. When the server receives an auth request, it will respond with an empty SERVERDATA_RESPONSE_VALUE, followed immediately by a SERVERDATA_AUTH_RESPONSE indicating whether authentication succeeded or failed. Note that the status code is returned in the packet id field, so when pairing the response with the original auth request, you may need to look at the packet id of the preceeding SERVERDATA_RESPONSE_VALUE.
Field | Contains |
---|---|
ID | If authentication was successful, the ID assigned by the request. If auth failed, -1 (0xFF FF FF FF) |
Type | 2 |
Body | Empty string (0x00) |
SERVERDATA_RESPONSE_VALUE
A SERVERDATA_RESPONSE_VALUE packet is the response to a SERVERDATA_EXECCOMMAND request.
Field | Contains |
---|---|
ID | The ID assigned by the original request |
Type | 0 |
Body | The server's response to the original command. May be empty string (0x00) in some cases. |
Note that particularly long responses may be sent in multiple SERVERDATA_RESPONSE_VALUE packets - see "Multiple-packet Responses" below.
Also note that requests executed asynchronously can possibly send their responses out of order[1] - using a unique ID to identify and associate the responses with their requests can circumvent this issue.
Multiple-packet Responses
Most responses are small enough to fit within the maximum possible packet size of 4096 bytes. However, a few commands such as cvarlist and, occasionally, status produce responses too long to be sent in one response packet. When this happens, the server will split the response into multiple SERVERDATA_RESPONSE_VALUE packets. Unfortunately, it can be difficult to accurately determine from the first packet alone whether the response has been split.
One common workaround is for the client to send an empty SERVERDATA_RESPONSE_VALUE packet after every SERVERDATA_EXECCOMMAND request. Rather than throwing out the erroneous request, SRCDS mirrors it back to the client, followed by another RESPONSE_VALUE packet containing 0x0000 0001 0000 0000 in the packet body field. Because SRCDS always responds to requests in the order it receives them, receiving a response packet with an empty packet body guarantees that all of the meaningful response packets have already been received. Then, the response bodies can simply be concatenated to build the full response.
Special thanks to Koraktor for discovering this trick.
Example Packets
The log below shows an example of communication between an RCON client and Source Dedicated Server. The client first sends an authentication request with password "passwrd", then executes commands "echo HLSW: Test", "log", and "status". Lines in red represent request packets, and lines in blue represent the server's responses.
00000000 11 00 00 00 00 00 00 00 03 00 00 00 70 61 73 73 ........ ....pass 00000010 77 72 64 00 00 wrd.. 00000000 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 ........ ...... 0000000E 0a 00 00 00 00 00 00 00 02 00 00 00 00 00 ........ ...... 00000015 19 00 00 00 00 00 00 00 02 00 00 00 65 63 68 6f ........ ....echo 00000025 20 48 4c 53 57 3a 20 54 65 73 74 00 00 HLSW: T est.. 0000001C 17 00 00 00 00 00 00 00 00 00 00 00 48 4c 53 57 ........ ....HLSW 0000002C 20 3a 20 54 65 73 74 20 0a 00 00 : Test ... 00000032 0d 00 00 00 00 00 00 00 02 00 00 00 6c 6f 67 00 ........ ....log. 00000042 00 . 00000037 4c 00 00 00 00 00 00 00 00 00 00 00 55 73 61 67 L....... ....Usag 00000047 65 3a 20 20 6c 6f 67 20 3c 20 6f 6e 20 7c 20 6f e: log < on | o 00000057 66 66 20 3e 0a 63 75 72 72 65 6e 74 6c 79 20 6c ff >.cur rently l 00000067 6f 67 67 69 6e 67 20 74 6f 3a 20 66 69 6c 65 2c ogging t o: file, 00000077 20 63 6f 6e 73 6f 6c 65 2c 20 75 64 70 0a 00 00 console , udp... 000000D6 10 00 00 00 00 00 00 00 02 00 00 00 73 74 61 74 ........ ....stat 000000E6 75 73 00 00 us.. 00000134 e4 00 00 00 00 00 00 00 00 00 00 00 68 6f 73 74 ........ ....host 00000144 6e 61 6d 65 3a 20 54 61 73 6b 66 6f 72 63 65 72 name: Ta skforcer 00000154 61 6e 67 65 72 2e 6e 65 74 20 54 46 32 20 2d 20 anger.ne t TF2 - 00000164 54 65 61 6d 77 6f 72 6b 21 0a 76 65 72 73 69 6f Teamwork !.versio 00000174 6e 20 3a 20 31 2e 30 2e 31 2e 34 2f 31 34 20 33 n : 1.0. 1.4/14 3 00000184 33 34 35 20 73 65 63 75 72 65 20 0a 75 64 70 2f 345 secu re .udp/ 00000194 69 70 20 20 3a 20 20 38 2e 32 2e 30 2e 32 38 3a ip : 8 .2.0.28: 000001A4 32 37 30 31 35 0a 6d 61 70 20 20 20 20 20 3a 20 27015.ma p : 000001B4 74 63 5f 68 79 64 72 6f 20 61 74 3a 20 30 20 78 tc_hydro at: 0 x 000001C4 2c 20 30 20 79 2c 20 30 20 7a 0a 70 6c 61 79 65 , 0 y, 0 z.playe 000001D4 72 73 20 3a 20 30 20 28 32 30 20 6d 61 78 29 0a rs : 0 ( 20 max). 000001E4 0a 23 20 75 73 65 72 69 64 20 6e 61 6d 65 20 75 .# useri d name u 000001F4 6e 69 71 75 65 69 64 20 63 6f 6e 6e 65 63 74 65 niqueid connecte 00000204 64 20 70 69 6e 67 20 6c 6f 73 73 20 73 74 61 74 d ping l oss stat 00000214 65 20 61 64 72 0a 00 00 e adr...
Packet Construction Examples
VB.Net
Written by Tuck and veN.
Private Function RCON_Command(ByVal Command As String, ByVal ServerData As Integer) As Byte()
Dim Packet As Byte() = New Byte(CByte((13 + Command.Length))) {}
Packet(0) = Command.Length + 9 'Packet Size (Integer)
Packet(4) = 0 'Request Id (Integer)
Packet(8) = ServerData 'SERVERDATA_EXECCOMMAND / SERVERDATA_AUTH (Integer)
For X As Integer = 0 To Command.Length - 1
Packet(12 + X) = System.Text.Encoding.Default.GetBytes(Command(X))(0)
Next
Return Packet
End Function
C++
.h file code:
#include <bit>
#include <concepts>
#include <ostream>
#include <type_traits>
std::ostream &write_le_to(std::ostream &dest, std::integral auto value) {
if constexpr (std::endian::native == std::endian::little) {
dest.write(reinterpret_cast<const char *>(&value), sizeof(value));
} else {
for (size_t i = 0; i != sizeof(value); ++i) {
dest.write(
reinterpret_cast<const char *>(&value) + (sizeof(value) - 1 - i), 1);
}
}
return dest;
}
static std::ostream &write_rcon_to(std::ostream &dest, int32_t type, int32_t id,
std::string_view body) {
const char nullbytes[] = {'\x00', '\x00'};
const int32_t minsize = sizeof(id) + sizeof(type) + sizeof(nullbytes);
const int32_t size = static_cast<int32_t>(body.size() + minsize);
write_le_to(dest, size);
write_le_to(dest, id);
write_le_to(dest, type);
dest.write(body.data(), body.size());
dest.write(nullbytes, sizeof(nullbytes));
return dest;
}
Node.js
Written by Speedhaxx, edited by 2006kiaoptima
function createRequest(type, id, body) {
// Size, in bytes, of the whole packet.
let size = Buffer.byteLength(body) + 14;
let buffer = Buffer.alloc(size);
buffer.writeInt32LE(size - 4, 0);
buffer.writeInt32LE(id, 4);
buffer.writeInt32LE(type, 8);
buffer.write(body, 12, size - 2, "ascii");
// String terminator and 8 empty bits
buffer.writeInt16LE(0, size - 2);
return buffer;
};
function readResponse(buffer) {
let response = {
size: buffer.readInt32LE(0),
id: buffer.readInt32LE(4),
type: buffer.readInt32LE(8),
body: buffer.toString("ascii", 12, buffer.length - 2);
}
return response;
};
Haskell
import Data.Bytestring as B
import Data.Bytestring.Lazy as BL
import Data.Bytestring.UTF8 as UTF8
import Data.Binary
import Data.Int
createPackage :: String -> Int32 -> B.ByteString
createPackage command idInt =
B.concat [encodeS sizeInt, id, reqType, body, null]
where
sizeInt :: Int32
sizeInt = fromIntegral (B.length $ UTF8.fromString command) + (10 :: Int32)
id = encodeS idInt
reqType = encodeS (2 :: Int32)
body = UTF8.fromString command
null = UTF8.fromString "\0\0"
encodeS :: Binary a => a -> B.ByteString
encodeS = BL.toStrict . encode
Source RCON Libraries
The following library implementations of Source RCON Protocol, listed alphabetically by language, may be useful for developers looking to include RCON functionality in their programs:
C
C++
- C++ with wxWidgets Tool including attempt to recreate a C++ Class in the platform-independent framework
- SRCON C++ class exposing a simple interface
- rcon++ A modern Source RCON library for C++
C#
- SourceRcon C# Library and Sample Program
- CoreRCON C#/.NET Core Library and Sample Program
- QueryMaster C# Library
- rconsharp RCON protocol implementation written in C# targeting netstandard 2.1
Golang
- go-steam RCON, UDP (Ping, Player Info, Server Info) Library in Go (documentation)
- rconGo Golang 实现新版协议RCON,同时支持 A2S (Player Info, Server Info, Rules)
- gorcon Source RCON Protocol implementation in Go
Java
- Asynchronous Game Query Library A Java 8 based Asynchronous Game Query Library powered by Netty
- Steam Condenser RCON, Master Server, and Web API support for Java, PHP, and Ruby
- RConEd Java RCON Library for Source (also HL1, COD/2, and BF2)
- rkon-core Java Source RCON library
- Rcon and A2s WebApi中文的api协议库,基于Spring boot
Perl 5
- Net::RCON perl module that provides an interface to an RCON server.
Perl 6
- Net::RCON Perl 6 module for interacting with the Source RCON (remote console) protocol.
PHP
- Steam Condenser RCON, Master Server, and Web API support for Java, PHP, and Ruby
- PHP RCON class for Source.
Python
- Rcon An RCON protocol client implementation.
- SRCDSpy Python Library to handle all Source Queries, including RCON
- SourceLib Yet another Python implementation of Valve Source Dedicated Server Query/RCON/Log protocols
- pysrcds Another Python library to handle Source RCON and log protocols
- aiorcon An asynchronous Python RCON library
Ruby
- Steam Condenser RCON, Master Server, and Web API support for Java, PHP, and Ruby
- Ruby RCon RCon library for Source and Quake RCON derivatives (such as HLDS) written in Ruby.
Node.js
- srcds-rcon RCON protocol library for Source.
- @fabricio-191/valve-server-query Server, Master Server and RCON protocols library for Source.