Removed NioTSO client and server

- NioTSO client isn't needed because we're using RayLib
- Added FreeSO's API server to handle most backend operations
This commit is contained in:
Tony Bark 2024-05-01 02:55:43 -04:00
parent f12ba1502b
commit 22191ce648
591 changed files with 53264 additions and 3362 deletions

View file

@ -0,0 +1,46 @@
namespace FSO.Server.Common
{
public class ApiAbstract
{
public event APIRequestShutdownDelegate OnRequestShutdown;
public event APIBroadcastMessageDelegate OnBroadcastMessage;
public event APIRequestUserDisconnectDelegate OnRequestUserDisconnect;
public event APIRequestMailNotifyDelegate OnRequestMailNotify;
public delegate void APIRequestShutdownDelegate(uint time, ShutdownType type);
public delegate void APIBroadcastMessageDelegate(string sender, string title, string message);
public delegate void APIRequestUserDisconnectDelegate(uint user_id);
public delegate void APIRequestMailNotifyDelegate(int message_id, string subject, string body, uint target_id);
public void RequestShutdown(uint time, ShutdownType type)
{
OnRequestShutdown?.Invoke(time, type);
}
/// <summary>
/// Asks the server to disconnect a user.
/// </summary>
/// <param name="user_id"></param>
public void RequestUserDisconnect(uint user_id)
{
OnRequestUserDisconnect?.Invoke(user_id);
}
/// <summary>
/// Asks the server to notify the client about the new message.
/// </summary>
/// <param name="message_id"></param>
/// <param name="subject"></param>
/// <param name="body"></param>
/// <param name="target_id"></param>
public void RequestMailNotify(int message_id, string subject, string body, uint target_id)
{
OnRequestMailNotify(message_id, subject, body, target_id);
}
public void BroadcastMessage(string sender, string title, string message)
{
OnBroadcastMessage?.Invoke(sender, title, message);
}
}
}

View file

@ -0,0 +1,10 @@
namespace FSO.Server.Common.Config
{
public class AWSConfig
{
public string Region { get; set; } = "eu-west-2";
public string Bucket { get; set; } = "fso-updates";
public string AccessKeyID { get; set; }
public string SecretAccessKey { get; set; }
}
}

View file

@ -0,0 +1,16 @@
namespace FSO.Server.Common.Config
{
public class GithubConfig
{
public string AppName { get; set; } = "FreeSO";
public string User { get; set; } = "riperiperi";
public string Repository { get; set; } = "FreeSO";
public string ClientID { get; set; }
public string ClientSecret { get; set; }
/// <summary>
/// Must be generated by installing the app on your user account. Browse to the /github/ API endpoint when this is null. (yes, on this server)
/// </summary>
public string AccessToken { get; set; }
}
}

View file

@ -0,0 +1,39 @@
using System;
namespace FSO.Server.Common
{
public class Epoch
{
public static uint Now
{
get
{
uint epoch = (uint)(DateTime.UtcNow - new DateTime(1970, 1, 1)).TotalSeconds;
return epoch;
}
}
public static uint FromDate(DateTime time)
{
return (uint)(time.ToUniversalTime() - new DateTime(1970, 1, 1)).TotalSeconds;
}
public static DateTime ToDate(uint time)
{
var epoch = new DateTime(1970, 1, 1, 0, 0, 0, DateTimeKind.Utc);
return epoch.AddSeconds(time);
}
public static string HMSRemaining(uint date)
{
TimeSpan span = (ToDate(date) - ToDate(Epoch.Now));
return String.Format("{0} hours, {1} minutes and {2} seconds", span.Hours, span.Minutes, span.Seconds);
}
public static uint Default
{
get { return 0; }
}
}
}

View file

