Esempio n. 1
0
        private void TryConnect(string address)
        {
            var inputName = _mainMenuControl.UserNameBox.Text.Trim();

            if (!UsernameHelpers.IsNameValid(inputName, out var reason))
            {
                var invalidReason = _loc.GetString(reason.ToText());
                _userInterfaceManager.Popup(
                    _loc.GetString("Invalid username:\n{0}", invalidReason),
                    _loc.GetString("Invalid Username"));
                return;
            }

            var configName = _configurationManager.GetCVar <string>("player.name");

            if (_mainMenuControl.UserNameBox.Text != configName)
            {
                _configurationManager.SetCVar("player.name", inputName);
                _configurationManager.SaveToFile();
            }

            _setConnectingState(true);
            _netManager.ConnectFailed += _onConnectFailed;
            try
            {
                ParseAddress(address, out var ip, out var port);
                _client.ConnectToServer(ip, port);
            }
            catch (ArgumentException e)
            {
                _userInterfaceManager.Popup($"Unable to connect: {e.Message}", "Connection error.");
                Logger.Warning(e.ToString());
                _netManager.ConnectFailed -= _onConnectFailed;
            }
        }
Esempio n. 2
0
        /// <summary>
        ///     Checks whether a user name is valid.
        ///     If this is false, feel free to kick the person requesting it. Loudly.
        /// </summary>
        /// <param name="name">The name to check.</param>
        /// <returns>True if the name is acceptable, false otherwise.</returns>
        public static (bool, string?reason) IsNameValid(string name)
        {
            var valid = AHelpers.IsNameValid(name, out var reason);

            if (valid)
            {
                return(true, null);
            }

            return(false, reason.ToText());
        }
        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);
            }
        }
Esempio n. 4
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);
        }