Пример #1
0
        public static void Initialize()
        {
            if (IsOpen || ListeningThread != null)
            {
                return;
            }

            MasterServerList.Clear();
            Prospects = new Dictionary <string, Client>();
            Clients   = new Dictionary <string, Client>();

            ushort port = (ushort)Configuration.General["Master Port"];

            Sock = new UdpClient(new IPEndPoint(IPAddress.Any, port));

            // 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);*/

            IsOpen          = true;
            ListeningThread = new Thread(Listener);
            ListeningThread.Start();
        }
Пример #2
0
        protected override void OnReceive(byte[] data)
        {
            Packet packet =
                Encryptor == null?Packet.FromBytes(data)
                    : Packet.FromBytes(Encryptor.Parse(data));

            if (packet == null)
            {
                Disconnect(Frame.kClosingReason.ProtocolError, "Packet received was not legal.");
                return;
            }

            if (packet.Id != (int)kInterMasterId.KeyExchange && Encryptor == null)
            {
                Disconnect(Frame.kClosingReason.ProtocolError, "You must exchange keys before performing any other operations.");
                return;
            }

            // TODO rate limiting by ip
            switch ((kInterMasterId)packet.Id)
            {
            case kInterMasterId.KeyExchange:
                Key.ParseResponsePacket(packet);
                if (!Key.Succeeded)
                {
                    Disconnect(Frame.kClosingReason.ProtocolError, "Could not exchange keys.");
                    return;
                }

                Encryptor = new StreamCipher(Key.PrivateKey);
                break;

            case kInterMasterId.LoginAttempt:
                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)
                    {
                        SendEncrypted(new Packet(kInterMasterId.LoginAttempt, Convert.ToByte(false), "User does not exist."));
                        break;
                    }

                    if (packet[1].Str.Trim() == "" || !packet[1].Str.CheckPassword(user.Password))
                    {
                        SendEncrypted(new Packet(kInterMasterId.LoginAttempt, Convert.ToByte(false), "Password is incorrect."));
                        break;
                    }

                    if (user.Session?.DeltaLastPing.TotalMinutes < 3)
                    {
                        SendEncrypted(new Packet(kInterMasterId.LoginAttempt, Convert.ToByte(false), "You are already logged in. Log out or try again shortly."));
                        break;
                    }

                    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;
                    }

                    if (user.Session?.DeltaLastPing.TotalMinutes >= 3)
                    {
                        db.Entry(user.Session).State = EntityState.Deleted;
                        db.SaveChanges();
                    }

                    db.Sessions.Add(session = new Session {
                        Id       = user.Id,
                        Secret   = RNG.NextBytes(16),
                        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 || !packet.CheckAllMaxLength(0xFF))
                {
                    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);
                break;

            default:
                Disconnect(Frame.kClosingReason.ProtocolError, "Packet ID could not be understood at this time.");
                break;
            }

            Console.WriteLine($"{Id} says {data.GetString()}");
        }
Пример #3
0
        public static void Listener()
        {
            while (IsOpen)
            {
                IPEndPoint endPoint = new IPEndPoint(IPAddress.Any, 0);
                while (Sock.Available > 0)
                {
                    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;
                    }

                    Packet packet =
                        encryptor == null?Packet.FromBytes(data)
                            : Packet.FromBytes(encryptor.Decrypt(data));

                    if (packet == null)
                    {
                        if (encryptor != null)
                        {
                            EncryptionError(endPoint);
                        }
                        continue;
                    }

                    if (IsProspectConnected(client) && encryptor == null)
                    {
                        Prospects[client].LastReceive = DateTime.UtcNow;
                    }
                    else if (IsClientConnected(client) && encryptor != null)
                    {
                        Clients[client].LastReceive = DateTime.UtcNow;
                    }

                    switch ((kIntraSlaveId)packet.Id)
                    {
                    case kIntraSlaveId.InitiationAttempt:
                        if (packet.RegionCount != 1 || IsProspectConnected(client))
                        {
                            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);
                        }
                        break;

                    case kIntraSlaveId.KeyExchange:
                        if (!IsProspectConnected(client))
                        {
                            break;
                        }

                        var privateKey = Prospects[client].Key.ParseResponsePacket(packet);
                        if (privateKey != -1)
                        {
                            Prospects[client].LastReceive = DateTime.UtcNow;
                            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.");
                            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");
                            break;
                        }

                        for (byte i = 0; i < serverCount; ++i)
                        {
                            if (!packet.CheckRegionLengths(1 + 3 * i, 2, 2, 2))
                            {
                                continue;
                            }

                            MasterServerList.Write(new Server {
                                Id        = packet[1 + 3 * i].Raw.UnpackUInt16(),
                                UserCount = packet[2 + 3 * i].Raw.UnpackUInt16(),
                                Address   = endPoint.Address,
                                Port      = packet[3 + 3 * i].Raw.UnpackUInt16(),
                                Owner     = Clients[client]
                            });
                        }

                        PositiveAck(endPoint, encryptor, kIntraSlaveId.StatusUpdate);
                        break;
                    }
                }

                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();
                if (expiredClients.Count > 0)
                {
                    MasterServerList.RemoveServersByOwners(expiredClients);
                }

                Thread.Sleep(1);
            }
        }