@ -0,0 +1,104 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{39B61962-FE43-4B64-8E57-8F793737FFFE}</ProjectGuid>
<OutputType>Library</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>FSO.Server.Common</RootNamespace>
<AssemblyName>FSO.Server.Common</AssemblyName>
<TargetFrameworkVersion>v4.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
<TargetFrameworkProfile />
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<DebugType>pdbonly</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
<UseVSHostingProcess>true</UseVSHostingProcess>
</PropertyGroup>
<PropertyGroup Condition="'$(Configuration)|$(Platform)' == 'ServerRelease|AnyCPU'">
<OutputPath>bin\ServerRelease\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<Optimize>true</Optimize>
<DebugType>pdbonly</DebugType>
<PlatformTarget>AnyCPU</PlatformTarget>
<ErrorReport>prompt</ErrorReport>
<CodeAnalysisRuleSet>MinimumRecommendedRules.ruleset</CodeAnalysisRuleSet>
</PropertyGroup>
<ItemGroup>
<Reference Include="crypto, Version=1.8.0.0, Culture=neutral, PublicKeyToken=0e99375e54769942, processorArchitecture=MSIL">
<HintPath>..\packages\Portable.BouncyCastle.1.8.0\lib\net45\crypto.dll</HintPath>
</Reference>
<Reference Include="JWT, Version=1.0.0.0, Culture=neutral, processorArchitecture=MSIL">
<HintPath>..\packages\Portable.JWT.1.0.5\lib\portable-net45+win+wpa81+wp80+MonoAndroid10+xamarinios10+MonoTouch10\JWT.dll</HintPath>
</Reference>
<Reference Include="Newtonsoft.Json, Version=12.0.0.0, Culture=neutral, PublicKeyToken=30ad4fe6b2a6aeed, processorArchitecture=MSIL">
<HintPath>..\packages\Newtonsoft.Json.12.0.2\lib\net45\Newtonsoft.Json.dll</HintPath>
</Reference>
<Reference Include="Ninject, Version=3.0.0.0, Culture=neutral, PublicKeyToken=c7192dc5380945e7, processorArchitecture=MSIL">
<HintPath>..\packages\Portable.Ninject.3.3.1\lib\net40-client\Ninject.dll</HintPath>
</Reference>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="Microsoft.CSharp" />
<Reference Include="System.Data" />
<Reference Include="System.Net.Http" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="ApiAbstract.cs" />
<Compile Include="Config\AWSConfig.cs" />
<Compile Include="Config\GithubConfig.cs" />
<Compile Include="Epoch.cs" />
<Compile Include="IAPIController.cs" />
<Compile Include="IPAddress.cs" />
<Compile Include="IPEndPointUtils.cs" />
<Compile Include="JsonWebToken\JWTConfiguration.cs" />
<Compile Include="JsonWebToken\JWTokenFactory.cs" />
<Compile Include="JsonWebToken\JWTUser.cs" />
<Compile Include="PacketLogger.cs" />
<Compile Include="PasswordHasher.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
<Compile Include="IServerDebugger.cs" />
<Compile Include="ServerVersion.cs" />
<Compile Include="Session\IAriesSession.cs" />
<Compile Include="Session\IGluonSession.cs" />
<Compile Include="ShutdownType.cs" />
</ItemGroup>
<ItemGroup>
<None Include="app.config" />
<None Include="packages.config" />
</ItemGroup>
<ItemGroup>
<ProjectReference Include="..\tso.common\FSO.Common.csproj">
<Project>{c42962a1-8796-4f47-9dcd-79ed5904d8ca}</Project>
<Name>FSO.Common</Name>
</ProjectReference>
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- 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.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

View file

@ -0,0 +1,7 @@
namespace FSO.Server.Common
{
public interface IAPILifetime
{
void Stop();
}
}

View file

@ -0,0 +1,21 @@
using System.Net;
namespace FSO.Server.Common
{
public class IPAddress
{
public static string Get(HttpListenerRequest httpRequest)
{
if (httpRequest.Headers["X-Forwarded-For"] != null)
{
return httpRequest.Headers["X-Forwarded-For"];
}
return Get(httpRequest.RemoteEndPoint);
}
public static string Get(IPEndPoint endpoint)
{
return endpoint.Address.ToString();
}
}
}

View file

@ -0,0 +1,32 @@
using System;
using System.Globalization;
using System.Net;
namespace FSO.Server.Common
{
public class IPEndPointUtils
{
public static IPEndPoint CreateIPEndPoint(string endPoint)
{
string[] ep = endPoint.Split(':');
if (ep.Length != 2) throw new FormatException("Invalid endpoint format");
System.Net.IPAddress ip;
if (!System.Net.IPAddress.TryParse(ep[0], out ip))
{
var addrs = Dns.GetHostEntry(ep[0]).AddressList;
if (addrs.Length == 0)
{
throw new FormatException("Invalid ip-address");
}
else ip = addrs[0];
}
int port;
if (!int.TryParse(ep[1], NumberStyles.None, NumberFormatInfo.CurrentInfo, out port))
{
throw new FormatException("Invalid port");
}
return new IPEndPoint(ip, port);
}
}
}

