Пример #1
0
 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
     }));
 }
Пример #2
0
        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
            });
        }
Пример #3
0
 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);
 }
Пример #4
0
 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
     }));
 }
Пример #5
0
 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
     }));
 }
Пример #6
0
        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
            });
        }
Пример #7
0
        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}')."
                );
        }
Пример #8
0
        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()));
        }
Пример #9
0
        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);
        }
Пример #10
0
        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
            });
        }
Пример #11
0
        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
            });
        }
Пример #12
0
 public bool TryGetSession(EndPoint endPoint, [MaybeNullWhen(false)] out MasterServerSession session) =>
 _sessions.TryGetValue(endPoint, out session);