Added rcon packet handling.
This commit is contained in:
parent
811f7ab1dc
commit
a465943eee
5 changed files with 270 additions and 6 deletions
|
|
@ -67,6 +67,9 @@ namespace TShockAPI
|
||||||
|
|
||||||
public int MaximumLoginAttempts = 3;
|
public int MaximumLoginAttempts = 3;
|
||||||
|
|
||||||
|
public string RconPassword = "";
|
||||||
|
public int RconPort = 7778;
|
||||||
|
|
||||||
public static ConfigFile Read(string path)
|
public static ConfigFile Read(string path)
|
||||||
{
|
{
|
||||||
if (!File.Exists(path))
|
if (!File.Exists(path))
|
||||||
|
|
|
||||||
253
TShockAPI/RconHandler.cs
Normal file
253
TShockAPI/RconHandler.cs
Normal file
|
|
@ -0,0 +1,253 @@
|
||||||
|
/*
|
||||||
|
TShock, a server mod for Terraria
|
||||||
|
Copyright (C) 2011 The TShock Team
|
||||||
|
|
||||||
|
This program is free software: you can redistribute it and/or modify
|
||||||
|
it under the terms of the GNU General Public License as published by
|
||||||
|
the Free Software Foundation, either version 3 of the License, or
|
||||||
|
(at your option) any later version.
|
||||||
|
|
||||||
|
This program is distributed in the hope that it will be useful,
|
||||||
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
GNU General Public License for more details.
|
||||||
|
|
||||||
|
You should have received a copy of the GNU General Public License
|
||||||
|
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
|
||||||
|
using System;
|
||||||
|
using System.Collections.Generic;
|
||||||
|
using System.Linq;
|
||||||
|
using System.Text;
|
||||||
|
using System.Threading;
|
||||||
|
using System.IO;
|
||||||
|
using System.Net;
|
||||||
|
using System.Net.Sockets;
|
||||||
|
using Terraria;
|
||||||
|
|
||||||
|
namespace TShockAPI
|
||||||
|
{
|
||||||
|
class RconHandler
|
||||||
|
{
|
||||||
|
public static string Password = "";
|
||||||
|
private static DateTime lastRequest;
|
||||||
|
public static int ListenPort;
|
||||||
|
public static bool ContinueServer = true;
|
||||||
|
public static string Response = "";
|
||||||
|
|
||||||
|
public static void StartThread()
|
||||||
|
{
|
||||||
|
(new Thread(Start)).Start();
|
||||||
|
}
|
||||||
|
|
||||||
|
public static void Start()
|
||||||
|
{
|
||||||
|
Log.Info("Starting RconHandler.");
|
||||||
|
try
|
||||||
|
{
|
||||||
|
Console.WriteLine(string.Format("RconHandler is running at UDP port {0} and password is {1}",
|
||||||
|
ListenPort.ToString(),
|
||||||
|
Password));
|
||||||
|
Thread listen = new Thread(new ThreadStart(Listener));
|
||||||
|
listen.Start();
|
||||||
|
while (true)
|
||||||
|
{
|
||||||
|
if (listen.ThreadState != ThreadState.Running)
|
||||||
|
{
|
||||||
|
listen.Abort();
|
||||||
|
while (listen.ThreadState != ThreadState.Stopped)
|
||||||
|
continue;
|
||||||
|
listen.Start();
|
||||||
|
}
|
||||||
|
Thread.Sleep(3000);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static void Listener()
|
||||||
|
{
|
||||||
|
while (ContinueServer)
|
||||||
|
{
|
||||||
|
UdpClient listener = new UdpClient(ListenPort);
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var listenEP = new IPEndPoint(IPAddress.Any, ListenPort);
|
||||||
|
lastRequest = DateTime.Now;
|
||||||
|
byte[] bytes = listener.Receive(ref listenEP);
|
||||||
|
Log.Info(string.Format("Recieved packet from {0}:{1}", listenEP.Address.ToString(), listenEP.Port.ToString()));
|
||||||
|
var packet = parsePacket(bytes, listenEP);
|
||||||
|
listener.Send(packet, packet.Length, listenEP);
|
||||||
|
listener.Close();
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e.ToString());
|
||||||
|
listener.Close();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string sendPacket(byte[] bytes, string hostname, int port)
|
||||||
|
{
|
||||||
|
var response = Encoding.UTF8.GetString(new byte[] { 0xFF, 0xFF, 0xFF, 0xFF }) + "disconnect";
|
||||||
|
try
|
||||||
|
{
|
||||||
|
var EP = new IPEndPoint(IPAddress.Any, port);
|
||||||
|
UdpClient client = new UdpClient();
|
||||||
|
client.Connect(hostname, port);
|
||||||
|
client.Client.ReceiveTimeout = 500;
|
||||||
|
client.Send(bytes, bytes.Length);
|
||||||
|
response = Encoding.UTF8.GetString(client.Receive(ref EP));
|
||||||
|
}
|
||||||
|
catch (Exception e)
|
||||||
|
{
|
||||||
|
Log.Error(e.ToString());
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] parsePacket(byte[] bytes, IPEndPoint EP)
|
||||||
|
{
|
||||||
|
string response = "";
|
||||||
|
var packetstring = Encoding.UTF8.GetString(padPacket(bytes));
|
||||||
|
var redirect = false;
|
||||||
|
var print = true;
|
||||||
|
if ((DateTime.Now - lastRequest).Milliseconds >= 100)
|
||||||
|
{
|
||||||
|
if (packetstring.StartsWith("rcon") || packetstring.Substring(4).StartsWith("rcon") || packetstring.Substring(5).StartsWith("rcon"))
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(Password))
|
||||||
|
{
|
||||||
|
var args = packetstring.Split(' ');
|
||||||
|
if (args.Length >= 3)
|
||||||
|
{
|
||||||
|
if (args[1] == Password)
|
||||||
|
{
|
||||||
|
string command = "";
|
||||||
|
for (int i = 2; i < args.Length; i++)
|
||||||
|
command += args[i] + " ";
|
||||||
|
command = command.TrimEnd(' ').TrimEnd('\0');
|
||||||
|
response = executeCommand(command);
|
||||||
|
if (response == "" && Response != "")
|
||||||
|
{
|
||||||
|
response = Response;
|
||||||
|
Response = "";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response = "Invalid password.";
|
||||||
|
Log.ConsoleInfo("Bad rcon password from " + EP.ToString());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
response = "";
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
response = "The server must set a password for clients to use rcon.";
|
||||||
|
Log.Info("No password for rcon set");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
redirect = true;
|
||||||
|
}
|
||||||
|
if (!redirect)
|
||||||
|
return (constructPacket(response, print));
|
||||||
|
else
|
||||||
|
return (constructPacket("", false));
|
||||||
|
}
|
||||||
|
|
||||||
|
private static string executeCommand(string text)
|
||||||
|
{
|
||||||
|
if (Main.rand == null)
|
||||||
|
Main.rand = new Random();
|
||||||
|
if (WorldGen.genRand == null)
|
||||||
|
WorldGen.genRand = new Random();
|
||||||
|
if (text.StartsWith("exit"))
|
||||||
|
{
|
||||||
|
Tools.ForceKickAll("Server shutting down!");
|
||||||
|
return "Server shutting down.";
|
||||||
|
}
|
||||||
|
else if (text.StartsWith("playing") || text.StartsWith("/playing"))
|
||||||
|
{
|
||||||
|
int count = 0;
|
||||||
|
foreach (TSPlayer player in TShock.Players)
|
||||||
|
{
|
||||||
|
if (player != null && player.Active)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
TSPlayer.Server.SendMessage(string.Format("{0} ({1}) [{2}]", player.Name, player.IP, player.Group.Name));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
TSPlayer.Server.SendMessage(string.Format("{0} players connected.", count));
|
||||||
|
}
|
||||||
|
else if (text.StartsWith("status"))
|
||||||
|
{
|
||||||
|
Response += "map: " + Main.worldName + "\n";
|
||||||
|
Response += "num score ping name\tlastmsg address\tqport rate\n";
|
||||||
|
int count = 0;
|
||||||
|
foreach (TSPlayer player in TShock.Players)
|
||||||
|
{
|
||||||
|
if (player != null && player.Active)
|
||||||
|
{
|
||||||
|
count++;
|
||||||
|
Response += (string.Format("{0} 0 0 {1}({2})\t{3} {4}\t0 0", count, player.Name, player.Group.Name, Netplay.serverSock[player.Index].tcpClient.Client.RemoteEndPoint.ToString())) + "\n";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else if (text.StartsWith("say "))
|
||||||
|
{
|
||||||
|
Log.Info(string.Format("Server said: {0}", text.Remove(0, 4)));
|
||||||
|
return string.Format("Server said: {0}", text.Remove(0, 4));
|
||||||
|
}
|
||||||
|
else if (text == "autosave")
|
||||||
|
{
|
||||||
|
Main.autoSave = TShock.Config.AutoSave = !TShock.Config.AutoSave;
|
||||||
|
Log.ConsoleInfo("AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled"));
|
||||||
|
return "AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled");
|
||||||
|
}
|
||||||
|
else
|
||||||
|
if (!Commands.HandleCommand(TSPlayer.Server, text))
|
||||||
|
return "Invalid command.";
|
||||||
|
return "";
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] constructPacket(string response, bool print)
|
||||||
|
{
|
||||||
|
var oob = new byte[] { 0xFF, 0xFF, 0xFF, 0xFF };
|
||||||
|
MemoryStream stream = new MemoryStream();
|
||||||
|
BinaryWriter writer = new BinaryWriter(stream);
|
||||||
|
writer.Write(oob);
|
||||||
|
if (print)
|
||||||
|
writer.Write(Encoding.UTF8.GetBytes(string.Format("print\n{0}", response)));
|
||||||
|
else
|
||||||
|
writer.Write(Encoding.UTF8.GetBytes("disconnect\n"));
|
||||||
|
var packet = Encoding.UTF8.GetBytes(
|
||||||
|
(Encoding.UTF8.GetString(stream.GetBuffer())
|
||||||
|
.Substring(0, (int)stream.Length)));
|
||||||
|
return packet;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] padPacket(byte[] packet)
|
||||||
|
{
|
||||||
|
var returnpacket = new byte[(4 + packet.Length)];
|
||||||
|
int h = 0;
|
||||||
|
if (packet[0] != 0xFF)
|
||||||
|
{
|
||||||
|
for (int i = 0; i < 4; i++)
|
||||||
|
returnpacket[i] = 0xFF;
|
||||||
|
for (int i = 4; i < returnpacket.Length; i++)
|
||||||
|
returnpacket[i] = packet[h++];
|
||||||
|
}
|
||||||
|
else
|
||||||
|
returnpacket = packet;
|
||||||
|
return returnpacket;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -370,6 +370,7 @@ namespace TShockAPI
|
||||||
public override void SendMessage(string msg, byte red, byte green, byte blue)
|
public override void SendMessage(string msg, byte red, byte green, byte blue)
|
||||||
{
|
{
|
||||||
Console.WriteLine(msg);
|
Console.WriteLine(msg);
|
||||||
|
RconHandler.Response += msg + "\n";
|
||||||
}
|
}
|
||||||
|
|
||||||
public void SetBloodMoon(bool bloodMoon)
|
public void SetBloodMoon(bool bloodMoon)
|
||||||
|
|
|
||||||
|
|
@ -113,6 +113,7 @@ namespace TShockAPI
|
||||||
RegionManager.ReadAllSettings();
|
RegionManager.ReadAllSettings();
|
||||||
WarpsManager.ReadAllSettings();
|
WarpsManager.ReadAllSettings();
|
||||||
ItemManager.LoadBans();
|
ItemManager.LoadBans();
|
||||||
|
RconHandler.StartThread();
|
||||||
|
|
||||||
Log.ConsoleInfo("AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled"));
|
Log.ConsoleInfo("AutoSave " + (TShock.Config.AutoSave ? "Enabled" : "Disabled"));
|
||||||
Log.ConsoleInfo("Backups " + (Backups.Interval > 0 ? "Enabled" : "Disabled"));
|
Log.ConsoleInfo("Backups " + (Backups.Interval > 0 ? "Enabled" : "Disabled"));
|
||||||
|
|
@ -373,6 +374,8 @@ namespace TShockAPI
|
||||||
if (text.StartsWith("exit"))
|
if (text.StartsWith("exit"))
|
||||||
{
|
{
|
||||||
Tools.ForceKickAll("Server shutting down!");
|
Tools.ForceKickAll("Server shutting down!");
|
||||||
|
WorldGen.saveWorld(false);
|
||||||
|
Netplay.disconnect = true;
|
||||||
}
|
}
|
||||||
else if (text.StartsWith("playing") || text.StartsWith("/playing"))
|
else if (text.StartsWith("playing") || text.StartsWith("/playing"))
|
||||||
{
|
{
|
||||||
|
|
@ -595,6 +598,9 @@ namespace TShockAPI
|
||||||
Netplay.serverPort = file.ServerPort;
|
Netplay.serverPort = file.ServerPort;
|
||||||
|
|
||||||
Netplay.spamCheck = file.SpamChecks;
|
Netplay.spamCheck = file.SpamChecks;
|
||||||
|
|
||||||
|
RconHandler.Password = file.RconPassword;
|
||||||
|
RconHandler.ListenPort = file.RconPort;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -84,6 +84,7 @@
|
||||||
<Compile Include="Log.cs" />
|
<Compile Include="Log.cs" />
|
||||||
<Compile Include="Net\NetTile.cs" />
|
<Compile Include="Net\NetTile.cs" />
|
||||||
<Compile Include="Net\WorldInfoMsg.cs" />
|
<Compile Include="Net\WorldInfoMsg.cs" />
|
||||||
|
<Compile Include="RconHandler.cs" />
|
||||||
<Compile Include="RegionManager.cs" />
|
<Compile Include="RegionManager.cs" />
|
||||||
<Compile Include="RememberPosManager.cs" />
|
<Compile Include="RememberPosManager.cs" />
|
||||||
<Compile Include="Resources.Designer.cs">
|
<Compile Include="Resources.Designer.cs">
|
||||||
|
|
@ -149,11 +150,11 @@
|
||||||
<UserProperties BuildVersion_IncrementBeforeBuild="False" BuildVersion_StartDate="2011/6/17" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_BuildAction="Both" BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" />
|
<UserProperties BuildVersion_IncrementBeforeBuild="False" BuildVersion_StartDate="2011/6/17" BuildVersion_BuildVersioningStyle="None.None.None.MonthAndDayStamp" BuildVersion_BuildAction="Both" BuildVersion_UpdateFileVersion="True" BuildVersion_UpdateAssemblyVersion="True" />
|
||||||
</VisualStudio>
|
</VisualStudio>
|
||||||
</ProjectExtensions>
|
</ProjectExtensions>
|
||||||
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
|
||||||
Other similar extension points exist, see Microsoft.Common.targets.
|
Other similar extension points exist, see Microsoft.Common.targets.
|
||||||
<Target Name="BeforeBuild">
|
<Target Name="BeforeBuild">
|
||||||
</Target>
|
</Target>
|
||||||
<Target Name="AfterBuild">
|
<Target Name="AfterBuild">
|
||||||
</Target>
|
</Target>
|
||||||
-->
|
-->
|
||||||
</Project>
|
</Project>
|
||||||
Loading…
Add table
Add a link
Reference in a new issue