View file

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace FSO.Server.Common
{
public interface IServerDebugger
{
IPacketLogger GetPacketLogger();
void AddSocketServer(ISocketServer server);
}
public interface ISocketServer
{
List<ISocketSession> GetSocketSessions();
}
public interface ISocketSession
{
void Write(params object[] data);
}
}

View file

@ -0,0 +1,8 @@
namespace FSO.Server.Servers.Api.JsonWebToken
{
public class JWTConfiguration
{
public byte[] Key;
public int TokenDuration = 3600;
}
}

View file

@ -0,0 +1,19 @@
using System.Collections.Generic;
namespace FSO.Server.Servers.Api.JsonWebToken
{
public class JWTUser
{
public uint UserID { get; set; }
public IEnumerable<string> Claims
{
get; set;
}
public string UserName
{
get; set;
}
}
}

View file

@ -0,0 +1,48 @@
using FSO.Server.Common;
using Newtonsoft.Json;
using System.Collections.Generic;
namespace FSO.Server.Servers.Api.JsonWebToken
{
public class JWTInstance
{
public string Token;
public int ExpiresIn;
}
public class JWTFactory
{
private JWTConfiguration Config;
public JWTFactory(JWTConfiguration config)
{
this.Config = config;
}
public JWTUser DecodeToken(string token)
{
var payload = JWT.JsonWebToken.Decode(token, Config.Key, true);
Dictionary<string, string> payloadParsed = JsonConvert.DeserializeObject<Dictionary<string, string>>(payload);
return Newtonsoft.Json.JsonConvert.DeserializeObject<JWTUser>(payloadParsed["data"]);
}
public JWTInstance CreateToken(JWTUser data)
{
var tokenData = Newtonsoft.Json.JsonConvert.SerializeObject(data);
return CreateToken(tokenData, Config.TokenDuration);
}
private JWTInstance CreateToken(string data, int expiresIn)
{
var expires = Epoch.Now + expiresIn;
var payload = new Dictionary<string, object>()
{
{ "exp", expires },
{ "data", data }
};
var token = JWT.JsonWebToken.Encode(payload, Config.Key, JWT.JwtHashAlgorithm.HS384);
return new JWTInstance { Token = token, ExpiresIn = expiresIn };
}
}
}

View file

@ -0,0 +1,29 @@
namespace FSO.Server.Common
{
public interface IPacketLogger
{
void OnPacket(Packet packet);
}
public class Packet
{
public PacketType Type;
public uint SubType;
public byte[] Data;
public PacketDirection Direction;
}
public enum PacketType
{
ARIES,
VOLTRON,
ELECTRON
}
public enum PacketDirection
{
OUTPUT,
INPUT
}
}

View file

@ -0,0 +1,103 @@
using System;
using System.Linq;
using System.Security.Cryptography;
namespace FSO.Server.Common
{
public class PasswordHasher
{
public static PasswordHash Hash(string password)
{
return Hash(password, "Rfc2898");
}
public static PasswordHash Hash(string password, string scheme)
{
var schemeImpl = GetScheme(scheme);
return schemeImpl.Hash(password);
}
public static bool Verify(string password, PasswordHash hash)
{
return Verify(password, hash, "Rfc2898");
}
public static bool Verify(string password, PasswordHash hash, string scheme)
{
var schemeImpl = GetScheme(scheme);
return schemeImpl.Verify(password, hash);
}
private static IPasswordHashScheme GetScheme(string scheme)
{
switch (scheme)
{
case "Rfc2898":
return new DefaultPasswordHashScheme();
}
throw new Exception("Unknown password hash scheme: " + scheme);
}
}
public class PasswordHash
{
public byte[] data;
public string scheme;
}
public class DefaultPasswordHashScheme : IPasswordHashScheme
{
public PasswordHash Hash(string password)
{
var salt_input = GetStrongRandomBytes(16);
return new PasswordHash()
{
scheme = "Rfc2898",
data = Hash(salt_input, password)
};
}
private byte[] Hash(byte[] salt_input, string password)
{
var hasher = new Rfc2898DeriveBytes(System.Text.Encoding.UTF8.GetBytes(password), salt_input, 1000);
var hash = hasher.GetBytes(64);
//Encode the salt + hash together
var result = new byte[1 + 16 + hash.Length];
result[0] = (byte)16;
Array.Copy(salt_input, 0, result, 1, salt_input.Length);
Array.Copy(hash, 0, result, salt_input.Length + 1, hash.Length);
return result;
}
public bool Verify(string password, PasswordHash hash)
{
var salt_length = hash.data[0];
var salt_input = new byte[salt_length];
Array.Copy(hash.data, 1, salt_input, 0, salt_length);
var expected = Hash(salt_input, password);
return expected.SequenceEqual(hash.data);
}
private byte[] GetStrongRandomBytes(int numBytes)
{
var random_bytes = new byte[numBytes];
using (RNGCryptoServiceProvider rngCsp = new RNGCryptoServiceProvider())
{
rngCsp.GetBytes(random_bytes);
}
return random_bytes;
}
}
public interface IPasswordHashScheme
{
PasswordHash Hash(string password);
bool Verify(string password, PasswordHash hash);
}
}

