Skip to content

Instantly share code, notes, and snippets.

@fakhrulhilal
Created July 28, 2019 12:06
Show Gist options
  • Save fakhrulhilal/ee4af38fe5f0c6057aea571f88dcbac6 to your computer and use it in GitHub Desktop.
Save fakhrulhilal/ee4af38fe5f0c6057aea571f88dcbac6 to your computer and use it in GitHub Desktop.
Manage CS GO dedicated server using RCON protocol
using namespace System.IO
using namespace System.Net
using namespace System.Net.Sockets
class RconClient {
hidden [Socket]$_socket
hidden [int]$_id = 1
RconClient([Socket]$socket) {
$this._socket = $socket
}
[void]Close() {
$this._socket.Disconnect($false)
$this._socket.Dispose()
}
}
class RconResponsePacket {
static [int]$MaxPacketLength = 4096
[int]$Id
[string]$Type
[string]$Body
RconResponsePacket([int]$id, [string]$type, [string]$body) {
$this.Id = $id
$this.Type = $type
$this.Body = $body
}
}
Function Connect-ServerPort {
[OutputType([System.Net.Sockets.Socket])]
param(
# Server address, can be IPv4 or DNS name
[Parameter(Mandatory=$True)]
[string]
[ValidateNotNull()]
$Address,
# Server port
[Parameter(Mandatory=$True)]
[int]
$Port
)
$ServerAddress = If ($Address -match '^(\d{1,3}\.){3}\d{1,3}$') {
[ipaddress]::Parse($Address)
} Else {
[Dns]::GetHostAddresses($Address) | ?{ $_.AddressFamily -eq [AddressFamily]::InterNetwork } | select -First 1
}
$Endpoint = [IPEndpoint]::new($ServerAddress, $Port)
$Socket = [Socket]::new([AddressFamily]::InterNetwork, [SocketType]::Stream, [ProtocolType]::Tcp)
$Socket.Connect($Endpoint)
Return $Socket
}
Function ConvertTo-CSGOPacket {
[OutputType([byte[]])]
param(
# packet ID
[Parameter(Mandatory=$True)]
[int]
$Id,
# packet type
[Parameter(Mandatory=$True)]
[ValidateSet('Auth', 'Command')]
[string]
$Type,
# packet body
[Parameter(Mandatory=$True)]
[ValidateNotNull()]
[string]
$Body
)
$BodyBytes = [System.Text.Encoding]::UTF8.GetBytes($Body + "\0")
$BodyLength = $BodyBytes.Length
$TypeId = If ('Auth' -eq $Type) { 3 } Else { 2 }
$Stream = [MemoryStream]::new(12 + $BodyLength)
try {
$Stream.Write([System.BitConverter]::GetBytes(9 + $BodyLength), 0, 4)
$Stream.Write([System.BitConverter]::GetBytes($Id), 0, 4)
$Stream.Write([System.BitConverter]::GetBytes(($TypeId)), 0, 4)
$Stream.Write($BodyBytes, 0, $BodyLength)
$Stream.Write(([byte]0), 0, 1)
Return $Stream.ToArray()
}
finally {
$Stream.Dispose()
}
}
Function ConvertFrom-CSGOPacket {
[OutputType('RconResponsePacket')]
param(
# packet bytes
[Parameter(Mandatory=$True)]
[byte[]]
$Buffer
)
$Size = [System.BitConverter]::ToInt32($Buffer, 0)
If ($Size -lt 10) {
Write-Error 'Invalid packet received' -ErrorAction Stop
}
$Id = [System.BitConverter]::ToInt32($Buffer, 4)
switch ([System.BitConverter]::ToInt32($Buffer, 8)) {
0 { $Type = 'Command' }
2 { $Type = 'Auth'}
Default { $Type = 'Unknown' }
}
try {
$RawBody = [System.Text.Encoding]::UTF8.GetChars($Buffer, 12, ($Size - 10))
$Body = ([string]::new($RawBody, 0, ($Size - 10))).TrimEnd()
$Body = $Body -replace "\r\n|\n\r|\n|\r", "`r`n"
Return [RconResponsePacket]::new($Id, $Type, $Body)
}
catch {
Write-Warning 'Error reading RCON packet: ' + $_.Exception.Message
Return [RconResponsePacket]::new($Id, $Type, [string]::Empty)
}
}
Function Connect-CSGODedicatedServer {
[OutputType('RconClient')]
param(
# server address, can be IPv4 or DNS name
[Parameter(Mandatory=$True)]
[string]
[ValidateNotNull()]
$Address,
# Port (default: 27015)
[Parameter(Mandatory=$False)]
[int]
$Port = 27015,
# RCON server password
[Parameter(Mandatory=$True, Position=1)]
[ValidateNotNull()]
[string]
$Password
)
$Socket = Connect-ServerPort -Address $Address -Port $Port
$Client = [RconClient]::new($Socket)
$AuthPacket = ConvertTo-CSGOPacket -Id $Client._id -Type Auth -Body $Password
$Socket.Send($AuthPacket, [SocketFlags]::None)
$Buffer = [byte[]]::new([RconResponsePacket]::MaxPacketLength)
$Socket.Receive($Buffer, 0, $Socket.Available, [SocketFlags]::None)
$Response = [RconResponsePacket](ConvertFrom-CSGOPacket -Buffer $Buffer)
$Client._id = $Client._id + 1
Return $Client
}
Function Start-CSGODedicatedServer {
[OutputType([RconClient])]
param(
# server port (default: 27015)
[Parameter(Mandatory=$False)]
[int]
$Port = 27015
)
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment