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

Commit

Permalink
Custom command event
Browse files Browse the repository at this point in the history
Updated README
Better exception handling
Added more documentation
Upped version to 1.1
  • Loading branch information
Subtixx committed Jan 25, 2018
1 parent 99f6c6b commit 752d085
Show file tree
Hide file tree
Showing 14 changed files with 356 additions and 135 deletions.
2 changes: 1 addition & 1 deletion RCON.sln
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@

Microsoft Visual Studio Solution File, Format Version 12.00
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCONServer", "RCONServer\RCONServer.csproj", "{EDB656EE-634B-4080-8293-73205470FCCF}"
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCONServerLibExample", "RCONServerLibExample\RCONServerLibExample.csproj", "{EDB656EE-634B-4080-8293-73205470FCCF}"
EndProject
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "RCONServerLib", "RCONServerLib\RCONServerLib.csproj", "{2EFA0311-14CE-414B-9867-FC0D1FF6F6C4}"
EndProject
Expand Down
5 changes: 1 addition & 4 deletions RCONServerLib/RCONServerLib.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@
<AssemblyName>RCONServerLib</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<LangVersion>6</LangVersion>
<LangVersion>5</LangVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
Expand All @@ -34,9 +34,6 @@
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="RemoteConClient.cs" />
Expand Down
42 changes: 23 additions & 19 deletions RCONServerLib/RCONServerLib.nuspec
Original file line number Diff line number Diff line change
@@ -1,21 +1,25 @@
<?xml version="1.0"?>
<package >
<metadata>
<id>source-rcon-server</id>
<version>1.0.0</version>
<title>Source RCON Server Lib</title>
<authors>Julien</authors>
<owners>Julien</owners>
<licenseUrl>https://github.com/Subtixx/source-rcon-library/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/Subtixx/source-rcon-library/</projectUrl>
<!--<iconUrl>http://ICON_URL_HERE_OR_DELETE_THIS_LINE</iconUrl>-->
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>Single class solution to create a source compatible RCON interface</description>
<releaseNotes>Initial Release.</releaseNotes>
<copyright>Copyright Subtixx 2018</copyright>
<tags>source-rcon-server rconserver remoteconsole rcon-server</tags>
</metadata>
<files>
<file src="bin\Release\RCONServerLib.dll" target="lib\net35\RCONServerLib.dll" />
</files>
<package>
<metadata>
<id>source-rcon-server</id>
<version>1.0.0</version>
<title>Source RCON Server Lib</title>
<authors>Julien H. (Subtixx)</authors>
<owners>Julien H. (Subtixx)</owners>
<licenseUrl>https://github.com/Subtixx/source-rcon-library/blob/master/LICENSE</licenseUrl>
<projectUrl>https://github.com/Subtixx/source-rcon-library/</projectUrl>
<iconUrl>https://user-images.githubusercontent.com/20743379/35411973-0227102e-021b-11e8-9a1b-023e08c33c4e.png</iconUrl>
<requireLicenseAcceptance>false</requireLicenseAcceptance>
<description>
Easy to use, single-class solution to create a Valve source RCON server which supports authentication,
IP Whitelisting and a command manager
</description>
<summary>Single class solution to create a source compatible RCON interface</summary>
<releaseNotes>Initial Release.</releaseNotes>
<copyright>Copyright Subtixx 2018</copyright>
<tags>source-rcon-server rconserver remoteconsole rcon-server</tags>
</metadata>
<files>
<file src="bin\Release\RCONServerLib.dll" target="lib\net35\RCONServerLib.dll"/>
</files>
</package>
172 changes: 112 additions & 60 deletions RCONServerLib/RemoteConClient.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,6 @@
using System;
using System.Diagnostics;
using System.Net;
using System.Net.Sockets;
using RCONServerLib.Utils;

