once as a kid i cried over a mario flash animation
my mom looked very disappointed
This commit is contained in:
parent
07e50a3301
commit
4d281dac5a
7 changed files with 105 additions and 37 deletions
10
protocol.md
10
protocol.md
|
@ -287,24 +287,18 @@ Communication between the master server and clients will be done over a WebSocke
|
|||
</tr>
|
||||
<tr>
|
||||
<td class="center">2</td>
|
||||
<td>Session Id</td>
|
||||
<td>Packed Unsigned Long</td>
|
||||
<td>R<sub>1</sub></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="center">3</td>
|
||||
<td>Secret</td>
|
||||
<td>Bytes (16)</td>
|
||||
<td>R<sub>1</sub></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="center">4</td>
|
||||
<td class="center">3</td>
|
||||
<td>Server Address</td>
|
||||
<td>IPv4 String</td>
|
||||
<td>R<sub>1</sub></td>
|
||||
</tr>
|
||||
<tr>
|
||||
<td class="center">5</td>
|
||||
<td class="center">4</td>
|
||||
<td>Server Port</td>
|
||||
<td>Packed Unsigned Short</td>
|
||||
<td>R<sub>1</sub></td>
|
||||
|
|
|
@ -26,6 +26,16 @@ namespace SockScape {
|
|||
}
|
||||
},
|
||||
|
||||
new SectionRules {
|
||||
Name = "Mail",
|
||||
Required = true,
|
||||
RequiredFields = new[] {
|
||||
"Host",
|
||||
"UseTLS",
|
||||
"Auth"
|
||||
}
|
||||
},
|
||||
|
||||
new SectionRules {
|
||||
Name = "Database",
|
||||
Required = true,
|
||||
|
|
|
@ -8,7 +8,6 @@ using System.Security.Cryptography;
|
|||
|
||||
namespace Glove {
|
||||
public static class RNG {
|
||||
// TODO add cryptographically secure rng
|
||||
private static readonly Random RandCtx = new Random();
|
||||
private static readonly RNGCryptoServiceProvider CsRandCtx
|
||||
= new RNGCryptoServiceProvider();
|
||||
|
|
|
@ -21,6 +21,12 @@ namespace SockScape {
|
|||
}
|
||||
}
|
||||
|
||||
public static Server Get(ushort id) {
|
||||
lock(_Servers) {
|
||||
return _Servers[id];
|
||||
}
|
||||
}
|
||||
|
||||
public static void Write(Server server) {
|
||||
lock(_Servers) {
|
||||
if(HasId(server.Id) && !_Servers[server.Id].Address.Equals(server.Address))
|
||||
|
|
|
@ -13,8 +13,8 @@ using SockScape.Encryption;
|
|||
|
||||
namespace SockScape {
|
||||
class MasterConnection : Connection {
|
||||
private Regex UsernameRegex = new Regex("[A-Z0-9_]", RegexOptions.IgnoreCase);
|
||||
private Regex EmailRegex = new Regex("\\B[A-Z0-9._%+-]+@[A-Z0-9.-]+\\.[A-Z]{2,}\\B", RegexOptions.IgnoreCase);
|
||||
private readonly Regex UsernameRegex = new Regex("[A-Z0-9_]", RegexOptions.IgnoreCase);
|
||||
private readonly Regex EmailRegex = new Regex(@"\B[A-Z0-9._%+-]+@[A-Z0-9.-]+\.[A-Z]{2,}\B", RegexOptions.IgnoreCase);
|
||||
|
||||
private Key Key;
|
||||
public StreamCipher Encryptor { get; private set; }
|
||||
|
@ -55,9 +55,10 @@ namespace SockScape {
|
|||
Encryptor = new StreamCipher(Key.PrivateKey);
|
||||
break;
|
||||
case kInterMasterId.LoginAttempt:
|
||||
if(packet.RegionCount != 3 || !packet.CheckRegions(2, 2))
|
||||
if(packet.RegionCount != 3 || !packet.CheckRegionsMaxLength(0, 16, 255) || !packet.CheckRegionLengths(2, 2))
|
||||
break;
|
||||
|
||||
Session session;
|
||||
using(var db = new ScapeDb()) {
|
||||
User user;
|
||||
if((user = db.Users.FirstOrDefault(x => x.Username == packet[0])) == null) {
|
||||
|
@ -75,8 +76,8 @@ namespace SockScape {
|
|||
break;
|
||||
}
|
||||
|
||||
ushort server = packet[2].Raw.UnpackUInt16();
|
||||
if(!MasterServerList.HasId(server)) {
|
||||
ushort serverId = packet[2].Raw.UnpackUInt16();
|
||||
if(!MasterServerList.HasId(serverId)) {
|
||||
SendEncrypted(new Packet(kInterMasterId.LoginAttempt, Convert.ToByte(false), "The world you have specified is offline."));
|
||||
break;
|
||||
}
|
||||
|
@ -86,32 +87,65 @@ namespace SockScape {
|
|||
db.SaveChanges();
|
||||
}
|
||||
|
||||
db.Sessions.Add(new Session {
|
||||
db.Sessions.Add(session = new Session {
|
||||
Id = user.Id,
|
||||
Secret = RNG.NextBytes(16),
|
||||
ServerId = server,
|
||||
ServerId = serverId,
|
||||
LastPing = DateTime.UtcNow
|
||||
});
|
||||
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
var server = MasterServerList.Get((ushort)session.ServerId);
|
||||
SendEncrypted(new Packet(kInterMasterId.LoginAttempt, Convert.ToByte(true), session.Secret, server.Address.ToString(), server.Port.Pack()));
|
||||
break;
|
||||
case kInterMasterId.RegistrationAttempt:
|
||||
if(packet.RegionCount != 3)
|
||||
if(packet.RegionCount != 3 || !packet.CheckAllMaxLength(0xFF))
|
||||
break;
|
||||
|
||||
using(var db = new ScapeDb()) {
|
||||
if(!packet[0].Raw.IsAsciiString()) {
|
||||
SendEncrypted(new Packet(kInterMasterId.RegistrationAttempt, Convert.ToByte(false), "Your username cannot contain unicode characters."));
|
||||
break;
|
||||
}
|
||||
|
||||
if(packet[0].Raw.Length > 16 || !UsernameRegex.IsMatch(packet[0].Str)) {
|
||||
SendEncrypted(new Packet(kInterMasterId.RegistrationAttempt, Convert.ToByte(false), "The username is max 16 characters and can only be letters, numbers, and underscores."));
|
||||
break;
|
||||
}
|
||||
|
||||
|
||||
if(!packet[0].Raw.IsAsciiString()) {
|
||||
SendEncrypted(new Packet(kInterMasterId.RegistrationAttempt, Convert.ToByte(false), "Your username cannot contain unicode characters."));
|
||||
break;
|
||||
}
|
||||
|
||||
string username = packet[0].Str.Trim(),
|
||||
password = packet[1].Str.Trim(),
|
||||
email = packet[2].Str.Trim();
|
||||
|
||||
if(username.Length > 16 || !UsernameRegex.IsMatch(username)) {
|
||||
SendEncrypted(new Packet(kInterMasterId.RegistrationAttempt, Convert.ToByte(false), "The username is max 16 characters and can only be letters, numbers, and underscores."));
|
||||
break;
|
||||
}
|
||||
|
||||
if(!EmailRegex.IsMatch(email)) {
|
||||
SendEncrypted(new Packet(kInterMasterId.RegistrationAttempt, Convert.ToByte(false), "The email address is malformed."));
|
||||
break;
|
||||
}
|
||||
|
||||
using(var db = new ScapeDb()) {
|
||||
if(db.Users.FirstOrDefault(x => x.Username == username) != null) {
|
||||
SendEncrypted(new Packet(kInterMasterId.RegistrationAttempt, Convert.ToByte(false), "This username is already in use."));
|
||||
break;
|
||||
}
|
||||
|
||||
if(db.Users.FirstOrDefault(x => x.Email == email) != null) {
|
||||
SendEncrypted(new Packet(kInterMasterId.RegistrationAttempt, Convert.ToByte(false), "This email address is already in use."));
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO email activation
|
||||
db.Users.Add(new User {
|
||||
Username = username,
|
||||
Password = password.HashPassword(),
|
||||
Email = email,
|
||||
Joined = DateTime.UtcNow
|
||||
});
|
||||
|
||||
db.SaveChanges();
|
||||
}
|
||||
|
||||
SendEncrypted(new Packet(kInterMasterId.RegistrationAttempt, Convert.ToByte(true), "Registration was successful."));
|
||||
break;
|
||||
case kInterMasterId.ServerListing:
|
||||
SendEncrypted(MasterServerList.ReportPacket);
|
||||
|
|
|
@ -102,7 +102,7 @@ namespace SockScape {
|
|||
if(!IsClientConnected(client) || packet.RegionCount < 1)
|
||||
break;
|
||||
|
||||
if(!packet.CheckRegions(0, 1)) {
|
||||
if(!packet.CheckRegionLengths(0, 1)) {
|
||||
NegativeAck(endPoint, encryptor, kIntraSlaveId.StatusUpdate, "Server count is malformed.");
|
||||
break;
|
||||
}
|
||||
|
@ -114,7 +114,7 @@ namespace SockScape {
|
|||
}
|
||||
|
||||
for(byte i = 0; i < serverCount; ++i) {
|
||||
if(!packet.CheckRegions(1 + 3 * i, 2, 2, 2))
|
||||
if(!packet.CheckRegionLengths(1 + 3 * i, 2, 2, 2))
|
||||
continue;
|
||||
|
||||
MasterServerList.Write(new Server {
|
||||
|
@ -132,7 +132,7 @@ namespace SockScape {
|
|||
}
|
||||
|
||||
Prospects = Prospects.Where(x => x.Value.ReceiveDelta.TotalSeconds < 10)
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
|
||||
var expiredClients = Clients.Where(x => x.Value.ReceiveDelta.TotalSeconds > 60).Select(x => x.Value).ToList();
|
||||
if(expiredClients.Count > 0)
|
||||
|
|
|
@ -50,7 +50,7 @@ namespace SockScape {
|
|||
|
||||
long bodyPtr = headerPtr;
|
||||
foreach(var regionLength in regionLengths) {
|
||||
// FLAG this could fail if one region exceeds 2^31-1 in size, check later
|
||||
// FLAG TODO this could fail if one region exceeds 2^31-1 in size, check later
|
||||
packet.Regions.Add(raw.Subset((int)bodyPtr, (int)regionLength));
|
||||
bodyPtr += regionLength;
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ namespace SockScape {
|
|||
return this;
|
||||
}
|
||||
|
||||
public bool CheckRegions(int startIndex, params int[] lengths) {
|
||||
public bool CheckRegionLengths(int startIndex, params int[] lengths) {
|
||||
if(startIndex + lengths.Length > RegionCount)
|
||||
return false;
|
||||
|
||||
|
@ -115,6 +115,30 @@ namespace SockScape {
|
|||
return true;
|
||||
}
|
||||
|
||||
public bool CheckRegionsMaxLength(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 bool CheckAllMaxLength(int length, int startIndex = 0) {
|
||||
if(startIndex > RegionCount)
|
||||
return false;
|
||||
|
||||
for(int i = startIndex; i < RegionCount; ++i) {
|
||||
if(this[i].Raw.Length > length)
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public byte[] GetBytes() {
|
||||
var header = new List<byte>();
|
||||
header.AddRange(MagicNumber);
|
||||
|
@ -162,8 +186,9 @@ namespace SockScape {
|
|||
}
|
||||
}
|
||||
|
||||
// FLAG TODO determine if you still need this thing, probably not
|
||||
public class PacketBuffer {
|
||||
private short MaxSize;
|
||||
private readonly short MaxSize;
|
||||
private Dictionary<uint, byte[]> Buffer
|
||||
= new Dictionary<uint, byte[]>();
|
||||
|
||||
|
@ -174,9 +199,9 @@ namespace SockScape {
|
|||
public void Add(uint id, byte[] packet) {
|
||||
Buffer[id] = packet;
|
||||
|
||||
if(Buffer.Count > 10)
|
||||
if(Buffer.Count > MaxSize)
|
||||
Buffer =
|
||||
Buffer.Where(x => x.Key >= id - 10)
|
||||
Buffer.Where(x => x.Key >= id - MaxSize)
|
||||
.ToDictionary(x => x.Key, x => x.Value);
|
||||
}
|
||||
|
||||
|
|
Loading…
Reference in a new issue