public Task <AuthenticateUserResponse> Authenticate(MasterServerSession session, AuthenticateUserRequest request) { _logger.Verbose( $"Handling {nameof(AuthenticateUserRequest)} " + $"(Platform={request.AuthenticationToken.Platform}, " + $"UserId='{request.AuthenticationToken.UserId}', " + $"UserName='******')." ); // TODO: Verify that there aren't any other sessions with the same user ID // TODO: Validate session token? _logger.Information( "Session authenticated " + $"(EndPoint='{session.EndPoint}', " + $"Platform={request.AuthenticationToken.Platform}, " + $"UserId='{request.AuthenticationToken.UserId}', " + $"UserName='******')." ); session.Platform = request.AuthenticationToken.Platform; session.UserId = request.AuthenticationToken.UserId; session.UserName = request.AuthenticationToken.UserName; return(Task.FromResult(new AuthenticateUserResponse { Result = AuthenticateUserResponse.ResultCode.Success })); }
public async Task <ServerHelloRequest> ClientHelloWithCookie(MasterServerSession session, ClientHelloWithCookieRequest request) { _logger.Verbose( $"Handling {nameof(ClientHelloWithCookieRequest)} " + $"(CertificateResponseId={request.CertificateResponseId}, " + $"Random='{BitConverter.ToString(request.Random)}', " + $"Cookie='{BitConverter.ToString(request.Cookie)}')." ); if (!request.Cookie.SequenceEqual(session.Cookie)) { _logger.Warning( $"Session sent {nameof(ClientHelloWithCookieRequest)} with a mismatching cookie " + $"(EndPoint='{session.EndPoint}', " + $"Cookie='{BitConverter.ToString(request.Cookie)}', " + $"Expected='{BitConverter.ToString(session.Cookie ?? new byte[0])}')." ); return(null); } if (!request.Random.SequenceEqual(session.ClientRandom)) { _logger.Warning( $"Session sent {nameof(ClientHelloWithCookieRequest)} with a mismatching client random " + $"(EndPoint='{session.EndPoint}', " + $"Random='{BitConverter.ToString(request.Random)}', " + $"Expected='{BitConverter.ToString(session.ClientRandom ?? new byte[0])}')." ); return(null); } // Generate a server random session.ServerRandom = _randomProvider.GetRandom(); // Generate a key pair var keyPair = _diffieHellmanService.GetECKeyPair(); session.ServerPrivateKeyParameters = keyPair.PrivateKeyParameters; // Generate a signature var signature = MakeSignature(session.ClientRandom, session.ServerRandom, keyPair.PublicKey); await _messageDispatcher.SendWithRetry(session, new ServerCertificateRequest() { ResponseId = request.CertificateResponseId, Certificates = new List <byte[]>() { _certificate.RawData } }); return(new ServerHelloRequest { Random = session.ServerRandom, PublicKey = keyPair.PublicKey, Signature = signature }); }
public Task SessionKeepalive(MasterServerSession session, SessionKeepaliveMessage message) { _logger.Verbose( $"Handling {nameof(SessionKeepalive)} " + $"(EndPoint='{session.EndPoint}', " + $"Platform={session.Platform}, " + $"UserId='{session.UserId}', " + $"UserName='******')." ); session.LastKeepAlive = DateTimeOffset.UtcNow; return(Task.CompletedTask); }
public Task <HelloVerifyRequest> ClientHello(MasterServerSession session, ClientHelloRequest request) { _logger.Verbose( $"Handling {nameof(ClientHelloRequest)} " + $"(Random='{BitConverter.ToString(request.Random)}')." ); session.Epoch = request.RequestId & _epochMask; session.State = MasterServerSessionState.New; session.EncryptionParameters = null; session.Cookie = _cookieProvider.GetCookie(); session.ClientRandom = request.Random; return(Task.FromResult(new HelloVerifyRequest { Cookie = session.Cookie })); }
public Task <ConnectToServerResponse> ConnectToMatchmaking(MasterServerSession session, ConnectToMatchmakingRequest request) { _logger.Verbose( $"Handling {nameof(ConnectToMatchmakingRequest)} " + $"(UserId='{request.UserId}', " + $"UserName='******', " + $"Random='{BitConverter.ToString(request.Random)}', " + $"PublicKey='{BitConverter.ToString(request.PublicKey)}', " + $"BeatmapDifficultyMask={request.Configuration.BeatmapDifficultyMask}, " + $"GameplayModifiersMask={request.Configuration.GameplayModifiersMask}, " + $"Secret='{request.Secret}')." ); return(Task.FromResult(new ConnectToServerResponse { Result = ConnectToServerResponse.ResultCode.UnknownError })); }
public async Task BroadcastServerHeartbeat(MasterServerSession session, BroadcastServerHeartbeatRequest request) { _logger.Verbose( $"Handling {nameof(BroadcastServerHeartbeatRequest)} " + $"(UserId='{request.UserId}', " + $"UserName='******', " + $"Secret='{request.Secret}', " + $"CurrentPlayerCount={request.CurrentPlayerCount})." ); if (session.Secret != request.Secret) { _logger.Warning( $"User sent {nameof(BroadcastServerHeartbeatRequest)} " + "with an invalid secret " + $"(UserId='{request.UserId}', " + $"UserName='******', " + $"Secret='{request.Secret}', " + $"Expected='{session.Secret}')." ); _messageDispatcher.Send(session, new BroadcastServerHeartbeatResponse { Result = BroadcastServerHeartbeatResponse.ResultCode.UnknownError }); return; } var server = await _serverRepository.GetServer(request.Secret); if (server == null) { _messageDispatcher.Send(session, new BroadcastServerHeartbeatResponse { Result = BroadcastServerHeartbeatResponse.ResultCode.ServerDoesNotExist }); return; } session.LastKeepAlive = DateTimeOffset.UtcNow; _serverRepository.UpdateCurrentPlayerCount(request.Secret, (int)request.CurrentPlayerCount); _messageDispatcher.Send(session, new BroadcastServerHeartbeatResponse { Result = BroadcastServerHeartbeatResponse.ResultCode.Success }); }
public async Task BroadcastServerRemove(MasterServerSession session, BroadcastServerRemoveRequest request) { _logger.Verbose( $"Handling {nameof(BroadcastServerRemoveRequest)} " + $"(UserId='{request.UserId}', " + $"UserName='******', " + $"Secret='{request.Secret}')." ); if (session.Secret != request.Secret) { _logger.Warning( $"User sent {nameof(BroadcastServerRemoveRequest)} " + "with an invalid secret " + $"(UserId='{request.UserId}', " + $"UserName='******', " + $"Secret='{request.Secret}', " + $"Expected='{session.Secret}')." ); return; } var server = await _serverRepository.GetServer(request.Secret); if (server == null) { return; } if (!await _serverRepository.RemoveServer(server.Secret)) { return; } _logger.Information( "Successfully removed server " + $"(UserId='{request.UserId}', " + $"UserName='******', " + $"Secret='{server.Secret}')." ); }
public Task <ChangeCipherSpecRequest> ClientKeyExchange(MasterServerSession session, ClientKeyExchangeRequest request) { _logger.Verbose( $"Handling {nameof(ClientKeyExchange)} " + $"(ClientPublicKey='{BitConverter.ToString(request.ClientPublicKey)}')." ); session.ClientPublicKeyParameters = _diffieHellmanService.DeserializeECPublicKey(request.ClientPublicKey); session.PreMasterSecret = _diffieHellmanService.GetPreMasterSecret( session.ClientPublicKeyParameters, session.ServerPrivateKeyParameters ! ); var receiveKey = new byte[32]; var sendKey = new byte[32]; var sendMacSourceArray = new byte[64]; var receiveMacSourceArray = new byte[64]; var masterSecretSeed = MakeSeed(_masterSecretSeed, session.ServerRandom !, session.ClientRandom !); var keyExpansionSeed = MakeSeed(_keyExpansionSeed, session.ServerRandom !, session.ClientRandom !); var sourceArray = PRF( PRF(session.PreMasterSecret, masterSecretSeed, 48), keyExpansionSeed, 192 ); Array.Copy(sourceArray, 0, sendKey, 0, 32); Array.Copy(sourceArray, 32, receiveKey, 0, 32); Array.Copy(sourceArray, 64, sendMacSourceArray, 0, 64); Array.Copy(sourceArray, 128, receiveMacSourceArray, 0, 64); session.EncryptionParameters = new EncryptionParameters( receiveKey, sendKey, new HMACSHA256(receiveMacSourceArray), new HMACSHA256(sendMacSourceArray) ); session.State = MasterServerSessionState.Established; _logger.Information($"Session established (EndPoint='{session.EndPoint}')."); return(Task.FromResult(new ChangeCipherSpecRequest())); }
public bool CloseSession(MasterServerSession session) { if (!_sessions.TryRemove(session.EndPoint, out _)) { return(false); } if (session.State == MasterServerSessionState.Authenticated) { _logger.Information( "Closing session " + $"(EndPoint='{session.EndPoint}', " + $"Platform={session.Platform}, " + $"UserId='{session.UserId}', " + $"UserName='******')." ); } else { _logger.Information($"Closing session (EndPoint='{session.EndPoint}')."); } session.State = MasterServerSessionState.None; return(true); }
public async Task <BroadcastServerStatusResponse> BroadcastServerStatus(MasterServerSession session, BroadcastServerStatusRequest request) { _logger.Verbose( $"Handling {nameof(BroadcastServerStatusRequest)} " + $"(ServerName='{request.ServerName}', " + $"UserId='{request.UserId}', " + $"UserName='******', " + $"Secret='{request.Secret}', " + $"CurrentPlayerCount={request.CurrentPlayerCount}, " + $"MaximumPlayerCount={request.MaximumPlayerCount}, " + $"DiscoveryPolicy={request.DiscoveryPolicy}, " + $"InvitePolicy={request.InvitePolicy}, " + $"BeatmapDifficultyMask={request.Configuration.BeatmapDifficultyMask}, " + $"GameplayModifiersMask={request.Configuration.GameplayModifiersMask}, " + $"Random={BitConverter.ToString(request.Random)}, " + $"PublicKey={BitConverter.ToString(request.PublicKey)})." ); var server = await _serverRepository.GetServer(request.Secret); if (server != null) { return new BroadcastServerStatusResponse { Result = BroadcastServerStatusResponse.ResultCode.SecretNotUnique } } ; // TODO: We should probably retry in the event that a duplicate // code is ever generated (pretty unlikely to happen though) session.Secret = request.Secret; server = new Server() { Host = new Player() { UserId = request.UserId, UserName = request.UserName }, RemoteEndPoint = (IPEndPoint)session.EndPoint, Secret = request.Secret, Code = _serverCodeProvider.Generate(), IsPublic = request.DiscoveryPolicy == DiscoveryPolicy.Public, DiscoveryPolicy = (Data.Enums.DiscoveryPolicy)request.DiscoveryPolicy, InvitePolicy = (Data.Enums.InvitePolicy)request.InvitePolicy, BeatmapDifficultyMask = (Data.Enums.BeatmapDifficultyMask)request.Configuration.BeatmapDifficultyMask, GameplayModifiersMask = (Data.Enums.GameplayModifiersMask)request.Configuration.GameplayModifiersMask, SongPackBloomFilterTop = request.Configuration.SongPackBloomFilterTop, SongPackBloomFilterBottom = request.Configuration.SongPackBloomFilterBottom, CurrentPlayerCount = request.CurrentPlayerCount, MaximumPlayerCount = request.MaximumPlayerCount, Random = request.Random, PublicKey = request.PublicKey }; if (!await _serverRepository.AddServer(server)) { _logger.Warning( "Failed to create server " + $"(ServerName='{request.ServerName}', " + $"UserId='{request.UserId}', " + $"UserName='******', " + $"Secret='{request.Secret}', " + $"Code='{server.Code}', " + $"CurrentPlayerCount={request.CurrentPlayerCount}, " + $"MaximumPlayerCount={request.MaximumPlayerCount}, " + $"DiscoveryPolicy={request.DiscoveryPolicy}, " + $"InvitePolicy={request.InvitePolicy}, " + $"BeatmapDifficultyMask={request.Configuration.BeatmapDifficultyMask}, " + $"GameplayModifiersMask={request.Configuration.GameplayModifiersMask}, " + $"Random={BitConverter.ToString(request.Random)}, " + $"PublicKey={BitConverter.ToString(request.PublicKey)})." ); return(new BroadcastServerStatusResponse { Result = BroadcastServerStatusResponse.ResultCode.UnknownError }); } _logger.Information( "Successfully created server " + $"(ServerName='{request.ServerName}', " + $"UserId='{request.UserId}', " + $"UserName='******', " + $"Secret='{request.Secret}', " + $"Code='{server.Code}', " + $"CurrentPlayerCount={request.CurrentPlayerCount}, " + $"MaximumPlayerCount={request.MaximumPlayerCount}, " + $"DiscoveryPolicy={request.DiscoveryPolicy}, " + $"InvitePolicy={request.InvitePolicy}, " + $"BeatmapDifficultyMask={request.Configuration.BeatmapDifficultyMask}, " + $"GameplayModifiersMask={request.Configuration.GameplayModifiersMask}, " + $"Random='{BitConverter.ToString(request.Random)}', " + $"PublicKey='{BitConverter.ToString(request.PublicKey)}')." ); return(new BroadcastServerStatusResponse { Result = BroadcastServerStatusResponse.ResultCode.Success, Code = server.Code, RemoteEndPoint = server.RemoteEndPoint }); }
public async Task <ConnectToServerResponse> ConnectToServer(MasterServerSession session, ConnectToServerRequest request) { _logger.Verbose( $"Handling {nameof(ConnectToServerRequest)} " + $"(UserId='{request.UserId}', " + $"UserName='******', " + $"Random='{BitConverter.ToString(request.Random)}', " + $"PublicKey='{BitConverter.ToString(request.PublicKey)}', " + $"Secret='{request.Secret}', " + $"Code='{request.Code}', " + $"Password='******', " + $"UseRelay={request.UseRelay})." ); Server server = null; if (!String.IsNullOrEmpty(request.Code)) { server = await _serverRepository.GetServerByCode(request.Code); } else if (!String.IsNullOrEmpty(request.Secret)) { server = await _serverRepository.GetServer(request.Secret); } if (server is null) { return new ConnectToServerResponse { Result = ConnectToServerResponse.ResultCode.InvalidCode } } ; if (server.CurrentPlayerCount >= server.MaximumPlayerCount) { return new ConnectToServerResponse { Result = ConnectToServerResponse.ResultCode.ServerAtCapacity } } ; if (!_sessionService.TryGetSession(server.RemoteEndPoint, out var hostSession)) { _logger.Warning( "Failed to retrieve server host session while handling " + $"{nameof(ConnectToServerRequest)} " + $"(RemoteEndPoint='{server.RemoteEndPoint}', " + $"UserId='{request.UserId}', " + $"UserName='******', " + $"Random='{BitConverter.ToString(request.Random)}', " + $"PublicKey='{BitConverter.ToString(request.PublicKey)}', " + $"Secret='{request.Secret}', " + $"Code='{request.Code}', " + $"Password='******', " + $"UseRelay={request.UseRelay})." ); return(new ConnectToServerResponse { Result = ConnectToServerResponse.ResultCode.UnknownError }); } var connectingEndPoint = (IPEndPoint)session.EndPoint; var remoteEndPoint = (IPEndPoint)hostSession.EndPoint; if (request.UseRelay) { GetAvailableRelayServerResponse getAvailableRelayServerResponse; try { getAvailableRelayServerResponse = await _relayServerService.GetAvailableRelayServer( new GetAvailableRelayServerRequest( session.EndPoint.ToString() !, hostSession.EndPoint.ToString() ! ) ); } catch (TimeoutException e) { _logger.Error(e, "Failed to get an available relay server while handling " + $"{nameof(ConnectToServerRequest)} " + $"(RemoteEndPoint='{server.RemoteEndPoint}', " + $"UserId='{request.UserId}', " + $"UserName='******', " + $"Random='{BitConverter.ToString(request.Random)}', " + $"PublicKey='{BitConverter.ToString(request.PublicKey)}', " + $"Secret='{request.Secret}', " + $"Code='{request.Code}', " + $"Password='******', " + $"UseRelay={request.UseRelay})." ); return(new ConnectToServerResponse { Result = ConnectToServerResponse.ResultCode.NoAvailableDedicatedServers }); } if (!getAvailableRelayServerResponse.Success) { _logger.Warning( "No available relay servers while handling " + $"{nameof(ConnectToServerRequest)} " + $"(RemoteEndPoint='{server.RemoteEndPoint}', " + $"UserId='{request.UserId}', " + $"UserName='******', " + $"Random='{BitConverter.ToString(request.Random)}', " + $"PublicKey='{BitConverter.ToString(request.PublicKey)}', " + $"Secret='{request.Secret}', " + $"Code='{request.Code}', " + $"Password='******', " + $"UseRelay={request.UseRelay})." ); return(new ConnectToServerResponse { Result = ConnectToServerResponse.ResultCode.NoAvailableDedicatedServers }); } remoteEndPoint = IPEndPoint.Parse(getAvailableRelayServerResponse.RemoteEndPoint); connectingEndPoint = remoteEndPoint; } // Let the host know that someone is about to connect (hole-punch) await _messageDispatcher.SendWithRetry(hostSession, new PrepareForConnectionRequest { UserId = request.UserId, UserName = request.UserName, RemoteEndPoint = connectingEndPoint, Random = request.Random, PublicKey = request.PublicKey, IsConnectionOwner = false, IsDedicatedServer = false }); session.Secret = request.Secret; _logger.Information( "Successfully connected to server " + $"(RemoteEndPoint='{remoteEndPoint}', " + $"UserId='{request.UserId}', " + $"UserName='******', " + $"Random='{BitConverter.ToString(request.Random)}', " + $"PublicKey='{BitConverter.ToString(request.PublicKey)}', " + $"Secret='{request.Secret}', " + $"Code='{request.Code}', " + $"Password='******', " + $"UseRelay={request.UseRelay})." ); return(new ConnectToServerResponse { Result = ConnectToServerResponse.ResultCode.Success, UserId = server.Host.UserId, UserName = server.Host.UserName, Secret = server.Secret, DiscoveryPolicy = (DiscoveryPolicy)server.DiscoveryPolicy, InvitePolicy = (InvitePolicy)server.InvitePolicy, MaximumPlayerCount = server.MaximumPlayerCount, Configuration = new GameplayServerConfiguration() { BeatmapDifficultyMask = (BeatmapDifficultyMask)server.BeatmapDifficultyMask, GameplayModifiersMask = (GameplayModifiersMask)server.GameplayModifiersMask, SongPackBloomFilterTop = server.SongPackBloomFilterTop, SongPackBloomFilterBottom = server.SongPackBloomFilterBottom }, IsConnectionOwner = true, IsDedicatedServer = false, RemoteEndPoint = remoteEndPoint, Random = server.Random, PublicKey = server.PublicKey }); }
public bool TryGetSession(EndPoint endPoint, [MaybeNullWhen(false)] out MasterServerSession session) => _sessions.TryGetValue(endPoint, out session);