Skip to content
This repository was archived by the owner on Dec 23, 2022. It is now read-only.

Commit 752d085

Browse files
committed
Custom command event
Updated README Better exception handling Added more documentation Upped version to 1.1
1 parent 99f6c6b commit 752d085

14 files changed

+356
-135
lines changed

RCON.sln

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11

22
Microsoft Visual Studio Solution File, Format Version 12.00
3-
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCONServer", "RCONServer\RCONServer.csproj", "{EDB656EE-634B-4080-8293-73205470FCCF}"
3+
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCONServerLibExample", "RCONServerLibExample\RCONServerLibExample.csproj", "{EDB656EE-634B-4080-8293-73205470FCCF}"
44
EndProject
55
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCONServerLib", "RCONServerLib\RCONServerLib.csproj", "{2EFA0311-14CE-414B-9867-FC0D1FF6F6C4}"
66
EndProject

RCONServerLib/RCONServerLib.csproj

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -11,7 +11,7 @@
1111
<AssemblyName>RCONServerLib</AssemblyName>
1212
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
1313
<FileAlignment>512</FileAlignment>
14-
<LangVersion>6</LangVersion>
14+
<LangVersion>5</LangVersion>
1515
</PropertyGroup>
1616
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
1717
<PlatformTarget>AnyCPU</PlatformTarget>
@@ -34,9 +34,6 @@
3434
</PropertyGroup>
3535
<ItemGroup>
3636
<Reference Include="System" />
37-
<Reference Include="System.Core" />
38-
<Reference Include="System.Data" />
39-
<Reference Include="System.Xml" />
4037
</ItemGroup>
4138
<ItemGroup>
4239
<Compile Include="RemoteConClient.cs" />

RCONServerLib/RCONServerLib.nuspec

Lines changed: 23 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,21 +1,25 @@
11
<?xml version="1.0"?>
2-
<package >
3-
<metadata>
4-
<id>source-rcon-server</id>
5-
<version>1.0.0</version>
6-
<title>Source RCON Server Lib</title>
7-
<authors>Julien</authors>
8-
<owners>Julien</owners>
9-
<licenseUrl>https://github.com/Subtixx/source-rcon-library/blob/master/LICENSE</licenseUrl>
10-
<projectUrl>https://github.com/Subtixx/source-rcon-library/</projectUrl>
11-
<!--<iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>-->
12-
<requireLicenseAcceptance>false</requireLicenseAcceptance>
13-
<description>Single class solution to create a source compatible RCON interface</description>
14-
<releaseNotes>Initial Release.</releaseNotes>
15-
<copyright>Copyright Subtixx 2018</copyright>
16-
<tags>source-rcon-server rconserver remoteconsole rcon-server</tags>
17-
</metadata>
18-
<files>
19-
<file src="bin\Release\RCONServerLib.dll" target="lib\net35\RCONServerLib.dll" />
20-
</files>
2+
<package>
3+
<metadata>
4+
<id>source-rcon-server</id>
5+
<version>1.0.0</version>
6+
<title>Source RCON Server Lib</title>
7+
<authors>Julien H. (Subtixx)</authors>
8+
<owners>Julien H. (Subtixx)</owners>
9+
<licenseUrl>https://github.com/Subtixx/source-rcon-library/blob/master/LICENSE</licenseUrl>
10+
<projectUrl>https://github.com/Subtixx/source-rcon-library/</projectUrl>
11+
<iconUrl>https://user-images.githubusercontent.com/20743379/35411973-0227102e-021b-11e8-9a1b-023e08c33c4e.png</iconUrl>
12+
<requireLicenseAcceptance>false</requireLicenseAcceptance>
13+
<description>
14+
Easy to use, single-class solution to create a Valve source RCON server which supports authentication,
15+
IP Whitelisting and a command manager
16+
</description>
17+
<summary>Single class solution to create a source compatible RCON interface</summary>
18+
<releaseNotes>Initial Release.</releaseNotes>
19+
<copyright>Copyright Subtixx 2018</copyright>
20+
<tags>source-rcon-server rconserver remoteconsole rcon-server</tags>
21+
</metadata>
22+
<files>
23+
<file src="bin\Release\RCONServerLib.dll" target="lib\net35\RCONServerLib.dll"/>
24+
</files>
2125
</package>

RCONServerLib/RemoteConClient.cs

Lines changed: 112 additions & 60 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,6 @@
11
using System;
2+
using System.Diagnostics;
3+
using System.Net;
24
using System.Net.Sockets;
35
using RCONServerLib.Utils;
46

