Esempio n. 1
0
        private async Task InitConnection(Socket socket)
        {
            try
            {
                const int loginMagic         = 10;
                const int reconnectMagic     = 18;
                const int normalConnectMagic = 16;
                const int crcCount           = 9;
                const int initMagicZeroCount = 8;
                const int initHandshakeMagic = 14;
                const int keyCount           = 4;
                const int randomKeySize      = sizeof(long);

                _log.Debug(this, "Initializing socket");

                var blob = new Blob(256);

                // initial handshake
                await SocketReceive(socket, blob, 2);

                var magic = blob.ReadByte();
                if (magic != initHandshakeMagic)
                {
                    _log.Debug(this, $"Killing socket due to back handshake magic ({magic})");
                    socket.Dispose();
                    return;
                }

                // another byte contains a bit of the username but we dont care about that
                blob.ReadCaret++;

                for (var i = 0; i < initMagicZeroCount; i++)
                {
                    blob.Write(0);
                }

                var sState = _server.GetState();
                if (sState.HasFlag(ServerStateFlags.PlayersFull))
                {
                    await KillBadConnection(socket, blob, InitResponseCode.WorldIsFull);

                    return;
                }
                if (sState.HasFlag(ServerStateFlags.LoginDisabled))
                {
                    await KillBadConnection(socket, blob, InitResponseCode.LoginServerOffline);

                    return;
                }

                // initMagicZeroCount can be any InitResponseCode
                blob.Write((byte)InitResponseCode.ContinueToCredentials);

                // write server isaac key
                var serverKey = new byte[randomKeySize];
                _rng.NextBytes(serverKey);
                blob.WriteBlock(serverKey, 0, randomKeySize);

                // send the packet
                await SocketSend(socket, blob);

                // receive login block
                // header
                await SocketReceive(socket, blob, blob.Buffer.Length);

                // todo : catch crypto exceptions
                var decrypted = _crypto.ProcessBlock(blob.Buffer, 0, blob.Buffer.Length);
                blob.Overwrite(decrypted, 0, 0);

                // verify login header magic
                magic = blob.ReadByte();
                if (magic != normalConnectMagic && magic != reconnectMagic)
                {
                    await KillBadConnection(socket, blob, InitResponseCode.GeneralFailure,
                                            $"Invalid login block magic: {magic}");

                    return;
                }

                var isReconnecting = magic == reconnectMagic;

                //1 - length
                //2  - 255
                // skip 'em
                blob.ReadCaret += 2;

                // verify revision
                var revision = blob.ReadInt16();
                if (revision != _revision)
                {
                    await KillBadConnection(socket, blob, InitResponseCode.MustUpdate);

                    return;
                }

                var isLowMem = blob.ReadByte() == 1;

                // read crcs
                var crcs = new int[crcCount];
                for (var i = 0; i < crcCount; i++)
                {
                    crcs[i] = blob.ReadInt32();
                }

                // login block
                // check login magic
                magic = blob.ReadByte();
                if (magic != loginMagic)
                {
                    await KillBadConnection(socket, blob, InitResponseCode.GeneralFailure,
                                            $"Invalid login magic: {magic}");

                    return;
                }

                // read client&server keys
                var keys = new int[keyCount];
                for (var i = 0; i < keyCount; i++)
                {
                    keys[i] = blob.ReadInt32();
                }

                var signlinkUid = blob.ReadInt32();

                // try read user/pass
                string username;
                string password;
                if (!blob.TryReadString(out username, PlayerComponent.MaxUsernameChars))
                {
                    await KillBadConnection(socket, blob, InitResponseCode.GeneralFailure,
                                            "Overflow detected when reading username.");

                    return;
                }

                if (!blob.TryReadString(out password, PlayerComponent.MaxPasswordChars))
                {
                    await KillBadConnection(socket, blob, InitResponseCode.GeneralFailure,
                                            "Overflow detected when reading password.");

                    return;
                }

                username = username.ToLowerInvariant();

                // check if user is logged in
                var loggedInPlayer = _players.Get(username);

                if (isReconnecting)
                {
                    // check if valid user
                    if (loggedInPlayer == null)
                    {
                        await KillBadConnection(socket, blob, InitResponseCode.GeneralFailure,
                                                "Tried to reconnect to player that is not present in ent pool.");

                        return;
                    }

                    var net    = loggedInPlayer.Get().GetNetwork();
                    var player = loggedInPlayer.Get().AssertGetPlayer();
                    if (net == null)
                    {
                        await KillBadConnection(socket, blob, InitResponseCode.GeneralFailure,
                                                "Reconnect player target exists with no network component.");

                        return;
                    }

                    // check if we can reconnect
                    if (!net.CanReinitialize(signlinkUid))
                    {
                        await KillBadConnection(socket, blob, InitResponseCode.GeneralFailure,
                                                "Tried to reconnect but player is not available for reconnecting.");

                        return;
                    }

                    // check if the password matches
                    if (!_db.IsValidPassword(player.Username, password))
                    {
                        await KillBadConnection(socket, blob, InitResponseCode.InvalidCredentials);

                        return;
                    }

                    // all's good, queue reconnect.
                    blob.Write((byte)InitResponseCode.ReconnectDone);
                    _loginQueue.Enqueue(new ReconnectPlayerLogin(loggedInPlayer, socket, signlinkUid));
                }
                else
                {
                    if (loggedInPlayer != null)
                    {
                        await KillBadConnection(socket, blob, InitResponseCode.AccountAlreadyLoggedIn);

                        return;
                    }

                    SerializablePlayerModel model = null;
                    // figure out whether we need to serialize the acc or make anew one.
                    if (_db.PlayerExists(username))
                    {
                        // check pw
                        if (!_db.IsValidPassword(username, password))
                        {
                            await KillBadConnection(socket, blob, InitResponseCode.InvalidCredentials);

                            return;
                        }

                        model = _db.Load(username);
                        if (model == null)
                        {
                            _log.Warning(this, $"Failed loading player for {username}");
                            await KillBadConnection(socket, blob, InitResponseCode.GeneralFailure);

                            return;
                        }
                    }
                    else
                    {
                        model = SerializablePlayerModel.Default(username, _skills);
                        _db.SetPassword(username, password);
                    }

                    blob.Write((byte)InitResponseCode.LoginDone);
                    blob.Write(0); // is flagged
                    blob.Write((byte)model.TitleId);

                    var login = new NormalPlayerLogin(
                        _services,
                        _greeting,
                        model,
                        socket,
                        signlinkUid);

                    _loginQueue.Enqueue(login);
                }

                await SocketSend(socket, blob);

                socket.Blocking = false;
                _log.Debug(this, "Done socket init.");
            }
            catch (SocketException)
            {
                _log.Debug(this, "SocketException in Entry.");
            }
            catch (ObjectDisposedException)
            {
                _log.Debug(this, "ObjectDisposedException in Entry.");
            }
            catch (CryptoException cryptEx)
            {
                _log.Exception(this, "Crypto Exception in EntryPoint.", cryptEx);
            }
            catch (Exception ex)
            {
                _log.Exception(this, "Unhandled exception in EntryPoint.", ex);
                Debug.Fail(ex.ToString());
            }
        }