the pink ball in crabby 64

rer
This commit is contained in:
Malloc of Kuzkycyziklistan 2017-09-06 15:59:38 -05:00
parent 3f51e965fc
commit 2937d799ae
13 changed files with 150 additions and 60 deletions

View file

@ -211,22 +211,17 @@ Communication between the master server and clients will be done over a WebSocke
<td colspan="2">Iterated over <i>n</i> (0 &leq; <i>i</i> &leq; <i>n - 1</i>)</td>
</tr>
<tr>
<td class="center">2 + 4<i>i</i></td>
<td class="center">2 + 3<i>i</i></td>
<td>Server Id</td>
<td>Packed Unsigned Short</td>
</tr>
<tr>
<td class="center">3 + 4<i>i</i></td>
<td class="center">3 + 3<i>i</i></td>
<td>User Count</td>
<td>Packed Unsigned Short</td>
</tr>
<tr>
<td class="center">4 + 4<i>i</i></td>
<td>IPv4 Address</td>
<td>Bytes (4)</td>
</tr>
<tr>
<td class="center">5 + 4<i>i</i></td>
<td class="center">4 + 3<i>i</i></td>
<td>Port</td>
<td>Packed Unsigned Short</td>
</tr>
@ -313,6 +308,30 @@ Communication between the master server and clients will be done over a WebSocke
</tr>
</table>
<table style="margin-right: 8px; margin-bottom: 8px;">
<thead>
<th colspan="100" class="center">
ID 4: Server List Request<br />
[Encrypted] Responder
</th>
</thead>
<thead>
<th>#</th>
<th>Region</th>
<th>Type</th>
</thead>
<tr>
<td class="center">1</td>
<td>Succeeded</td>
<td>Boolean</td>
</tr>
<tr>
<td class="center">2</td>
<td>Message</td>
<td>String</td>
</tr>
</table>
#### Client to Master
<table style="margin-right: 8px; margin-bottom: 8px;">
@ -387,6 +406,16 @@ Communication between the master server and clients will be done over a WebSocke
</tr>
</table>
<table style="margin-right: 8px; margin-bottom: 8px;">
<thead>
<th colspan="100" class="center">
ID 4: Server List Request<br />
[Encrypted] Requester<br />
<i>Bodyless Packet</i>
</th>
</thead>
</table>
## Sockstamps
Because epoch time is not standardized across systems, an intermediate layer of date/time transmission must be used between the client and server so as to handle time dependent interactions. Therefore, a "sockstamp" will be used in place of the context-dependent implementations of epoch time.

View file