@@ -9,18 +11,50 @@ namespace RCONServerLib
911
/// </summary>
1012
internal class RemoteConClient
1113
{
14+
/// <summary>
15+
/// The maximum packet size to receive
16+
/// </summary>
1217
private const int MaxAllowedPacketSize = 4096;
18+
19+
/// <summary>
20+
/// Used to determine if we're in unit test mode (Means no actual connection)
21+
/// </summary>
22+
private readonly bool _isUnitTest;
23+
24+
/// <summary>
25+
/// Underlaying NetworkStream
26+
/// </summary>
1327
private readonly NetworkStream _ns;
28+
29+
/// <summary>
30+
/// Reference to RemoteConServer for getting settings
31+
/// </summary>
1432
private readonly RemoteConServer _remoteConServer;
33+
34+
/// <summary>
35+
/// The TCP Client
36+
/// </summary>
1537
private readonly TcpClient _tcp;
16-
internal bool Authenticated;
38+
39+
/// <summary>
40+
/// How many failed login attempts the client has
41+
/// </summary>
1742
private int _authTries;
1843

44+
/// <summary>
45+
/// A buffer containing the packet
46+
/// </summary>
1947
private byte[] _buffer;
2048

49+
/// <summary>
50+
/// Wether or not the client is connected
51+
/// </summary>
2152
private bool _connected;
2253

23-
private readonly bool _isUnitTest;
54+
/// <summary>
55+
/// Has the client been successfully authenticated
56+
/// </summary>
57+
internal bool Authenticated;
2458

2559
public RemoteConClient(TcpClient tcp, RemoteConServer remoteConServer)
2660
{
@@ -64,7 +98,7 @@ private void CloseConnection()
6498
{
6599
if (_isUnitTest)
66100
return;
67-
101+
68102
_connected = false;
69103

70104
if (!_tcp.Connected)
@@ -83,7 +117,7 @@ private void SendPacket(RemoteConPacket packet)
83117
{
84118
if (_isUnitTest)
85119
return;
86-
120+
87121
if (!_connected)
88122
throw new Exception("Not connected.");
89123

@@ -149,86 +183,104 @@ private void OnPacket(IAsyncResult result)
149183
}
150184

151185
/// <summary>
152-
/// Parses raw bytes to RemoteConPacket
186+
/// Parses raw bytes to RemoteConPacket
153187
/// </summary>
154188
/// <param name="rawPacket"></param>
155189
internal void ParsePacket(byte[] rawPacket)
156190
{
157-
var packet = new RemoteConPacket(rawPacket);
158-
159-
// Do not allow any other packets than auth to be sent when client is not authenticated
160-
if (!Authenticated)
191+
try
161192
{
162-
if (packet.Type != RemoteConPacket.PacketType.Auth)
193+
var packet = new RemoteConPacket(rawPacket);
194+
195+
// Do not allow any other packets than auth to be sent when client is not authenticated
196+
if (!Authenticated)
163197
{
164-
if (_isUnitTest)
165-
throw new NotAuthenticatedException();
166-
CloseConnection();
167-
}
198+
if (packet.Type != RemoteConPacket.PacketType.Auth)
199+
{
200+
if (_isUnitTest)
201+
throw new NotAuthenticatedException();
202+
CloseConnection();
203+
}
168204

169-
_authTries++;
205+
_authTries++;
170206

171-
if (packet.Payload == _remoteConServer.Password)
172-
{
173-
Authenticated = true;
207+
if (packet.Payload == _remoteConServer.Password)
208+
{
209+
Authenticated = true;
210+
211+
if (!_remoteConServer.SendAuthImmediately)
212+
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue, ""));
213+
214+
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ExecCommand, ""));
215+
return;
216+
}
217+
218+
if (_authTries >= _remoteConServer.MaxPasswordTries)
219+
{
220+
CloseConnection();
221+
return;
222+
}
174223

175224
if (!_remoteConServer.SendAuthImmediately)
176225
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue, ""));
177226

178-
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ExecCommand, ""));
227+
SendPacket(new RemoteConPacket(-1, RemoteConPacket.PacketType.ExecCommand, ""));
228+
179229
return;
180230
}
181-
182-
if (_authTries >= _remoteConServer.MaxPasswordTries)
231+
232+
// Invalid packet type.
233+
if (packet.Type != RemoteConPacket.PacketType.ExecCommand)
183234
{
184-
CloseConnection();
235+
if (_isUnitTest)
236+
throw new InvalidPacketTypeException();
237+
238+
if (_remoteConServer.InvalidPacketKick)
239+
CloseConnection();
185240
return;
186241
}
187242

