diff --git a/protocol.md b/protocol.md
index 5963cde..7d36327 100644
--- a/protocol.md
+++ b/protocol.md
@@ -211,22 +211,17 @@ Communication between the master server and clients will be done over a WebSocke
Iterated over n (0 ≤ i ≤ n - 1) |
- 2 + 4i |
+ 2 + 3i |
Server Id |
Packed Unsigned Short |
- 3 + 4i |
+ 3 + 3i |
User Count |
Packed Unsigned Short |
- 4 + 4i |
- IPv4 Address |
- Bytes (4) |
-
-
- 5 + 4i |
+ 4 + 3i |
Port |
Packed Unsigned Short |
@@ -313,6 +308,30 @@ Communication between the master server and clients will be done over a WebSocke
+
+
+
+ ID 4: Server List Request
+ [Encrypted] Responder
+ |
+
+
+ # |
+ Region |
+ Type |
+
+
+ 1 |
+ Succeeded |
+ Boolean |
+
+
+ 2 |
+ Message |
+ String |
+
+
+
#### Client to Master
@@ -387,6 +406,16 @@ Communication between the master server and clients will be done over a WebSocke
+
+
+
+ ID 4: Server List Request
+ [Encrypted] Requester
+ Bodyless Packet
+ |
+
+
+
## 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.
diff --git a/server/Entrypoint.cs b/server/Entrypoint.cs
index b0ea486..559861b 100644
--- a/server/Entrypoint.cs
+++ b/server/Entrypoint.cs
@@ -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 {
InitialCount = 3,
diff --git a/server/Libraries/Glove/INI/SettingsFile.cs b/server/Libraries/Glove/INI/SettingsFile.cs
index 883c75f..a8e90e6 100644
--- a/server/Libraries/Glove/INI/SettingsFile.cs
+++ b/server/Libraries/Glove/INI/SettingsFile.cs
@@ -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}'");
}
}
diff --git a/server/Libraries/Kneesocks/Frame.cs b/server/Libraries/Kneesocks/Frame.cs
index 506e7a7..3cbfab8 100644
--- a/server/Libraries/Kneesocks/Frame.cs
+++ b/server/Libraries/Kneesocks/Frame.cs
@@ -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;
diff --git a/server/Libraries/Kneesocks/Handshake.cs b/server/Libraries/Kneesocks/Handshake.cs
index 410c11c..8e634a2 100644
--- a/server/Libraries/Kneesocks/Handshake.cs
+++ b/server/Libraries/Kneesocks/Handshake.cs
@@ -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 Headers =
new Dictionary(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";
}
diff --git a/server/Libraries/Kneesocks/Pool.cs b/server/Libraries/Kneesocks/Pool.cs
index 6bcfc86..f54f098 100644
--- a/server/Libraries/Kneesocks/Pool.cs
+++ b/server/Libraries/Kneesocks/Pool.cs
@@ -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) {
diff --git a/server/Libraries/Kneesocks/Stack.cs b/server/Libraries/Kneesocks/Stack.cs
index 0f3fc0f..84d95bd 100644
--- a/server/Libraries/Kneesocks/Stack.cs
+++ b/server/Libraries/Kneesocks/Stack.cs
@@ -32,9 +32,7 @@ namespace Kneesocks {
}
}
- public int Count {
- get => Clients.Count;
- }
+ public int Count => Clients.Count;
internal void StopThread() => Running = false;
diff --git a/server/ServerList.cs b/server/ServerList.cs
index 13e7b5b..0665bb1 100644
--- a/server/ServerList.cs
+++ b/server/ServerList.cs
@@ -6,26 +6,51 @@ using System.Text;
using System.Threading.Tasks;
namespace SockScape {
- class ServerList {
- public static Dictionary Servers { get; }
- = new Dictionary();
+ 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 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 List { get; }
+ = new Dictionary();
+
+ 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; }
}
}
diff --git a/server/Socks/MasterConnection.cs b/server/Socks/MasterConnection.cs
index edf6947..fb1d9bc 100644
--- a/server/Socks/MasterConnection.cs
+++ b/server/Socks/MasterConnection.cs
@@ -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()}");
}
}
}
diff --git a/server/Socks/MasterUdpClient.cs b/server/Socks/MasterUdpClient.cs
index ebdca27..7ee0e42 100644
--- a/server/Socks/MasterUdpClient.cs
+++ b/server/Socks/MasterUdpClient.cs
@@ -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);
}
}
diff --git a/server/Socks/MasterUdpServer.cs b/server/Socks/MasterUdpServer.cs
index 9e51355..961a735 100644
--- a/server/Socks/MasterUdpServer.cs
+++ b/server/Socks/MasterUdpServer.cs
@@ -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 Prospects;
private static Dictionary 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();
Clients = new Dictionary();
@@ -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 {
diff --git a/server/Socks/Protocols/InterMasterIds.cs b/server/Socks/Protocols/InterMasterIds.cs
index f51e7fc..a33b7a7 100644
--- a/server/Socks/Protocols/InterMasterIds.cs
+++ b/server/Socks/Protocols/InterMasterIds.cs
@@ -8,6 +8,7 @@ namespace SockScape {
public enum kInterMasterId {
KeyExchange = 1,
LoginAttempt,
- RegistrationAttempt
+ RegistrationAttempt,
+ ServerListing
}
}
diff --git a/server/Socks/Protocols/Packet.cs b/server/Socks/Protocols/Packet.cs
index 5610c24..a820d44 100644
--- a/server/Socks/Protocols/Packet.cs
+++ b/server/Socks/Protocols/Packet.cs
@@ -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();
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) {