Beispiel #1
0
        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);
        }
Beispiel #2
0
        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);
        }
Beispiel #3
0
        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;
            }
        }
Beispiel #4
0
        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);
            }
        }
Beispiel #6
0
        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;
                }