188-
if (!_remoteConServer.SendAuthImmediately)
189-
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue, ""));
243+
if (packet.Payload == "")
244+
{
245+
if (_isUnitTest)
246+
throw new EmptyPacketPayloadException();
190247

191-
SendPacket(new RemoteConPacket(-1, RemoteConPacket.PacketType.ExecCommand, ""));
248+
if (_remoteConServer.EmptyPayloadKick)
249+
CloseConnection();
250+
return;
251+
}
192252

193-
return;
194-
}
253+
var args = ArgumentParser.ParseLine(packet.Payload);
254+
var cmd = args[0];
255+
args.RemoveAt(0);
195256

196-
// Invalid packet type.
197-
if (packet.Type != RemoteConPacket.PacketType.ExecCommand)
198-
{
199-
if (_isUnitTest)
200-
throw new InvalidPacketTypeException();
201-
202-
if (_remoteConServer.InvalidPacketKick)
203-
CloseConnection();
204-
return;
205-
}
206-
207-
if (packet.Payload == "")
208-
{
209-
if (_isUnitTest)
210-
throw new EmptyPacketPayloadException();
211-
212-
if (_remoteConServer.EmptyPayloadKick)
213-
CloseConnection();
214-
return;
215-
}
257+
if (_remoteConServer.UseCustomCommandHandler)
258+
{
259+
var result = _remoteConServer.ExecuteCustomCommandHandler(cmd, args);
260+
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
261+
result));
262+
return;
263+
}
216264

217-
var args = ArgumentParser.ParseLine(packet.Payload);
218-
var cmd = args[0];
219-
args.RemoveAt(0);
220-
var command = _remoteConServer.CommandManager.GetCommand(cmd);
221-
if (command == null)
222-
{
223-
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
224-
"Invalid command \"" + packet.Payload + "\""));
265+
var command = _remoteConServer.CommandManager.GetCommand(cmd);
266+
if (command == null)
267+
{
268+
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
269+
"Invalid command \"" + packet.Payload + "\""));
270+
}
271+
else
272+
{
273+
var commandResult = command.Handler(cmd, args);
274+
// TODO: Split packets?
275+
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
276+
commandResult));
277+
}
225278
}
226-
else
279+
catch (Exception e)
227280
{
228-
var commandResult = command.Func(cmd, args);
229-
// TODO: Split packets?
230-
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
231-
commandResult));
281+
Debug.WriteLine(string.Format("Client {0} caused an exception: {1} and was killed.",
282+
((IPEndPoint) _tcp.Client.RemoteEndPoint).Address, e.Message));
283+
CloseConnection();
232284
}
233285
}
234286
}

RCONServerLib/RemoteConPacket.cs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,8 @@ public enum PacketType
1818
Auth = 3
1919
}
2020

21+
private const int MaxAllowedPacketSize = 4096;
22+
2123
/// <summary>
2224
/// The identifier of the packet
2325
/// (It need not be unique, but if a unique packet id is assigned,
@@ -54,12 +56,12 @@ public RemoteConPacket(byte[] packetBytes)
5456
throw new LengthMismatchException("packet length mismatch");
5557

5658
Id = reader.ReadInt32LittleEndian();
57-
59+
5860
var packetType = reader.ReadInt32LittleEndian();
5961
if (!Enum.IsDefined(typeof(PacketType), packetType))
6062
throw new InvalidPacketTypeException("Invalid packet type");
6163
Type = (PacketType) Enum.ToObject(typeof(PacketType), packetType);
62-
64+
6365
Payload = reader.ReadAscii();
6466

6567
// Get payload length by subtracting 9 bytes (ID 4-Bytes, Type 4-Bytes, Null-terminator 1-Byte)
@@ -81,15 +83,22 @@ public RemoteConPacket(int id, PacketType type, string payload)
8183
Payload = payload;
8284
Id = id;
8385
Type = type;
84-
if (Length > 4096)
86+
if (Length > MaxAllowedPacketSize)
8587
throw new PacketTooLongException();
8688
}
8789

8890
/// <summary>
8991
/// The total size of the packet
9092
/// </summary>
91-
public int Length => Encoding.ASCII.GetBytes(Payload + '\0').Length + 13;
93+
public int Length
94+
{
95+
get { return Encoding.ASCII.GetBytes(Payload + '\0').Length + 13; }
96+
}
9297

98+
/// <summary>
99+
/// Writes the packet to a byte-array
100+
/// </summary>
101+
/// <returns>The packet in byte array</returns>
93102
public byte[] GetBytes()
94103
{
95104
using (var ms = new MemoryStream())

0 commit comments

Comments
 (0)