Expand All @@ -9,18 +11,50 @@ namespace RCONServerLib
/// </summary>
internal class RemoteConClient
{
/// <summary>
/// The maximum packet size to receive
/// </summary>
private const int MaxAllowedPacketSize = 4096;

/// <summary>
/// Used to determine if we're in unit test mode (Means no actual connection)
/// </summary>
private readonly bool _isUnitTest;

/// <summary>
/// Underlaying NetworkStream
/// </summary>
private readonly NetworkStream _ns;

/// <summary>
/// Reference to RemoteConServer for getting settings
/// </summary>
private readonly RemoteConServer _remoteConServer;

/// <summary>
/// The TCP Client
/// </summary>
private readonly TcpClient _tcp;
internal bool Authenticated;

/// <summary>
/// How many failed login attempts the client has
/// </summary>
private int _authTries;

/// <summary>
/// A buffer containing the packet
/// </summary>
private byte[] _buffer;

/// <summary>
/// Wether or not the client is connected
/// </summary>
private bool _connected;

private readonly bool _isUnitTest;
/// <summary>
/// Has the client been successfully authenticated
/// </summary>
internal bool Authenticated;

public RemoteConClient(TcpClient tcp, RemoteConServer remoteConServer)
{
Expand Down Expand Up @@ -64,7 +98,7 @@ private void CloseConnection()
{
if (_isUnitTest)
return;

_connected = false;

if (!_tcp.Connected)
Expand All @@ -83,7 +117,7 @@ private void SendPacket(RemoteConPacket packet)
{
if (_isUnitTest)
return;

if (!_connected)
throw new Exception("Not connected.");

Expand Down Expand Up @@ -149,86 +183,104 @@ private void OnPacket(IAsyncResult result)
}

/// <summary>
/// Parses raw bytes to RemoteConPacket
/// Parses raw bytes to RemoteConPacket
/// </summary>
/// <param name="rawPacket"></param>
internal void ParsePacket(byte[] rawPacket)
{
var packet = new RemoteConPacket(rawPacket);

// Do not allow any other packets than auth to be sent when client is not authenticated
if (!Authenticated)
try
{
if (packet.Type != RemoteConPacket.PacketType.Auth)
var packet = new RemoteConPacket(rawPacket);

// Do not allow any other packets than auth to be sent when client is not authenticated
if (!Authenticated)
{
if (_isUnitTest)
throw new NotAuthenticatedException();
CloseConnection();
}
if (packet.Type != RemoteConPacket.PacketType.Auth)
{
if (_isUnitTest)
throw new NotAuthenticatedException();
CloseConnection();
}

_authTries++;
_authTries++;

if (packet.Payload == _remoteConServer.Password)
{
Authenticated = true;
if (packet.Payload == _remoteConServer.Password)
{
Authenticated = true;

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

SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ExecCommand, ""));
return;
}

if (_authTries >= _remoteConServer.MaxPasswordTries)
{
CloseConnection();
return;
}

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

SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ExecCommand, ""));
SendPacket(new RemoteConPacket(-1, RemoteConPacket.PacketType.ExecCommand, ""));

return;
}

if (_authTries >= _remoteConServer.MaxPasswordTries)

// Invalid packet type.
if (packet.Type != RemoteConPacket.PacketType.ExecCommand)
{
CloseConnection();
if (_isUnitTest)
throw new InvalidPacketTypeException();

if (_remoteConServer.InvalidPacketKick)
CloseConnection();
return;
}

if (!_remoteConServer.SendAuthImmediately)
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue, ""));
if (packet.Payload == "")
{
if (_isUnitTest)
throw new EmptyPacketPayloadException();

SendPacket(new RemoteConPacket(-1, RemoteConPacket.PacketType.ExecCommand, ""));
if (_remoteConServer.EmptyPayloadKick)
CloseConnection();
return;
}

return;
}
var args = ArgumentParser.ParseLine(packet.Payload);
var cmd = args[0];
args.RemoveAt(0);

// Invalid packet type.
if (packet.Type != RemoteConPacket.PacketType.ExecCommand)
{
if (_isUnitTest)
throw new InvalidPacketTypeException();

if (_remoteConServer.InvalidPacketKick)
CloseConnection();
return;
}

if (packet.Payload == "")
{
if (_isUnitTest)
throw new EmptyPacketPayloadException();

if (_remoteConServer.EmptyPayloadKick)
CloseConnection();
return;
}
if (_remoteConServer.UseCustomCommandHandler)
{
var result = _remoteConServer.ExecuteCustomCommandHandler(cmd, args);
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
result));
return;
}

var args = ArgumentParser.ParseLine(packet.Payload);
var cmd = args[0];
args.RemoveAt(0);
var command = _remoteConServer.CommandManager.GetCommand(cmd);
if (command == null)
{
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
"Invalid command \"" + packet.Payload + "\""));
var command = _remoteConServer.CommandManager.GetCommand(cmd);
if (command == null)
{
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
"Invalid command \"" + packet.Payload + "\""));
}
else
{
var commandResult = command.Handler(cmd, args);
// TODO: Split packets?
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
commandResult));
}
}
else
catch (Exception e)
{
var commandResult = command.Func(cmd, args);
// TODO: Split packets?
SendPacket(new RemoteConPacket(packet.Id, RemoteConPacket.PacketType.ResponseValue,
commandResult));
Debug.WriteLine(string.Format("Client {0} caused an exception: {1} and was killed.",
((IPEndPoint) _tcp.Client.RemoteEndPoint).Address, e.Message));
CloseConnection();
}
}
}
Expand Down
17 changes: 13 additions & 4 deletions RCONServerLib/RemoteConPacket.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,8 @@ public enum PacketType
Auth = 3
}

private const int MaxAllowedPacketSize = 4096;

/// <summary>
/// The identifier of the packet
/// (It need not be unique, but if a unique packet id is assigned,
Expand Down Expand Up @@ -54,12 +56,12 @@ public RemoteConPacket(byte[] packetBytes)
throw new LengthMismatchException("packet length mismatch");

Id = reader.ReadInt32LittleEndian();

var packetType = reader.ReadInt32LittleEndian();
if (!Enum.IsDefined(typeof(PacketType), packetType))
throw new InvalidPacketTypeException("Invalid packet type");
Type = (PacketType) Enum.ToObject(typeof(PacketType), packetType);

Payload = reader.ReadAscii();

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

/// <summary>
/// The total size of the packet
/// </summary>
public int Length => Encoding.ASCII.GetBytes(Payload + '\0').Length + 13;
public int Length
{
get { return Encoding.ASCII.GetBytes(Payload + '\0').Length + 13; }
}

/// <summary>
/// Writes the packet to a byte-array
/// </summary>
/// <returns>The packet in byte array</returns>
public byte[] GetBytes()
{
using (var ms = new MemoryStream())
Expand Down
Loading

0 comments on commit 752d085

Please sign in to comment.