Exemplo n.º 1
0
        private static void HandleClient()
        {
            LogManager.Log("RCON", "Accepted RCON connection from " + currentClient.Username + " (" + currentClient.RemoteEndpoint + ")");

            bool          closingConnection = false;
            NetworkStream stream            = currentClient.tcpClient.GetStream();

            string welcomeString = Program.BismuthWelcomeHeader + "\r\nWelcome to this Bismuth RCON server\r\n";

            if (true) //Check if login required
            {
                welcomeString += "\r\n" + UsernamePrompt;
            }

            SendClientResponse(stream, Encoding.ASCII.GetBytes(welcomeString));

            Stopwatch timeSinceLastRequest = new Stopwatch();
            Stopwatch timeSinceLastInput   = new Stopwatch();

            while (!closingConnection)
            {
                timeSinceLastRequest.Restart();

                LinkedList <byte> requestData = new LinkedList <byte>();
                byte lastByte = 255;
                while (lastByte != 10)
                {
                    timeSinceLastInput.Restart();

                    while (currentClient.tcpClient.Available == 0)
                    {
                        Thread.Sleep(2);
                        if (timeSinceLastRequest.ElapsedMilliseconds > RCONConnectionMaxTTL * 1000)
                        {
                            SendClientResponse(stream, ttlFullExpiredText, true);
                            closingConnection = true;
                            break;
                        }
                        else if (timeSinceLastInput.ElapsedMilliseconds > RCONConnectionInputTTL * 1000)
                        {
                            SendClientResponse(stream, ttlInputExpiredText, true);
                            closingConnection = true;
                            break;
                        }
                    }

                    if (!currentClient.tcpClient.Connected || closingConnection)
                    {
                        closingConnection = true;
                        break;
                    }

                    byte[] pdata = new byte[currentClient.tcpClient.Available];
                    stream.Read(pdata, 0, pdata.Length);
                    for (int i = 0; i < pdata.Length; i++)
                    {
                        if (pdata[i] == 8) //Backspace
                        {
                            if (requestData.Count > 0)
                            {
                                requestData.RemoveLast();
                            }

                            continue;
                        }

                        requestData.AddLast(pdata[i]);
                    }

                    lastByte = requestData.Last == null ? lastByte: requestData.Last.Value;
                }

                if (closingConnection || !currentClient.tcpClient.Connected)
                {
                    break;
                }

                string requestString = Encoding.ASCII.GetString(requestData.ToArray()).TrimEnd('\r', '\n');
                requestString = new string(requestString.Where(c => !char.IsControl(c)).ToArray());

                if (requestString == "" && currentClient.LoginState == RCONClient.ELoginState.NotLoggedIn)
                {
                    requestString = "quit";
                    currentClient.SetLoginState(RCONClient.ELoginState.LoggedIn); //Will be booted straight out again
                }

                if (currentClient.LoginState == RCONClient.ELoginState.NotLoggedIn)
                {
                    currentClient.SetUsername(requestString);
                    currentClient.SetLoginState(RCONClient.ELoginState.NeedPassword);
                    SendClientResponse(stream, PasswordPrompt);
                }
                else if (currentClient.LoginState == RCONClient.ELoginState.NeedPassword)
                {
                    if ((ValidRCONClients.ContainsKey(currentClient.Username) || ValidRCONClients.ContainsKey("@" + currentClient.Username)) &&
                        AuthManager.CheckPlaintextCredentials(authType, requestString, ValidRCONClients[currentClient.Username]))
                    {
                        SendClientResponse(stream, LoginSuccessfulText + currentClient.Username + "!\r\n");
                        currentClient.SetLoginState(RCONClient.ELoginState.LoggedIn);
                        LogManager.Log("RCON", "Client " + currentClient.Username + " successfully logged in");
                    }
                    else
                    {
                        LogManager.Log("RCON", "Client " + currentClient.RemoteEndpoint + " failed to login as " + currentClient.Username);
                        currentClient.SetUsername(currentClient.RemoteEndpoint);
                        SendClientResponse(stream, LoginFailedText);
                        currentClient.SetLoginState(RCONClient.ELoginState.NotLoggedIn);
                    }
                }
                else if (currentClient.LoginState == RCONClient.ELoginState.LoggedIn)
                {
                    string[] request = requestString.Split(' ');

                    if (request.Length == 0)
                    {
                        continue;
                    }

                    string   command     = request[0].ToLower();
                    string[] commandArgs = request.Skip(1).ToArray();

                    if (command == "quit" || command == "disconnect" || command == "exit")
                    {
                        command           = "quit";
                        closingConnection = true;
                    }

                    byte[] responseData;
                    if (commands.ContainsKey(command))
                    {
                        object response = commands[command].Invoke(commandArgs);
                        if (response is byte[])
                        {
                            responseData = (byte[])response;
                        }
                        else
                        {
                            responseData = Encoding.ASCII.GetBytes(response.ToString());
                        }
                    }
                    else
                    {
                        //Presume that the request contained secure data
                        LogManager.Log("RCON - " + currentClient.Username + TelnetInputPromptStr + command + " + " + commandArgs.Length + " args");
                        responseData = Encoding.ASCII.GetBytes("Error: Unknown command '" + command + "'");
                    }

                    SendClientResponse(stream, responseData, closingConnection);
                }
            }

            LogManager.Log("RCON", "Closed RCON connection from " + currentClient.Username + " (" + currentClient.RemoteEndpoint + ")");
            currentClient.tcpClient.Close();
        }
