private async void HandleInitialHandshakeComplete(NetPeerData peer, NetConnection sender, NetUserId userId, string userName, NetEncryption?encryption, LoginType loginType) { var channel = new NetChannel(this, sender, userId, userName, loginType); _assignedUserIds.Add(userId, sender); _assignedUsernames.Add(userName, sender); _channels.Add(sender, channel); peer.AddChannel(channel); channel.Encryption = encryption; _strings.SendFullTable(channel); try { await Task.Delay(1000); await _serializer.Handshake(channel); } catch (TaskCanceledException) { // Client disconnected during handshake. return; } Logger.InfoS("net", "{ConnectionEndpoint}: Connected", channel.RemoteEndPoint); OnConnected(channel); }
private async void HandleInitialHandshakeComplete(NetPeerData peer, NetConnection sender) { var session = _assignedSessions[sender]; var channel = new NetChannel(this, sender, session); _channels.Add(sender, channel); peer.AddChannel(channel); _strings.SendFullTable(channel); try { await _serializer.Handshake(channel); } catch (TaskCanceledException) { // Client disconnected during handshake. return; } Logger.InfoS("net", "{ConnectionEndpoint}: Connected", channel.RemoteEndPoint); OnConnected(channel); }
private void HandleStatusChanged(NetPeerData peer, NetIncomingMessage msg) { var sender = msg.SenderConnection; msg.ReadByte(); var reason = msg.ReadString(); Logger.DebugS("net", "{ConnectionEndpoint}: Status changed to {ConnectionStatus}, reason: {ConnectionStatusReason}", sender.RemoteEndPoint, sender.Status, reason); if (_awaitingStatusChange.TryGetValue(sender, out var resume)) { _awaitingStatusChange.Remove(sender); resume.Item1.Dispose(); resume.Item2.SetResult(reason); return; } switch (sender.Status) { case NetConnectionStatus.Connected: if (IsServer) { HandleHandshake(peer, sender); } break; case NetConnectionStatus.Disconnected: if (_awaitingData.TryGetValue(sender, out var awaitInfo)) { awaitInfo.Item1.Dispose(); awaitInfo.Item2.TrySetException( new ClientDisconnectedException($"Disconnected: {reason}")); _awaitingData.Remove(sender); } if (_channels.ContainsKey(sender)) { HandleDisconnect(peer, sender, reason); } if (_awaitingDisconnect.TryGetValue(sender, out var tcs)) { tcs.TrySetResult(null); } break; } }
private void HandleDisconnect(NetPeerData peer, NetConnection connection, string reason) { var channel = _channels[connection]; Logger.InfoS("net", "{ConnectionEndpoint}: Disconnected ({DisconnectReason})", channel.RemoteEndPoint, reason); _assignedSessions.Remove(connection); OnDisconnected(channel, reason); _channels.Remove(connection); peer.RemoveChannel(channel); if (IsClient) { connection.Peer.Shutdown(reason); _toCleanNetPeers.Add(connection.Peer); _strings.Reset(); _cancelConnectTokenSource?.Cancel(); ClientConnectState = ClientConnectionState.NotConnecting; } }
private async void HandleHandshake(NetPeerData peer, NetConnection connection) { try { var incPacket = await AwaitData(connection); var msgLogin = new MsgLoginStart(); msgLogin.ReadFromBuffer(incPacket); var ip = connection.RemoteEndPoint.Address; var isLocal = IPAddress.IsLoopback(ip) && _config.GetCVar(CVars.AuthAllowLocal); var canAuth = msgLogin.CanAuth; var needPk = msgLogin.NeedPubKey; var authServer = _config.GetSecureCVar <string>("auth.server"); if (Auth == AuthMode.Required && !isLocal) { if (!canAuth) { connection.Disconnect("Connecting to this server requires authentication"); return; } } NetEncryption?encryption = null; NetUserId userId; string userName; LoginType type; var padSuccessMessage = true; if (canAuth && Auth != AuthMode.Disabled) { var verifyToken = new byte[4]; RandomNumberGenerator.Fill(verifyToken); var msgEncReq = new MsgEncryptionRequest { PublicKey = needPk ? RsaPublicKey : Array.Empty <byte>(), VerifyToken = verifyToken }; var outMsgEncReq = peer.Peer.CreateMessage(); outMsgEncReq.Write(false); outMsgEncReq.WritePadBits(); msgEncReq.WriteToBuffer(outMsgEncReq); peer.Peer.SendMessage(outMsgEncReq, connection, NetDeliveryMethod.ReliableOrdered); incPacket = await AwaitData(connection); var msgEncResponse = new MsgEncryptionResponse(); msgEncResponse.ReadFromBuffer(incPacket); byte[] verifyTokenCheck; byte[] sharedSecret; try { verifyTokenCheck = _authRsaPrivateKey !.Decrypt( msgEncResponse.VerifyToken, RSAEncryptionPadding.OaepSHA256); sharedSecret = _authRsaPrivateKey !.Decrypt( msgEncResponse.SharedSecret, RSAEncryptionPadding.OaepSHA256); } catch (CryptographicException) { // Launcher gives the client the public RSA key of the server BUT // that doesn't persist if the server restarts. // In that case, the decrypt can fail here. connection.Disconnect("Token decryption failed./nPlease reconnect to this server from the launcher."); return; } if (!verifyToken.SequenceEqual(verifyTokenCheck)) { connection.Disconnect("Verify token is invalid"); return; } encryption = new NetAESEncryption(peer.Peer, sharedSecret, 0, sharedSecret.Length); var authHashBytes = MakeAuthHash(sharedSecret, RsaPublicKey !); var authHash = Base64Helpers.ConvertToBase64Url(authHashBytes); var client = new HttpClient(); var url = $"{authServer}api/session/hasJoined?hash={authHash}&userId={msgEncResponse.UserId}"; var joinedResp = await client.GetAsync(url); joinedResp.EnsureSuccessStatusCode(); var joinedRespJson = JsonConvert.DeserializeObject <HasJoinedResponse>( await joinedResp.Content.ReadAsStringAsync()); if (!joinedRespJson.IsValid) { connection.Disconnect("Failed to validate login"); return; } userId = new NetUserId(joinedRespJson.UserData !.UserId); userName = joinedRespJson.UserData.UserName; padSuccessMessage = false; type = LoginType.LoggedIn; } else { var reqUserName = msgLogin.UserName; if (!UsernameHelpers.IsNameValid(reqUserName, out var reason)) { connection.Disconnect($"Username is invalid ({reason.ToText()})."); return; } // If auth is set to "optional" we need to avoid conflicts between real accounts and guests, // so we explicitly prefix guests. var origName = Auth == AuthMode.Disabled ? reqUserName : (isLocal ? $"localhost@{reqUserName}" : $"guest@{reqUserName}"); var name = origName; var iterations = 1; while (_assignedUsernames.ContainsKey(name)) { // This is shit but I don't care. name = $"{origName}_{++iterations}"; } userName = name; (userId, type) = await AssignUserIdAsync(name); } var endPoint = connection.RemoteEndPoint; var connect = await OnConnecting(endPoint, userId, userName, type); if (connect.IsDenied) { connection.Disconnect($"Connection denied: {connect.DenyReason}"); return; } // Well they're in. Kick a connected client with the same GUID if we have to. if (_assignedUserIds.TryGetValue(userId, out var existing)) { existing.Disconnect("Another connection has been made with your account."); // Have to wait until they're properly off the server to avoid any collisions. await AwaitDisconnectAsync(existing); } var msg = peer.Peer.CreateMessage(); var msgResp = new MsgLoginSuccess { UserId = userId.UserId, UserName = userName, Type = type }; if (padSuccessMessage) { msg.Write(true); msg.WritePadBits(); } msgResp.WriteToBuffer(msg); encryption?.Encrypt(msg); peer.Peer.SendMessage(msg, connection, NetDeliveryMethod.ReliableOrdered); Logger.InfoS("net", "Approved {ConnectionEndpoint} with username {Username} user ID {userId} into the server", connection.RemoteEndPoint, userName, userId); // Handshake complete! HandleInitialHandshakeComplete(peer, connection, userId, userName, encryption, type); } catch (ClientDisconnectedException) { Logger.InfoS("net", $"Peer {NetUtility.ToHexString(connection.RemoteUniqueIdentifier)} disconnected while handshake was in-progress."); } catch (Exception e) { connection.Disconnect("Unknown server error occured during handshake."); Logger.ErrorS("net", "Exception during handshake with peer {0}:\n{1}", NetUtility.ToHexString(connection.RemoteUniqueIdentifier), e); } }
private async void HandleHandshake(NetPeerData peer, NetConnection connection) { string requestedUsername; try { var userNamePacket = await AwaitData(connection); requestedUsername = userNamePacket.ReadString(); } catch (ClientDisconnectedException) { return; } if (!UsernameHelpers.IsNameValid(requestedUsername, out var reason)) { connection.Disconnect($"Username is invalid ({reason.ToText()})."); return; } var endPoint = connection.RemoteEndPoint; var name = requestedUsername; var origName = name; var iterations = 1; while (_assignedSessions.Values.Any(u => u.Username == name)) { // This is shit but I don't care. name = $"{origName}_{++iterations}"; } var session = new NetSessionId(name); if (OnConnecting(endPoint, session)) { _assignedSessions.Add(connection, session); var msg = connection.Peer.CreateMessage(); msg.Write(name); connection.Peer.SendMessage(msg, connection, NetDeliveryMethod.ReliableOrdered); } else { connection.Disconnect("Sorry, denied. Why? Couldn't tell you, I didn't implement a deny reason."); return; } NetIncomingMessage okMsg; try { okMsg = await AwaitData(connection); } catch (ClientDisconnectedException) { return; } if (okMsg.ReadString() != "ok") { connection.Disconnect("You should say ok."); return; } Logger.InfoS("net", "Approved {ConnectionEndpoint} with username {Username} into the server", connection.RemoteEndPoint, session); // Handshake complete! HandleInitialHandshakeComplete(peer, connection); }
private async void HandleHandshake(NetPeerData peer, NetConnection connection) { try { var incPacket = await AwaitData(connection); var msgLogin = new MsgLoginStart(); msgLogin.ReadFromBuffer(incPacket); var ip = connection.RemoteEndPoint.Address; var isLocal = IPAddress.IsLoopback(ip) && _config.GetCVar(CVars.AuthAllowLocal); var canAuth = msgLogin.CanAuth; var needPk = msgLogin.NeedPubKey; var authServer = _config.GetCVar(CVars.AuthServer); if (Auth == AuthMode.Required && !isLocal) { if (!canAuth) { connection.Disconnect("Connecting to this server requires authentication"); return; } } NetEncryption?encryption = null; NetUserData userData; LoginType type; var padSuccessMessage = true; if (canAuth && Auth != AuthMode.Disabled) { var verifyToken = new byte[4]; RandomNumberGenerator.Fill(verifyToken); var msgEncReq = new MsgEncryptionRequest { PublicKey = needPk ? RsaPublicKey : Array.Empty <byte>(), VerifyToken = verifyToken }; var outMsgEncReq = peer.Peer.CreateMessage(); outMsgEncReq.Write(false); outMsgEncReq.WritePadBits(); msgEncReq.WriteToBuffer(outMsgEncReq); peer.Peer.SendMessage(outMsgEncReq, connection, NetDeliveryMethod.ReliableOrdered); incPacket = await AwaitData(connection); var msgEncResponse = new MsgEncryptionResponse(); msgEncResponse.ReadFromBuffer(incPacket); byte[] verifyTokenCheck; byte[] sharedSecret; try { verifyTokenCheck = _authRsaPrivateKey !.Decrypt( msgEncResponse.VerifyToken, RSAEncryptionPadding.OaepSHA256); sharedSecret = _authRsaPrivateKey !.Decrypt( msgEncResponse.SharedSecret, RSAEncryptionPadding.OaepSHA256); } catch (CryptographicException) { // Launcher gives the client the public RSA key of the server BUT // that doesn't persist if the server restarts. // In that case, the decrypt can fail here. connection.Disconnect( "Token decryption failed.\nPlease reconnect to this server from the launcher."); return; } if (!verifyToken.SequenceEqual(verifyTokenCheck)) { connection.Disconnect("Verify token is invalid"); return; } if (msgLogin.Encrypt) { encryption = new NetAESEncryption(peer.Peer, sharedSecret, 0, sharedSecret.Length); } var authHashBytes = MakeAuthHash(sharedSecret, RsaPublicKey !); var authHash = Base64Helpers.ConvertToBase64Url(authHashBytes); var client = new HttpClient(); var url = $"{authServer}api/session/hasJoined?hash={authHash}&userId={msgEncResponse.UserId}"; var joinedRespJson = await client.GetFromJsonAsync <HasJoinedResponse>(url); if (joinedRespJson is not { IsValid : true }) { connection.Disconnect("Failed to validate login"); return; } var userId = new NetUserId(joinedRespJson.UserData !.UserId); userData = new NetUserData(userId, joinedRespJson.UserData.UserName) { PatronTier = joinedRespJson.UserData.PatronTier, HWId = msgLogin.HWId }; padSuccessMessage = false; type = LoginType.LoggedIn; }