sockscape/server/Socks/MasterIntraServer.cs

189 lines
8 KiB
C#
Raw Normal View History

2017-06-16 21:00:01 +00:00
using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
2017-06-16 21:00:01 +00:00
using System.Text;
using System.Threading;
using System.Threading.Tasks;
2017-08-30 21:02:51 +00:00
using Glove;
2017-09-06 20:59:38 +00:00
using SockScape.DAL;
2017-08-30 21:02:51 +00:00
using SockScape.Encryption;
2017-06-16 21:00:01 +00:00
2017-08-29 20:14:44 +00:00
namespace SockScape {
2017-09-08 21:06:55 +00:00
static class MasterIntraServer {
2017-08-30 21:02:51 +00:00
private static Dictionary<string, Client> Prospects;
private static Dictionary<string, Client> Clients;
2017-06-16 21:00:01 +00:00
private static UdpClient Sock;
2017-08-21 21:03:32 +00:00
private static Thread ListeningThread;
private static bool IsOpen;
2017-06-16 21:00:01 +00:00
public static void Initialize() {
if(IsOpen || ListeningThread != null)
2017-06-16 21:00:01 +00:00
return;
2017-09-06 20:59:38 +00:00
MasterServerList.Clear();
Prospects = new Dictionary<string, Client>();
2017-08-30 21:02:51 +00:00
Clients = new Dictionary<string, Client>();
ushort port = (ushort)Configuration.General["Master Port"];
2017-08-29 20:14:44 +00:00
Sock = new UdpClient(new IPEndPoint(IPAddress.Any, port));
2017-09-08 21:06:55 +00:00
// TODO figure out what this has to do with ICMP (in client too)
/*uint IOC_IN = 0x80000000,
IOC_VENDOR = 0x18000000,
SIO_UDP_CONNRESET = IOC_IN | IOC_VENDOR | 12;
Sock.Client.IOControl((int)SIO_UDP_CONNRESET, new byte[] {0}, null);*/
2017-06-16 21:00:01 +00:00
IsOpen = true;
2017-08-21 21:03:32 +00:00
ListeningThread = new Thread(Listener);
2017-06-16 21:00:01 +00:00
ListeningThread.Start();
}
public static void Listener() {
while(IsOpen) {
2017-09-08 21:06:55 +00:00
IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
2017-06-16 21:00:01 +00:00
while(Sock.Available > 0) {
2017-08-30 21:02:51 +00:00
var data = Sock.Receive(ref endPoint);
var client = endPoint.ToString();
var encryptor = IsClientConnected(client) ? Clients[client].Encryptor : null;
if(data.Take(Packet.MagicNumber.Length).SequenceEqual(Packet.MagicNumber))
encryptor = null;
2017-08-30 21:02:51 +00:00
Packet packet =
encryptor == null ? Packet.FromBytes(data)
2017-09-08 21:06:55 +00:00
: Packet.FromBytes(encryptor.Decrypt(data));
2017-08-30 21:02:51 +00:00
if(packet == null) {
if(encryptor != null)
EncryptionError(endPoint);
2017-09-06 20:59:38 +00:00
continue;
}
2017-09-07 04:26:58 +00:00
2017-09-08 21:06:55 +00:00
if(IsProspectConnected(client) && encryptor == null)
Prospects[client].LastReceive = DateTime.UtcNow;
2017-09-08 21:06:55 +00:00
else if(IsClientConnected(client) && encryptor != null)
Clients[client].LastReceive = DateTime.UtcNow;
2017-09-08 21:06:55 +00:00
switch((kIntraSlaveId)packet.Id) {
case kIntraSlaveId.InitiationAttempt:
if(packet.RegionCount != 1 || IsProspectConnected(client))
2017-08-30 21:02:51 +00:00
break;
if(packet[0] == Configuration.General["Master Secret"]) {
var key = new Key();
Prospects[client] = new Client {
LastReceive = DateTime.UtcNow,
Address = endPoint,
Key = key
};
Send(key.GenerateRequestPacket(), endPoint);
2017-08-30 21:02:51 +00:00
}
break;
case kIntraSlaveId.KeyExchange:
if(!IsProspectConnected(client))
break;
var privateKey = Prospects[client].Key.ParseResponsePacket(packet);
if(privateKey != -1) {
Prospects[client].LastReceive = DateTime.UtcNow;
2017-09-08 21:06:55 +00:00
Prospects[client].Encryptor = new BlockCipher(privateKey);
Clients[client] = Prospects[client];
Prospects.Remove(client);
} else
Prospects.Remove(client);
break;
case kIntraSlaveId.StatusUpdate:
if(!IsClientConnected(client) || packet.RegionCount < 1)
break;
if(!packet.CheckRegionLengths(0, 1)) {
NegativeAck(endPoint, encryptor, kIntraSlaveId.StatusUpdate, "Server count is malformed.");
2017-09-06 20:59:38 +00:00
break;
}
byte serverCount = packet[0].Raw[0];
if(packet.RegionCount != 1 + 3 * serverCount) {
NegativeAck(endPoint, encryptor, kIntraSlaveId.StatusUpdate, "Region count does not match server count");
2017-09-06 20:59:38 +00:00
break;
}
for(byte i = 0; i < serverCount; ++i) {
if(!packet.CheckRegionLengths(1 + 3 * i, 2, 2, 2))
2017-09-06 20:59:38 +00:00
continue;
MasterServerList.Write(new Server {
2017-09-08 21:06:55 +00:00
Id = packet[1 + 3 * i].Raw.UnpackUInt16(),
UserCount = packet[2 + 3 * i].Raw.UnpackUInt16(),
2017-09-06 20:59:38 +00:00
Address = endPoint.Address,
2017-09-08 21:06:55 +00:00
Port = packet[3 + 3 * i].Raw.UnpackUInt16(),
Owner = Clients[client]
2017-09-06 20:59:38 +00:00
});
}
PositiveAck(endPoint, encryptor, kIntraSlaveId.StatusUpdate);
2017-08-30 21:02:51 +00:00
break;
}
2017-06-16 21:00:01 +00:00
}
Prospects = Prospects.Where(x => x.Value.ReceiveDelta.TotalSeconds < 10)
.ToDictionary(x => x.Key, x => x.Value);
var expiredClients = Clients.Where(x => x.Value.ReceiveDelta.TotalSeconds > 60).Select(x => x.Value).ToList();
2017-09-08 21:06:55 +00:00
if(expiredClients.Count > 0)
MasterServerList.RemoveServersByOwners(expiredClients);
2017-06-16 21:00:01 +00:00
Thread.Sleep(1);
}
}
private static void Send(Packet packet, IPEndPoint client) {
Send(packet.GetBytes(), client);
}
private static void Send(byte[] bytes, IPEndPoint client) {
Sock.Send(bytes, bytes.Length, client);
}
2017-06-16 21:00:01 +00:00
public static void Close() {
IsOpen = false;
ListeningThread.Join();
ListeningThread = null;
2017-08-29 20:14:44 +00:00
Sock.Dispose();
2017-06-16 21:00:01 +00:00
}
private static bool IsProspectConnected(string client) {
return Prospects.ContainsKey(client);
}
private static bool IsClientConnected(string client) {
return Clients.ContainsKey(client);
}
2017-09-08 21:06:55 +00:00
private static void PositiveAck(IPEndPoint endPoint, BlockCipher cipher, kIntraSlaveId id) {
Send(cipher.Encrypt(new Packet(kIntraMasterId.PositiveAck, (byte)id).GetBytes()), endPoint);
}
2017-09-08 21:06:55 +00:00
private static void NegativeAck(IPEndPoint endPoint, BlockCipher cipher, kIntraSlaveId id, string message = "An error occurred while parsing a packet.") {
Send(cipher.Encrypt(new Packet(kIntraMasterId.NegativeAck, (byte)id, message).GetBytes()), endPoint);
}
2017-09-06 20:59:38 +00:00
private static void EncryptionError(IPEndPoint endPoint, string message = "A general encryption error has occurred. Renegotiation required.") {
Send(new Packet(kIntraMasterId.EncryptionError, message), endPoint);
}
2017-08-30 21:02:51 +00:00
2017-09-08 21:06:55 +00:00
public class Client {
public IPEndPoint Address { get; set; }
2017-08-30 21:02:51 +00:00
public DateTime LastReceive { get; set; }
public TimeSpan ReceiveDelta => DateTime.UtcNow - LastReceive;
2017-09-08 21:06:55 +00:00
public BlockCipher Encryptor { get; set; }
2017-08-30 21:02:51 +00:00
public Key Key { get; set; }
}
}
2017-06-16 21:00:01 +00:00
}