Exemplo n.º 2
0
        public override bool Setup()
        {
            Port   = BismuthConfig.GetConfigValue <int>("RCON.Port", RCON_DEFAULT_PORT, "RCON");
            server = new TcpListener(IPAddress.Any, Port); //TODO: Config
            server.Start();
            LogManager.WriteLine("RCON server started for " + server.LocalEndpoint.ToString(), ConsoleColor.Green);

            RCONConnectionMaxTTL   = BismuthConfig.GetConfigValue <int>("RCON.MaxTimeout", RCON_DEFAULT_TTL_MAX, "RCON");
            RCONConnectionInputTTL = BismuthConfig.GetConfigValue <int>("RCON.InputTimeout", RCON_DEFAULT_TTL_INPUT, "RCON");

            ttlFullExpiredText  = "\r\n\r\nConnection exeeeded max idle time of " + RCONConnectionMaxTTL + " seconds.\r\nYou have been forceably disconnected";
            ttlInputExpiredText = "\r\n\r\nNo input recieved for " + RCONConnectionInputTTL + " seconds.\r\nYou have been forceably disconnected";

            authType = BismuthConfig.GetConfigValue <string>("RCON.AuthMethod", "basic", "RCON");
            List <object> RCONClients = BismuthConfig.GetConfigValue <List <object> >("RCON.Users", "RCON");

            if (RCONClients == null || RCONClients.Count == 0)
            {
                LogManager.Critical("RCON", "No users defined for RCON! Until this is resolved, anyone can access RCON!");
                LogManager.Critical("RCON", "  It might be wise to disable RCON or limit its access to localhost.");
            }
            else
            {
                int    vbarPos;
                string fullstring, username, password;

                for (int i = 0; i < RCONClients.Count; ++i)
                {
                    fullstring = RCONClients[i].ToString();
                    vbarPos    = fullstring.IndexOf('|');

                    if (vbarPos < 0)
                    {
                        LogManager.Warn("RCON", "RCON User " + RCONClients[i] + " has no password defined. Skipping.");
                        continue;
                    }

                    username = fullstring.Substring(0, vbarPos);
                    password = fullstring.Substring(vbarPos + 1);
                    ValidRCONClients.Add(username, password);
                }
            }

            rconListenThread = new Thread(new ThreadStart(RCONListenForClients));
            rconListenThread.Start();

            AddRCONCommand("quit", "Logs out of and disconnects from the server", (args) => { return("Bye!"); });
            AddRCONCommand("exit", "Logs out of and disconnects from the server", (args) => { return("Bye!"); });
            AddRCONCommand("disconnect", "Logs out of and disconnects from the server", (args) => { return("Bye!"); });
            AddRCONCommand("logout", "Logs out of and disconnects from the server", (args) => { return("Bye!"); });
            AddRCONCommand("help", "Displays a full list of commands with associated documentation", (args) =>
            {
                List <string> commandInfoLines = new List <string>();
                foreach (KeyValuePair <string, RCONCommand> command in commands)
                {
                    commandInfoLines.Add(command.Value.Name.PadRight(15) + " - " + command.Value.Documentation);
                }
                commandInfoLines.Sort();

                StringBuilder returnString = new StringBuilder();
                returnString.Append("List of valid RCON commands\r\n");
                for (int i = 0; i < commandInfoLines.Count; i++)
                {
                    returnString.Append(commandInfoLines[i] + "\r\n");
                }

                return(returnString.ToString());
            });

            AddRCONCommand("rcon-add-user", "[SU] Adds a new RCON user encoded with the current auth method", (args) =>
            {
                StringBuilder returnString = new StringBuilder();

                if (currentClient.Username[0] != '@')
                {
                    returnString.Append("Error: You need to be a superuser to delete users.\r\n");
                }
                else if (args.Length != 2)
                {
                    returnString.Append("Invalid number of parameters. Expected 2.\r\n");
                    returnString.Append("Usage: rcon-add-user <username> <password>\r\n");
                }
                else
                {
                    ValidRCONClients.Add(args[0], AuthManager.GetEncryptedText(authType, args[1]));
                    returnString.Append("Added user '" + args[0] + "' with password encoded via auth method " + authType + ".\r\n");
                    LogManager.Log("RCON - " + currentClient.Username + " added RCON user " + args[0]);
                }

                return(returnString.ToString());
            }, true);
            AddRCONCommand("rcon-delete-user", "[SU] Deletes a given RCON user", (args) =>
            {
                StringBuilder returnString = new StringBuilder();

                if (currentClient.Username[0] != '@')
                {
                    returnString.Append("Error: You need to be a superuser to delete users.\r\n");
                }
                else if (args.Length != 1)
                {
                    returnString.Append("Invalid number of parameters. Expected 1.\r\n");
                    returnString.Append("Usage: rcon-delete-user <username>\r\n");
                }
                else if (ValidRCONClients.ContainsKey(args[0]))
                {
                    ValidRCONClients.Remove(args[0]);
                    returnString.Append("Deleted user '" + args[0] + "'.\r\n");
                    LogManager.Log("RCON - " + currentClient.Username + " deleted RCON user " + args[0]);
                }
                else
                {
                    returnString.Append("User '" + args[0] + "' not found.\r\n");
                }

                return(returnString.ToString());
            });

            AddRCONCommand("rcon-list-users", "Lists all registered RCON users", (args) =>
            {
                StringBuilder returnString = new StringBuilder();

                returnString.Append("Registered RCON users:\r\n");
                foreach (KeyValuePair <string, string> kvp in ValidRCONClients)
                {
                    returnString.Append(kvp.Key + "\r\n");
                }

                return(returnString.ToString());
            });
            AddRCONCommand("rcon-change-password", "Changes the current user's password", (args) =>
            {
                StringBuilder returnString = new StringBuilder();

                if (args.Length != 2)
                {
                    returnString.Append("Invalid number of parameters. Expected 2\r\n");
                    returnString.Append("Usage: rcon-change-password <current password> <new password>\r\n");
                }
                else
                {
                    if (AuthManager.CheckPlaintextCredentials(authType, args[0], ValidRCONClients[currentClient.Username]))
                    {
                        ValidRCONClients[currentClient.Username] = AuthManager.GetEncryptedText(authType, args[1]);
                        returnString.Append("Password successfully changed.\r\n");
                    }
                    else
                    {
                        returnString.Append("Current password was incorrect. Password not changed.\r\n");
                    }
                }

                return(returnString.ToString());
            }, true);
            return(true);
        }