@ -22,8 +22,6 @@ namespace SockScape {
class Entrypoint {
static void Main(string[] args) {
var db = new DAL.ScapeDb();
foreach(var server in Configuration.Servers) {
var pool = new Pool<PlayerConnection> {
InitialCount = 3,

View file

@ -29,7 +29,7 @@ namespace Glove.INI {
if(currentInstance != null)
currentInstance.Push(line);
else
throw new FormatException("Non-section line before any define sections in '"+ path +"'");
throw new FormatException($"Non-section line before any define sections in '{path}'");
}
}
}
@ -41,18 +41,18 @@ namespace Glove.INI {
if(ContainsSection(name)) {
var section = Sections[name];
if(!rule.AllowMultiple && section.Count > 1)
throw new FormatException("Section '"+ name +"' is not allowed to have multiple declarations in '"+ path +"'");
throw new FormatException($"Section '{name}' is not allowed to have multiple declarations in '{path}'");
if(rule.RequiredFields.Length > 0) {
foreach(var instance in section) {
foreach(var field in rule.RequiredFields) {
if(!instance.ContainsKey(field))
throw new FormatException("Expected field '"+ field +"' in section '" + name + "' was not found in '" + path + "'");
throw new FormatException($"Expected field '{field}' in section '{name}' was not found in '{path}'");
}
}
}
} else if(rule.Required)
throw new FormatException("Expected section '"+ name +"' was not found in '"+ path +"'");
throw new FormatException($"Expected section '{name}' was not found in '{path}'");
}
}

View file

@ -118,7 +118,7 @@ namespace Kneesocks {
var rawOpcode = raw[0] & 0x0F;
if(!Enum.IsDefined(typeof(kOpcode), rawOpcode))
throw new ArgumentException("Opcode '0x" + rawOpcode.ToString("X") + "' not understood");
throw new ArgumentException($"Opcode '0x{rawOpcode.ToString("X")}' not understood");
var returnFrame = new Frame {
IsFinal = (raw[0] & 0x80) != 0,
@ -156,9 +156,9 @@ namespace Kneesocks {
uint expectedFrameLength = (uint)returnFrame.BodyLength + (uint)returnFrame.HeaderLength;
if((uint)raw.Length < expectedFrameLength)
throw new FormatException("Raw frame length ("+ (uint)raw.Length + ") is less than described size ("+ expectedFrameLength + ")");
throw new FormatException($"Raw frame length ({(uint)raw.Length}) is less than described size ({expectedFrameLength})");
returnFrame.Content = new byte[returnFrame.BodyLength];
returnFrame.Content = new byte[returnFrame.BodyLength];
Array.Copy(raw, returnFrame.HeaderLength, returnFrame.Content, 0L, returnFrame.BodyLength);
if(returnFrame.IsMasked)
returnFrame.Content = returnFrame.MaskedContent;

View file

@ -30,9 +30,8 @@ namespace Kneesocks {
Gateway_Timeout = 504
}
public kStatusCode? StatusCode { get; private set; } = null;
protected string StatusCodeText {
get => Enum.GetName(typeof(kStatusCode), StatusCode).Replace('_', ' ');
}
protected string StatusCodeText
=> Enum.GetName(typeof(kStatusCode), StatusCode).Replace('_', ' ');
private readonly Dictionary<string, string> Headers =
new Dictionary<string, string>(StringComparer.OrdinalIgnoreCase);
@ -103,9 +102,9 @@ namespace Kneesocks {
SetHeader("Content-Type", "text/plain");
}
var raw = "HTTP/"+ HttpVersion +" "+ (int)StatusCode + " "+ StatusCodeText +"\r\n";
var raw = $"HTTP/{HttpVersion} {(int)StatusCode} {StatusCodeText}\r\n";
foreach(var header in Headers)
raw += header.Key.Trim() + ": " + header.Value.Trim() + "\r\n";
raw += $"{header.Key.Trim()}: {header.Value.Trim()}\r\n";
return raw + "\r\n";
}

View file

@ -45,6 +45,9 @@ namespace Kneesocks {
CreateThread(runWithNoClients: true);
}
public int ConnectionCount
=> Connections.Count;
public T this[UInt64 id] {
get {
lock (Connections) {
@ -86,7 +89,7 @@ namespace Kneesocks {
if(Disposed)
return false;
if(MaxTotal != 0 && Connections.Count >= MaxTotal)
if(MaxTotal != 0 && ConnectionCount >= MaxTotal)
return false;
lock(Threads) {

View file

@ -32,9 +32,7 @@ namespace Kneesocks {
}
}
public int Count {
get => Clients.Count;
}
public int Count => Clients.Count;
internal void StopThread() => Running = false;

View file

@ -6,26 +6,51 @@ using System.Text;
using System.Threading.Tasks;
namespace SockScape {
class ServerList {
public static Dictionary<int, Server> Servers { get; }
= new Dictionary<int, Server>();
static class ServerList {
private static ServerDictionary Servers { get; }
= new ServerDictionary();
public Server this[int i] {
get => Servers.ContainsKey(i) ? Servers[i] : null;
set => Servers[i] = value;
private static Dictionary<UInt16, Server> List
=> Servers.List;
public static void Write(Server server) {
lock(Servers) {
if(HasId(server.Id) && !List[server.Id].Address.Equals(server.Address))
Console.WriteLine($"{DateTime.Now.ToShortTimeString()} - Server {server.Id} has changed IP addresses.");
List[server.Id] = server;
}
}
public bool HasId(int id)
=> Servers.ContainsKey(id);
public static Packet ReportPacket {
get {
var packet = new Packet(kInterMasterId.);
public void Clear()
=> Servers.Clear();
return packet;
}
}
public static bool HasId(UInt16 id)
=> List.ContainsKey(id);
public static void Clear()
=> List.Clear();
public class ServerDictionary {
public Dictionary<UInt16, Server> List { get; }
= new Dictionary<UInt16, Server>();
public Server this[UInt16 i] {
get => List.ContainsKey(i) ? List[i] : null;
set => List[i] = value;
}
}
}
class Server {
public ushort Id { get; set; }
public ushort UserCount { get; set; }
public IPEndPoint Address { get; set; }
public IPEndPoint Owner { get; set; }
public IPAddress Address { get; set; }
public ushort Port { get; set; }
}
}

View file

@ -11,7 +11,6 @@ namespace SockScape {
class MasterConnection : Connection {
private Key Key;
public Cipher Encryptor { get; private set; }
protected override void OnOpen() {
Key = new Key();
@ -53,7 +52,7 @@ namespace SockScape {
break;
}
Console.WriteLine(Id + " says " + data.GetString());
Console.WriteLine($"{Id} says {data.GetString()}");
}
}
}

View file

@ -18,7 +18,7 @@ namespace SockScape {
private static Thread ListeningThread;
private static bool IsOpen;
private static DateTime LastMessageOut;
private static DateTime LastMessageOut = new DateTime(0);
private static TimeSpan DeltaLastOut
=> DateTime.Now - LastMessageOut;
@ -43,9 +43,6 @@ namespace SockScape {
public static void Listener() {
while(IsOpen) {
if(LastMessageIn.Ticks == 0 && DeltaLastOut.TotalSeconds > 10)
Send(new Packet(kIntraSlaveId.InitiationAttempt, Configuration.General["Master Secret"]));
var endPoint = new IPEndPoint(0, 0);
while(Sock.Available > 0) {
var data = Sock.Receive(ref endPoint);
@ -67,11 +64,11 @@ namespace SockScape {
break;
case kIntraMasterId.PositiveAck:
Console.WriteLine($"Packet type {packet[0]} accepted by master");
break;
case kIntraMasterId.NegativeAck:
Console.WriteLine("Packet type "+ packet[0] +" declined for reason "+ packet[1]);
Console.WriteLine($"Packet type {packet[0]} declined by master for reason {packet[1]}");
break;
case kIntraMasterId.EncryptionError:
@ -82,6 +79,12 @@ namespace SockScape {
}
}
if(LastMessageIn.Ticks == 0 && DeltaLastOut.TotalSeconds > 10)
Send(new Packet(kIntraSlaveId.InitiationAttempt, Configuration.General["Master Secret"]));
else {
}
Thread.Sleep(1);
}
}

View file

@ -7,13 +7,13 @@ using System.Text;
using System.Threading;
using System.Threading.Tasks;
using Glove;
using SockScape.DAL;
using SockScape.Encryption;
namespace SockScape {
static class MasterUdpServer {
private static Dictionary<string, Client> Prospects;
private static Dictionary<string, Client> Clients;
private static ServerList Servers;
private static UdpClient Sock;
private static Thread ListeningThread;
@ -22,8 +22,8 @@ namespace SockScape {
public static void Initialize() {
if(IsOpen || ListeningThread != null)
return;
Servers = new ServerList();
ServerList.Clear();
Prospects = new Dictionary<string, Client>();
Clients = new Dictionary<string, Client>();
@ -50,7 +50,7 @@ namespace SockScape {
: Packet.FromBytes(encryptor.Parse(data));
if(packet == null)
break;
continue;
switch((kIntraSlaveId)packet.Id) {
case kIntraSlaveId.InitiationAttempt:
@ -86,7 +86,30 @@ namespace SockScape {
if(!IsClientConnected(client) || packet.RegionCount < 1)
break;
if(packet.CheckRegions(0, 1)) {
NegativeAck(endPoint, kIntraSlaveId.StatusUpdate, "Server count is malformed.");
break;
}
byte serverCount = packet[0].Raw[0];
if(packet.RegionCount != 1 + 3 * serverCount) {
NegativeAck(endPoint, kIntraSlaveId.StatusUpdate, "Region count does not match server count");
break;
}
for(byte i = 0; i < serverCount; ++i) {
if(!packet.CheckRegions(2 + 3 * i, 2, 2, 2))
continue;
ServerList.Write(new Server {
Id = packet[2 + 3 * i].Raw.UnpackUInt16(),
UserCount = packet[3 + 3 * i].Raw.UnpackUInt16(),
Address = endPoint.Address,
Port = packet[4 + 3 * i].Raw.UnpackUInt16()
});
}
PositiveAck(endPoint, kIntraSlaveId.StatusUpdate);
break;
}
}
@ -117,16 +140,16 @@ namespace SockScape {
return Clients.ContainsKey(client);
}
private static Packet PositiveAck(byte id) {
return new Packet(kIntraMasterId.PositiveAck, id);
private static void PositiveAck(IPEndPoint endPoint, kIntraSlaveId id) {
Send(new Packet(kIntraMasterId.PositiveAck, id), endPoint);
}
private static Packet NegativeAck(byte id, string message = "") {
return new Packet(kIntraMasterId.NegativeAck, id, message);
private static void NegativeAck(IPEndPoint endPoint, kIntraSlaveId id, string message = "An error occurred while parsing a packet.") {
Send(new Packet(kIntraMasterId.NegativeAck, id, message), endPoint);
}
private static Packet EncryptionError(string message = "A general encryption error has occurred.") {
return new Packet(kIntraMasterId.EncryptionError, message);
private static void EncryptionError(IPEndPoint endPoint, string message = "A general encryption error has occurred. Renegotiation required.") {
Send(new Packet(kIntraMasterId.EncryptionError, message), endPoint);
}
class Client {

View file

@ -8,6 +8,7 @@ namespace SockScape {
public enum kInterMasterId {
KeyExchange = 1,
LoginAttempt,
RegistrationAttempt
RegistrationAttempt,
ServerListing
}
}

View file

@ -92,6 +92,18 @@ namespace SockScape {
return this;
}
public bool CheckRegions(int startIndex, params int[] lengths) {
if(startIndex + lengths.Length > RegionCount)
return false;
for(int i = 0; i < lengths.Length; ++i) {
if(this[startIndex + i].Raw.Length == lengths[i])
return false;
}
return true;
}
public byte[] GetBytes() {
var header = new List<byte>();
header.AddRange(MagicNumber);
@ -117,14 +129,14 @@ namespace SockScape {
}
public class Region {
public byte[] Data { get; }
private byte[] Data { get; }
public Region(byte[] data) {
Data = data;
}
public static implicit operator byte[] (Region region) => region.Data;
public string Bytes
public byte[] Raw
=> this;
public static implicit operator string(Region region) {