View file

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("FSO.Server.Common")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("FSO.Server.Common")]
[assembly: AssemblyCopyright("Copyright © 2015")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("39b61962-fe43-4b64-8e57-8f793737fffe")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

View file

@ -0,0 +1,48 @@
using System.IO;
namespace FSO.Server.Common
{
public class ServerVersion
{
public string Name;
public string Number;
public int? UpdateID;
public static ServerVersion Get()
{
var result = new ServerVersion()
{
Name = "unknown",
Number = "0"
};
if (File.Exists("version.txt"))
{
using (StreamReader Reader = new StreamReader(File.Open("version.txt", FileMode.Open, FileAccess.Read, FileShare.Read)))
{
var str = Reader.ReadLine();
var split = str.LastIndexOf('-');
result.Name = str;
if (split != -1)
{
result.Name = str.Substring(0, split);
result.Number = str.Substring(split + 1);
}
}
}
if (File.Exists("updateID.txt"))
{
var stringID = File.ReadAllText("updateID.txt");
int id;
if (int.TryParse(stringID, out id))
{
result.UpdateID = id;
}
}
return result;
}
}
}

View file

@ -0,0 +1,17 @@
using FSO.Server.Common;
namespace FSO.Server.Framework.Aries
{
public interface IAriesSession : ISocketSession
{
bool IsAuthenticated { get; }
uint LastRecv { get; set; }
bool Connected { get; }
void Write(params object[] messages);
void Close();
object GetAttribute(string key);
void SetAttribute(string key, object value);
}
}

View file

@ -0,0 +1,12 @@
using FSO.Common.Security;
using FSO.Server.Framework.Aries;
namespace FSO.Server.Framework.Gluon
{
public interface IGluonSession : IAriesSession, ISecurityContext
{
string CallSign { get; }
string PublicHost { get; }
string InternalHost { get; }
}
}

View file

@ -0,0 +1,9 @@
namespace FSO.Server.Common
{
public enum ShutdownType : byte
{
SHUTDOWN = 0,
RESTART = 1,
UPDATE = 2 //restart but runs an update task
}
}

View file

@ -0,0 +1,19 @@
<?xml version="1.0" encoding="utf-8"?>
<configuration>
<runtime>
<assemblyBinding xmlns="urn:schemas-microsoft-com:asm.v1">
<dependentAssembly>
<assemblyIdentity name="Common.Logging.Core" publicKeyToken="af08829b84f0328e" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.4.1.0" newVersion="3.4.1.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Newtonsoft.Json" publicKeyToken="30ad4fe6b2a6aeed" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-12.0.0.0" newVersion="12.0.0.0" />
</dependentAssembly>
<dependentAssembly>
<assemblyIdentity name="Common.Logging" publicKeyToken="af08829b84f0328e" culture="neutral" />
<bindingRedirect oldVersion="0.0.0.0-3.4.1.0" newVersion="3.4.1.0" />
</dependentAssembly>
</assemblyBinding>
</runtime>
</configuration>

View file

@ -0,0 +1,7 @@
<?xml version="1.0" encoding="utf-8"?>
<packages>
<package id="Newtonsoft.Json" version="12.0.2" targetFramework="net45" />
<package id="Portable.BouncyCastle" version="1.8.0" targetFramework="net45" />
<package id="Portable.JWT" version="1.0.5" targetFramework="net45" />
<package id="Portable.Ninject" version="3.3.1" targetFramework="net45" />
</packages>