public/GoldSourceRcon.ps1

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
<#
.SYNOPSIS
Performs a Goldsource Rcon.

.DESCRIPTION
Performs a Goldsource Rcon.

.PARAMETER Address
DNS or IP address.

.PARAMETER Port
Port.

.PARAMETER Password
Rcon password.

.PARAMETER Command
Rcon command.

.EXAMPLE
GoldsourceRcon -Address $address -Port $port -Password $rcon_password -Command 'status'

.NOTES
Goldsource: https://developer.valvesoftware.com/wiki/Goldsource
Goldsource Rcon: https://developer.valvesoftware.com/wiki/Source_RCON_Protocol
Goldsource Rcon python code: https://forums.alliedmods.net/showpost.php?p=1718732&postcount=3
#>

function GoldSourceRcon {
    [CmdletBinding()]
    param(
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Address
    ,
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [int]$Port
    ,
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Password
    ,
        [parameter(Mandatory=$true)]
        [ValidateNotNullOrEmpty()]
        [string]$Command
    )

    try {
        # Determine the IP
        $Address = Resolve-DNS -Address $Address

        Write-Verbose "Sending GoldSourceRcon to $Address`:$Port"

        $enc = [system.Text.Encoding]::UTF8

        # Set up UDP Socket
        $remoteEP  = New-Object System.Net.IPEndPoint([System.Net.IPAddress]::Parse($Address), $Port)
        $udpClient = New-Object System.Net.Sockets.UdpClient
        $udpClient.Client.SendTimeout = 500
        $udpClient.Client.ReceiveTimeout = 500
        $udpClient.Connect($remoteEP)

        function BuildPacket ([string]$Command) {
            $pack = @(255,255,255,255) + $enc.GetBytes($Command) + 0
            $pack
        }
        function SendPacket ([byte[]]$pack) {
            Debug-Packet $MyInvocation.MyCommand.Name $pack
            $udpClient.Send($pack, $pack.Length) > $null
        }
        function ReceivePacket {
            $pack = $udpClient.Receive([ref]$remoteEP)
            Debug-Packet $MyInvocation.MyCommand.Name $pack
            $pack
        }
        function GetResponse ([byte[]]$pack) {
            $response = $enc.GetString( $pack[5..($pack.Length - 1)] )
            $response
        }
        function Init {
            # 1 - Client sends: \xFF\xFF\xFF\xFFchallenge rcon\n\0
            $pack = BuildPacket "challenge rcon`n"
            # 2 - Server replies with challenge id
            $response = SendReceive $pack
            if ($response -match '(\d+)') {
                $challengeID = $matches[1]
                Write-Verbose "Got challengeID: $challengeID"
                $challengeID
            }
        }
        function SendReceive ([byte[]]$pack) {
            SendPacket $pack
            $rPack = ReceivePacket
            $response = GetResponse $rPack
            $response
        }

        function Debug-Packet ($label, $pack) {
            if ($pack) {
                Write-Verbose "[$label]"
                #Write-Verbose "pack: $pack"
                Write-Verbose "pack: $( $pack | % { $_.ToString('X2').PadLeft(2) } )"
                Write-Verbose "pack: "
                Write-Verbose "$( $pack | % { if ($_ -eq 0x00) { "\".PadLeft(2) } else { [System.Text.Encoding]::Utf8.GetString($_).Trim().PadLeft(2) } } )"
                Write-Verbose "length: $($pack.Length)"
                Write-Verbose ""
            }
        }

        # Rcon
        $challengeID = Init
        if (!$challengeID) {
            throw "No challengeID."
        }else {
            # 3 - Client sends: \xFF\xFF\xFF\xFFrcon CHALLENGEID RCONPASSWORD COMMAND
            $pack = BuildPacket "rcon $challengeID $Password $Command"
            # 4 - Server replies with plain text
            $response = SendReceive $pack
            $udpClient.Dispose()
            if ($response -match 'Bad rcon_password') {
                throw $response
            }
            $response
        }
    }catch {
        if ($ErrorActionPreference -eq 'Stop') {
            throw
        }else {
            Write-Error -ErrorRecord $_
        }
    }
}