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