Source RCON Protocol: Difference between revisions

From Valve Developer Community
Jump to navigation Jump to search
(Added packet structure client-server examples)
m (Node.js - Bytes, not bits)
 
(89 intermediate revisions by 48 users not shown)
Line 1: Line 1:
This article was taken from a HLDS_APPS posting by [[User:Alfred|Alfred Reynolds]].
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 [[ConVar|console variable]] ''rcon_password''.


Additional Remarks have been added to Alfred's Documentation. This is taken from the [http://wikki.kquery.net/ KQuery Wiki], as unfortunately the page is defaced. One can dig deeply through the revision history to obtain the information, some of which is vital to creating a working implementation.. I have taken the liberty of echoing it here.
==Using Source RCON==


==Source Server RCON format==
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.


The protocol is based around command/response packets encapsulated in a TCP/IP stream. The stream can have multiple outstanding commands and can be extended to allow for multiple sub-channels of data.
Once a connection is established, it must be authenticated with a '''SERVERDATA_AUTH''' packet before any commands can be issued (see [[Source RCON Protocol#Requests and Responses|"Requests and Responses"]] below).


===Sending===
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 [[Source RCON Protocol#Multiple-packet Responses|"Multiple-packet Responses"]] and [[Source RCON Protocol#SERVERDATA_RESPONSE_VALUE|"SERVERDATA_RESPONSE_VALUE"]]
The command packet format consists of:
* '''packet size (int)'''
** the number of bytes from the start of the requestid to the end of string2 (including the null byte). It must be at least 10.
* '''request id (int)'''
* '''SERVERDATA_EXECCOMMAND / SERVERDATA_AUTH (int)'''
** SERVERDATA_AUTH is currently 3
** SERVERDATA_EXECCOMMAND is currently 2
* '''string1''' (is the command to run).
* '''string2''' must be null ("");
For RCON connections the first command must be a SERVERDATA_AUTH command. If a SERVERDATA_EXECCOMMAND command is sent prior to successful authentication then a SERVERDATA_AUTH_RESPONSE response packet with the failure condition is sent (see the response section for details). The INT's are 32 Bit Little Endian byte order.


===Receiving===
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.


The response packet is the same as the command packet, which is:
==Basic Packet Structure==
* '''packet size (int)'''
** '''If the response spans over multiple packets only the first has these fields. The Other packets are just data.'''
*** It seems to me, that this has been changed, my response are including those fields. --[[User:Plastofix|Plastofix]] 18:46, 19 Dec 2007 (PST)
* '''request id (int)'''
* '''command response (int)'''
**valid command responses being:<BR>SERVERDATA_RESPONSE_VALUE = 0<BR>or SERVERDATA_AUTH_RESPONSE = 2
* '''string1 (null delimited string)'''
* '''string2 (null delimited string)'''


SERVERDATA_AUTH_RESPONSE is sent in response to a SERVERDATA_AUTH command (or to a SERVERDATA_EXECCOMMAND command if the  connection is not successfully authenticated). Both strings are set to null. If the request id is -1 (0xffffffff) then the  authentication attempt failed (due to a bad password). If the request id is the same value as sent in the command (i.e the  value was mirrored back) then authentication was successful. Any other request id is an error and the SERVERDATA_AUTH command should be resent.
Both requests and responses are sent as TCP packets. Their payload follows the following basic structure:


SERVERDATA_RESPONSE_VALUE is sent in response to a SERVERDATA_EXECCOMMAND command. string1 contains the response to the command and string2 is null (""). string1 is at most 4096 characters, so a single SERVERDATA_EXECCOMMAND command may result  in multiple SERVERDATA_RESPONSE_VALUE response packets.
{| class=standard-table
! Field || Type ||| Value
|-
| Size || 32-bit [[wiki:Endianness|little-endian]] Signed [[Integer]] || Varies, see below.
|-
| ID || 32-bit [[wiki:Endianness|little-endian]] Signed [[Integer]] || Varies, see below.
|-
| Type || 32-bit [[wiki:Endianness|little-endian]] Signed [[Integer]] || Varies, see below.
|-
| Body || [[wiki:Null-terminated string|Null-terminated]] [[wiki:ASCII|ASCII]] [[String]] || Varies, see below.
|-
| Empty String || [[wiki:Null-terminated string|Null-terminated]] [[wiki:ASCII|ASCII]] [[String]] || 0x00
|-
|}


==Additional Comments==
===Packet Size===


These are additional comments added by an anonymous reader on the KQuery wiki. These are accurate and recommended (and in some cases required) for proper implementation of the RCON protocol in Source.
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:


* Request ID is mirrored back properly
{| class=standard-table
* CS:Source server sends one junk packet during the authentication step, before it responds with the correct authentication response.
! Size || Containing
** It seems this junk packet is also valid under HL2:DM, not just CS:Source, so probably applies to all Source games (unless Rcon stuff is done differently in other Source powered games by 3rd parties). --[[User:Bartk|Bartk]] 12:13, 2 Oct 2006 (PDT)
|-
* The packet size field does NOT include the size of the packet size field itself. That is why the minimum length is 10 -- four bytes for the two integers (request ID and command) plus two bytes for the potentially empty ASCIIZ strings.
| 4 [[Byte|Bytes]] || ID Field
* Make sure that you code the ability to handle multiple response "packets". With a 32-player server, it will use these commonly for status commands (for instance), just as HLDS often did. Also, keep in mind that you won't be able to read the whole "packet" at one time -- you need to keep reading in a loop until you receive the entire packet before processing it.
|-
| 4 [[Byte|Bytes]] || Type Field
|-
| At least 1 [[Byte]] || Packet body (potentially empty)
|-
| 1 [[Byte|Bytes]] || Empty string terminator
|}


Other User Comments:
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.


* For a detailed packet structure, run Wireshark (ethereal) in tandem with hlsw and follow the TCP packet stream. --[[User:TGAcid|TGAcid]] 13:22, 13 Jan 2008 (PST)
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 [[Source RCON Protocol#Multiple-packet Responses|"Multiple-packet Responses"]] below for more information on how to determine when a packet is split.


==Packet Structure Client/Server Communication Examples==
===Packet ID===


Example of TCP communication authentication request with rcon password "passwrd", then subsequent rcon commands and responses for "echo HLSW: Test", "log", and "status" --[[User:TGAcid|TGAcid]] 13:22, 13 Jan 2008 (PST)
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.
00000000  11 00 00 00 00 00 00 00  '''03''' 00 00 00 62 34 62 79 ........ ....pass          <-- password is 7 chars long
00000010  6c 30 6e 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...


==Implementations==
===Packet Type===


A (hopefully comprehensive) listing of library implementations of the Source RCON protocol.
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:


{| class=standard-table
! 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 [[Source RCON Protocol#Requests and Responses|"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 [[Source RCON Protocol#Requests and Responses|"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:
{| class=standard-table
! 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 [[ConVar|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.
{| class=standard-table
! 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.
{| class=standard-table
! 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.
{| class=standard-table
! 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 [[Source RCON Protocol#Multiple-packet Responses|"Multiple-packet Responses"]]  below.
Also note that requests executed asynchronously can possibly send their responses out of order[https://forums.factorio.com/viewtopic.php?p=528944#p528944] - 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 [[User:Koraktor|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.
<span style="color:#ff0000">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..</span>
<span style="color:#0000ff">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      ........ ......</span>
<span style="color:#ff0000">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..</span>
<span style="color:#0000ff">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  ...</span>
<span style="color:#ff0000">00000032  0d 00 00 00 00 00 00 00  '''02''' 00 00 00 6c 6f 67 00 ........ ....log.
00000042  00                                              .</span>
<span style="color:#0000ff">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...</span>
<span style="color:#ff0000">000000D6  10 00 00 00 00 00 00 00  '''02''' 00 00 00 73 74 61 74 ........ ....stat
000000E6  75 73 00 00                                      us..</span>
<span style="color:#0000ff">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...</span>
==Packet Construction Examples==
===VB.Net===
''Written by [[User:VTuck|Tuck]] and veN.''
<source lang=vbnet>
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
</source>
===C++===
.h file code:
<source lang=C++>
#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;
}
</source>
===Node.js===
''Written by [[User:Speedhaxx|Speedhaxx]], edited by [[User:2006kiaoptima|2006kiaoptima]]''
<source lang=javascript>
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;
};
</source>
===Haskell===
<source lang=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>
==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'''
* [https://github.com/n0la/rcon rcon] A Linux command line tool where [https://github.com/n0la/rcon/blob/master/srcrcon.c srcrcon.c]/[https://github.com/n0la/rcon/blob/master/srcrcon.h srcrcon.h] are the protocol implementation.
'''C++'''
* [http://hehoe.de/mcs/downloads/L4DDSS_src.zip C++ with wxWidgets] Tool including attempt to recreate a C++ Class in the platform-independent framework
* [https://github.com/Phil25/SourceRCON SRCON] C++ class exposing a simple interface
* [https://github.com/Jaskowicz1/rconpp rcon++] A modern Source RCON library for C++
'''C#'''
* [http://github.com/aiusepsi/SourceRcon SourceRcon] C# Library and Sample Program
* [https://github.com/Challengermode/CoreRcon CoreRCON] C#/.NET Core Library and Sample Program
* [https://github.com/armageddonapps/QueryMaster QueryMaster] C# Library
* [https://github.com/stefanodriussi/rconsharp rconsharp] RCON protocol implementation written in C# targeting netstandard 2.1
'''Golang'''
* [https://github.com/kidoman/go-steam go-steam] RCON, UDP (Ping, Player Info, Server Info) Library in Go ([http://godoc.org/github.com/kidoman/go-steam documentation])
* [https://github.com/xv-chang/rconGo rconGo] Golang 实现新版协议RCON,同时支持 A2S (Player Info, Server Info, Rules)
* [https://github.com/gorcon/rcon gorcon] Source RCON Protocol implementation in Go
'''Java'''
* [https://github.com/ribasco/async-gamequery-lib 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
* [http://rconed.sf.net RConEd] Java RCON Library for Source (also HL1, COD/2, and BF2)
* [https://github.com/Kronos666/rkon-core rkon-core] Java Source RCON library
* [https://github.com/hundunzhidian/RconA2sAPi Rcon and A2s WebApi]中文的api协议库,基于Spring boot
'''Perl 5'''
* [https://metacpan.org/pod/Net::RCON Net::RCON] perl module that provides an interface to an RCON server.
'''Perl 6'''
* [https://github.com/shuppet/p6-net-rcon 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
* [http://fremnet.net/article/199/source-rcon-class PHP] RCON class for Source.
'''Python'''
* [https://github.com/coNQP/rcon Rcon] An RCON protocol client implementation.
* [http://sourceforge.net/projects/srcdspy/ SRCDSpy] Python Library to handle all Source Queries, including RCON
* [http://github.com/frostschutz/SourceLib SourceLib] Yet another Python implementation of Valve Source Dedicated Server Query/RCON/Log protocols
* [http://pypi.python.org/pypi/pysrcds pysrcds] Another Python library to handle Source RCON and log protocols
* [https://github.com/skmendez/aiorcon aiorcon] An asynchronous Python RCON library
'''Ruby'''
* [[Steam Condenser]] RCON, Master Server, and Web API support for Java, PHP, and Ruby
* [http://rubyforge.org/projects/rcon/ Ruby RCon] RCon library for Source and Quake RCON derivatives (such as HLDS) written in Ruby.
* [http://rubyforge.org/projects/rcon/ Ruby RCon] RCon library for Source and Quake RCON derivatives (such as HLDS) written in Ruby.
* [http://home.amnet.net.au/~mostar/rcon.rar C++] Sample class to do Source RCON in C++
* [http://www.asyserver.com/~cstrike/rcon.c CLI] A command-line program to issue RCON commands, written in C
* [http://sourceforge.net/projects/srcdspy/ Python SRCDSpy] Python Library to handle all Source Queries, including RCON
* [http://home.no/eks/SourceRcon/SourceRconLib.zip C#] C# Library and Sample Program (by Andrew Simpson)
* [http://rconed.sf.net Java] Java RCON Library for Source (also HL1, COD/2, and BF2)
* [http://www.thedragonmaster.net/cs/RCON2.rar VB6] VisualBasic 6.0 (Source RCON only) sample up to you to process DataArrival (by: Jaime Gavin)


[[Category:Programming]]
'''Node.js'''
[[Category:Technical]]
* [https://github.com/randunel/node-srcds-rcon srcds-rcon] RCON protocol library for Source.
* [https://www.npmjs.com/package/@fabricio-191/valve-server-query @fabricio-191/valve-server-query] Server, Master Server and RCON protocols library for Source.
 
== See also ==
 
* [[Master Server Query Protocol]]
* [[Server queries]]
 
[[Category:Dedicated Server]]

Latest revision as of 21:15, 3 February 2024

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.

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

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

See also