private static void UpdateWhoIsOnlineFile() { try { var SB = new StringBuilder(); SB.AppendLine("Node,RemoteIP,User,Status"); lock (_ListLock) { // Get status from each node for (int Node = Config.Instance.FirstNode; Node <= Config.Instance.LastNode; Node++) { if (_ClientThreads[Node] == null) { SB.AppendLine($"{Node}\t\t\tWaiting for caller"); } else { SB.AppendLine($"{Node}\t{_ClientThreads[Node].IPAddress}\t{_ClientThreads[Node].Alias}\t{_ClientThreads[Node].Status}"); } } } string WhoIsOnlineFilename = StringUtils.PathCombine(ProcessUtils.StartupPath, "whoisonline.txt"); FileUtils.FileWriteAllText(WhoIsOnlineFilename, SB.ToString()); } catch (Exception ex) { RMLog.Exception(ex, "Unable to update whoisonline.txt"); } }
private bool Initialize() { // Create temporary environment file FileUtils.FileWriteAllText(EnvFile, Environment.GetEnvironmentVariable("COMSPEC") + " /C " + _Command + " " + _Parameters); // Create a hungup event for when user drops carrier HungUpEvent = NativeMethods.CreateEvent(IntPtr.Zero, true, false, "sbbsexec_hungup" + _ClientThread.NodeInfo.Node.ToString()); if (HungUpEvent == IntPtr.Zero) { RMLog.Error("CreateEvent() failed to create HungUpEvent: " + Marshal.GetLastWin32Error().ToString()); return(false); } // Create a hangup event (for when the door requests to drop DTR) HangUpEvent = NativeMethods.CreateEvent(IntPtr.Zero, true, false, "sbbsexec_hangup" + _ClientThread.NodeInfo.Node.ToString()); if (HangUpEvent == IntPtr.Zero) { RMLog.Error("CreateEvent() failed to create HangUpEvent: " + Marshal.GetLastWin32Error().ToString()); return(false); } // Create a read mail slot ReadSlot = NativeMethods.CreateMailslot("\\\\.\\mailslot\\sbbsexec\\rd" + _ClientThread.NodeInfo.Node.ToString(), XTRN_IO_BUF_LEN, 0, IntPtr.Zero); if (ReadSlot == IntPtr.Zero) { RMLog.Error("CreateMailslot() failed to create ReadSlot: " + Marshal.GetLastWin32Error().ToString()); return(false); } return(true); }
public void Start() { RMLog.Handler += RMLog_Handler; RMLog.Info("fTelnetProxy Starting Up"); Config.Default.Load(); ParseEnvironmentVariables(); ParseCommandLineArgs(); if ((Config.Default.CertificateFilename != "") && File.Exists(Config.Default.CertificateFilename)) { try { Config.Default.Certificate = new X509Certificate2(Config.Default.CertificateFilename, Config.Default.CertificatePassword); } catch (Exception ex) { RMLog.Exception(ex, "--Error loading cert file"); } } try { RMLog.Info("Starting WebSocket proxy thread"); _WebSocketServer = new WebSocketServerThread("0.0.0.0", Config.Default.ListenPort); _WebSocketServer.Start(); } catch (Exception ex) { RMLog.Exception(ex, "Failed to start WebSocket proxy thread"); Environment.Exit(1); } }
public void DropRoot(string targetUser) { // Don't do anything if no user was given, or if root was given as the target user if (string.IsNullOrWhiteSpace(targetUser) || targetUser.Equals("root", StringComparison.OrdinalIgnoreCase)) { return; } // If we're on a Unix machine, and running as root, drop privilege to the target user if (OSUtils.IsUnix && (WindowsIdentity.GetCurrent().Token == IntPtr.Zero)) { RMLog.Info($"Switching user from 'root' to '{targetUser}'"); using (WindowsIdentity wiTarget = new WindowsIdentity(targetUser)) { wiTarget.Impersonate(); using (WindowsIdentity wiCurrent = WindowsIdentity.GetCurrent()) { if (wiCurrent.Name != targetUser) { throw new ArgumentOutOfRangeException(nameof(targetUser), "Requested user account '" + targetUser + "' does not exist"); } } } } }
protected override void Execute() { while (!_Stop) { string IgnoredIPsFileName = StringUtils.PathCombine(ProcessUtils.StartupPath, "config", "ignored-ips.txt"); string CombinedFileName = StringUtils.PathCombine(ProcessUtils.StartupPath, "config", "ignored-ips-combined.txt"); string StatusCakeFileName = StringUtils.PathCombine(ProcessUtils.StartupPath, "config", "ignored-ips-statuscake.txt"); string UptimeRobotFileName = StringUtils.PathCombine(ProcessUtils.StartupPath, "config", "ignored-ips-uptimerobot.txt"); // Get the list of servers from StatusCake try { string IPs = WebUtils.HttpGet("https://www.statuscake.com/API/Locations/txt"); IPs = IPs.Replace("\r\n", "CRLF").Replace("\n", "\r\n").Replace("CRLF", "\r\n"); FileUtils.FileWriteAllText(StatusCakeFileName, IPs); } catch (Exception ex) { RMLog.Exception(ex, "Unable to download https://www.statuscake.com/API/Locations/txt"); } // Get the list of servers from UptimeRobot try { string Locations = WebUtils.HttpGet("http://uptimerobot.com/locations"); var Matches = Regex.Matches(Locations, @"[<]li[>](\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})"); List <string> IPs = new List <string>(); foreach (Match M in Matches) { IPs.Add(M.Groups[1].Value); } FileUtils.FileWriteAllText(UptimeRobotFileName, string.Join("\r\n", IPs.ToArray())); } catch (Exception ex) { RMLog.Exception(ex, "Unable to download https://www.statuscake.com/API/Locations/txt"); } // Combine the lists try { FileUtils.FileWriteAllText(CombinedFileName, ""); if (File.Exists(IgnoredIPsFileName)) { FileUtils.FileAppendAllText(CombinedFileName, FileUtils.FileReadAllText(IgnoredIPsFileName)); } if (File.Exists(StatusCakeFileName)) { FileUtils.FileAppendAllText(CombinedFileName, FileUtils.FileReadAllText(StatusCakeFileName)); } if (File.Exists(UptimeRobotFileName)) { FileUtils.FileAppendAllText(CombinedFileName, FileUtils.FileReadAllText(UptimeRobotFileName)); } } catch (Exception ex) { RMLog.Exception(ex, "Unable to combine Ignored IPs lists"); } // Wait for one hour before updating again if (!_Stop && (_StopEvent != null)) { _StopEvent.WaitOne(3600000); } } }
public void Run() { try { // Clear the buffers and reset the screen _ClientThread.NodeInfo.Connection.ReadString(); _ClientThread.ClrScr(); // Create the node directory and drop files CreateNodeDirectory(); // Determine how to run the door if (((_ClientThread.NodeInfo.Door.Platform == OSUtils.Platform.Linux) && OSUtils.IsUnix) || ((_ClientThread.NodeInfo.Door.Platform == OSUtils.Platform.Windows) && OSUtils.IsWindows)) { RunDoorNative(TranslateCLS(_ClientThread.NodeInfo.Door.Command), TranslateCLS(_ClientThread.NodeInfo.Door.Parameters)); } else if ((_ClientThread.NodeInfo.Door.Platform == OSUtils.Platform.DOS) && OSUtils.IsWindows) { if (ProcessUtils.Is64BitOperatingSystem) { if (Helpers.IsDOSBoxInstalled()) { RunDoorDOSBox(TranslateCLS(_ClientThread.NodeInfo.Door.Command), TranslateCLS(_ClientThread.NodeInfo.Door.Parameters)); } else { RMLog.Error("DOS doors are not supported on 64bit Windows (unless you install DOSBox 0.73)"); } } else { (new RunDoorSBBSEXEC(_ClientThread)).Run(TranslateCLS(_ClientThread.NodeInfo.Door.Command), TranslateCLS(_ClientThread.NodeInfo.Door.Parameters), _ClientThread.NodeInfo.Door.ForceQuitDelay); } } else if ((_ClientThread.NodeInfo.Door.Platform == OSUtils.Platform.DOS) && OSUtils.IsUnix) { if (Helpers.IsDOSEMUInstalled()) { RunDoorDOSEMU(TranslateCLS(_ClientThread.NodeInfo.Door.Command), TranslateCLS(_ClientThread.NodeInfo.Door.Parameters)); } else { RMLog.Error("DOS doors are not supported on Linux (unless you install DOSEMU)"); } } else { RMLog.Error("Unsure how to run door on current platform"); } } catch (Exception ex) { RMLog.Exception(ex, "Error while running door '" + _ClientThread.NodeInfo.Door.Name + "'"); } finally { // Clean up try { _ClientThread.ClrScr(); _ClientThread.NodeInfo.Connection.SetBlocking(true); // In case native door disabled blocking sockets DeleteNodeDirectory(); } catch { /* Ignore */ } } }
protected override void Execute() { using (_Server = new WebSocketConnection()) { if (_Server.Listen(_Address, _Port)) { RaiseListeningEvent(); while (!_Stop) { try { // Accept an incoming connection if (_Server.CanAccept(1000)) // 1 second { Socket NewSocket = _Server.Accept(); if (NewSocket != null) { lock (_ClientThreadsLock) { WebSocketClientThread ClientThread = new WebSocketClientThread(NewSocket, ++_ClientThreadCounter); ClientThread.FinishEvent += ClientThread_FinishEvent; _ClientThreads.Add(ClientThread); RMLog.Info(_ClientThreads.Count.ToString() + " active connections"); ClientThread.Start(); } } } } catch (Exception ex) { RMLog.Exception(ex, "Unable to accept new websocket connection"); } } // Stop client threads int ClientThreadCount = 0; lock (_ClientThreadsLock) { foreach (var ClientThread in _ClientThreads) { if (ClientThread != null) { ClientThread.Stop(); } } ClientThreadCount = _ClientThreads.Count; } // Wait for client threads while (ClientThreadCount > 0) { lock (_ClientThreadsLock) { ClientThreadCount = _ClientThreads.Count; } Thread.Sleep(100); } } else { RMLog.Error("WebSocket Server Thread: Unable to listen on " + _Address + ":" + _Port); } } }
public static void StartThread() { RMLog.Info("Starting Ignored IPs Thread"); try { // Create Ignored IPs Thread and Thread objects _IgnoredIPsThread = new IgnoredIPsThread(); _IgnoredIPsThread.Start(); } catch (Exception ex) { RMLog.Exception(ex, "Error in IgnoredIPsThread::StartThread()"); } }
public void Run(string door) { _ClientThread.NodeInfo.Door = new DoorInfo(door); if (_ClientThread.NodeInfo.Door.Loaded) { Run(); } else { RMLog.Error("Unable to find door: '" + door + "'"); } }
private void StopProcess() { try { // Terminate process if it hasn't closed yet if ((P != null) && !P.HasExited) { RMLog.Error("Door still running, performing a force quit"); P.Kill(); } } catch (Exception ex) { RMLog.Exception(ex, "Unable to perform force quit"); } }
public void Start() { if (_Status == GameSrvStatus.Paused) { // If we're paused, call Pause() again to un-pause Pause(); } else if (_Status == GameSrvStatus.Stopped) { // Clean up the files not needed by this platform Helpers.CleanUpFiles(); // Check for 3rd party software Helpers.CheckFor3rdPartySoftware(); // Load the Global settings Config.Instance.Init(); // Start the node manager NodeManager.Start(); // Start the server threads ServerThreadManager.StartThreads(); // Start the ignored ips thread IgnoredIPsThread.StartThread(); // Start the timed events thread TimedEventsThread.StartThread(); // Drop root, if necessary try { Helpers.DropRoot(Config.Instance.UnixUser); } catch (ArgumentOutOfRangeException aoorex) { RMLog.Exception(aoorex, "Unable to drop from root to '" + Config.Instance.UnixUser + "'"); // Undo previous actions on error TimedEventsThread.StopThread(); IgnoredIPsThread.StopThread(); ServerThreadManager.StopThreads(); NodeManager.Stop(); // If we get here, we failed to go online UpdateStatus(GameSrvStatus.Stopped); return; } // If we get here, we're online UpdateStatus(GameSrvStatus.Started); } }
public static void StopThreads() { RMLog.Info("Stopping Server Threads"); try { foreach (KeyValuePair <int, ServerThread> KV in _ServerThreads) { KV.Value.Stop(); } _ServerThreads.Clear(); } catch (Exception ex) { RMLog.Exception(ex, "Error in ServerThreadManager::StopThreads()"); } }
protected override void Execute() { // Load the events into memory var EventNames = TimedEvent.GetEventNames(); foreach (var EventName in EventNames) { _TimedEvents.Add(new TimedEvent(EventName)); } while (!_Stop) { // Get the current day and time, which we'll compare to the list of events in memory string CurrentDay = DateTime.Now.DayOfWeek.ToString(); string CurrentTime = DateTime.Now.ToString("HH:mm"); // Get matching events var EventsToRun = _TimedEvents.Where(x => x.Days.Contains(CurrentDay) && x.Time == CurrentTime); foreach (var EventToRun in EventsToRun) { // Check if we need to go offline for this event if (EventToRun.GoOffline) { RMLog.Info("Going offline to run event '" + EventToRun.Name + "'"); _ActiveOfflineEvents += 1; // TODOX Raise event to take GameSrv offline } else { RMLog.Info("Running event '" + EventToRun.Name + "'"); } // Execute the event ProcessStartInfo PSI = new ProcessStartInfo(EventToRun.Command) { WindowStyle = EventToRun.WindowStyle, WorkingDirectory = ProcessUtils.StartupPath, }; var P = RMProcess.Start(PSI); // TODOX Need to get notification of the event completing so we can go back online } // Wait until the next minute rolls around to try again if (!_Stop && (_StopEvent != null)) { _StopEvent.WaitOne((61 - DateTime.Now.Second) * 1000); } } }
public static void StopThread() { RMLog.Info("Stopping Ignored IPs Thread"); if (_IgnoredIPsThread != null) { try { _IgnoredIPsThread.Stop(); _IgnoredIPsThread.Dispose(); _IgnoredIPsThread = null; } catch (Exception ex) { RMLog.Exception(ex, "Error in IgnoredIPsThread::StopThread()"); } } }
public static void StopThread() { RMLog.Info("Stopping Timed Events Thread"); if (_TimedEventsThread != null) { try { _TimedEventsThread.Stop(); _TimedEventsThread.Dispose(); _TimedEventsThread = null; } catch (Exception ex) { RMLog.Exception(ex, "Error in TimedEventsThread::StartThread()"); } } }
private void FlushLog() { lock (_LogLock) { // Flush log to disk if (_Log.Count > 0) { try { FileUtils.FileAppendAllText(StringUtils.PathCombine(ProcessUtils.StartupPath, "logs", "gamesrv.log"), string.Join(Environment.NewLine, _Log.ToArray()) + Environment.NewLine); _Log.Clear(); } catch (Exception ex) { RMLog.Exception(ex, "Unable to update gamesrv.log"); } } } }
public void DisplayActiveConnections() { lock (_ClientThreadsLock) { foreach (var ClientThread in _ClientThreads) { if (ClientThread != null) { try { ClientThread.DisplayConnectionInformation(); } catch (Exception ex) { RMLog.Exception(ex, "Error listing client thread details"); } } } } }
public static void Run(ClientThread clientThread) { if (clientThread == null) { throw new ArgumentNullException("clientThread"); } // Loop through the options, and run the ones we allow here bool ExitFor = false; string[] Processes = LogOnProcess.GetProcesses(); for (int i = 0; i < Processes.Length; i++) { try { LogOnProcess LP = new LogOnProcess(Processes[i]); if ((LP.Loaded) && (!clientThread.QuitThread())) { switch (LP.Action) { case Action.Disconnect: case Action.DisplayFile: case Action.DisplayFileMore: case Action.DisplayFilePause: case Action.MainMenu: case Action.Pause: case Action.RunDoor: MenuOption MO = new MenuOption("", '\0') { Action = LP.Action, Name = LP.Name, Parameters = LP.Parameters, RequiredAccess = LP.RequiredAccess, }; ExitFor = clientThread.HandleMenuOption(MO); break; } if (ExitFor) { break; } } } catch (Exception ex) { // If there's something wrong with the ini entry (Action is invalid for example), this will throw a System.ArgumentException error, so we just ignore that menu item RMLog.Exception(ex, "Error during logon process '" + Processes[i] + "'"); } } }
public void Stop() { _Stopping = true; RMLog.Info("fTelnetProxy shutting down"); if (_WebSocketServer != null) { RMLog.Info("Stopping WebSocket proxy thread"); _WebSocketServer.Stop(); _WebSocketServer.WaitFor(); } RMLog.Info("fTelnetProxy terminated"); FileUtils.FileAppendAllText(_LogFilename, Environment.NewLine + Environment.NewLine); }
public static bool IsRLoginIP(string ip) { try { string RLoginIPsFileName = StringUtils.PathCombine(ProcessUtils.StartupPath, "config", "rlogin-ips.txt"); if (File.Exists(RLoginIPsFileName)) { return(FileContainsIP(RLoginIPsFileName, ip)); } else { // No file means any RLogin connection allowed return(true); } } catch (Exception ex) { RMLog.Exception(ex, "Unable to validate client IP against ignored-ips.txt"); return(true); // Give them the benefit of the doubt on error } }
public static bool IsBannedIP(string ip) { try { string BannedIPsFileName = StringUtils.PathCombine(ProcessUtils.StartupPath, "config", "banned-ips.txt"); if (File.Exists(BannedIPsFileName)) { return(FileContainsIP(BannedIPsFileName, ip)); } else { // No file means not banned return(false); } } catch (Exception ex) { RMLog.Exception(ex, "Unable to validate client IP against banned-ips.txt"); return(false); // Give them the benefit of the doubt on error } }
protected override void HandleNewConnection(TcpConnection newConnection) { if (newConnection == null) { throw new ArgumentNullException("newConnection"); } RLoginConnection TypedConnection = new RLoginConnection(); if (TypedConnection.Open(newConnection.GetSocket())) { ClientThread NewClientThread = new ClientThread(TypedConnection, _ConnectionType); NewClientThread.Start(); } else { RMLog.Info("Timeout waiting for RLogin header"); TypedConnection.Close(); } }
public void Init() { // Settings are actually loaded already, just checking that the node numbers are sane here RMLog.Info("Loading Global Settings"); // Flip nodes, if necessary if (FirstNode > LastNode) { int Temp = FirstNode; FirstNode = LastNode; LastNode = Temp; } // Save default config file, if necessary if (!Loaded) { RMLog.Info("Unable To Load Global Settings...Will Use Defaults"); Save(); } }
public static void CheckFor3rdPartySoftware() { if (OSUtils.IsWinNT) { if (ProcessUtils.Is64BitOperatingSystem) { if (!Helpers.IsDOSBoxInstalled()) { RMLog.Error("PLEASE INSTALL DOSBOX 0.73 IF YOU PLAN ON RUNNING DOS DOORS USING DOSBOX"); } } else { if (!File.Exists(StringUtils.PathCombine(Environment.SystemDirectory, "sbbsexec.dll"))) { RMLog.Error("PLEASE COPY SBBSEXEC.DLL TO " + StringUtils.PathCombine(Environment.SystemDirectory, "sbbsexec.dll").ToUpper() + " IF YOU PLAN ON RUNNING DOS DOORS USING THE EMBEDDED SYNCHRONET FOSSIL"); } } } }
public static void StartThreads() { if ((Config.Instance.RLoginServerPort > 0) || (Config.Instance.TelnetServerPort > 0)) { RMLog.Info("Starting Server Threads"); try { _ServerThreads.Clear(); if (Config.Instance.RLoginServerPort > 0) { // Create Server Thread and add to collection _ServerThreads.Add(Config.Instance.RLoginServerPort, new RLoginServerThread()); } if (Config.Instance.TelnetServerPort > 0) { // Create Server Thread and add to collection _ServerThreads.Add(Config.Instance.TelnetServerPort, new TelnetServerThread()); } if (Config.Instance.WebSocketServerPort > 0) { // Create Server Thread and add to collection _ServerThreads.Add(Config.Instance.WebSocketServerPort, new WebSocketServerThread()); } // Now actually start the server threads foreach (var KVP in _ServerThreads) { KVP.Value.Start(); } } catch (Exception ex) { RMLog.Exception(ex, "Error in ServerThreadManager::StartThreads()"); } } else { RMLog.Error("Must specify a port for RLogin and/or Telnet servers"); } }
protected override void Execute() { while (!_Stop) { using (TcpConnection Connection = new TcpConnection()) { if (Connection.Listen(_LocalAddress, _LocalPort)) { while (!_Stop) { // Accept an incoming connection if (Connection.CanAccept(1000)) // 1 second { try { TcpConnection NewConnection = Connection.AcceptTCP(); if (NewConnection != null) { // TODOX Add check for flash socket policy request by doing a peek with a 1 second timeout or something // If peeked character is < then peek another character to see if it's the flash request string HandleNewConnection(NewConnection); } } catch (Exception ex) { RMLog.Exception(ex, "Error in ServerThread::Execute()"); } } } } else { RMLog.Error($"{_ConnectionType} Server Thread unable to listen on {_LocalAddress}:{_LocalPort}. Retry in 15 seconds."); for (int i = 1; i <= 15; i++) { Thread.Sleep(1000); if (_Stop) { break; } } } } } }
protected override void HandleNewConnection(TcpConnection newConnection) { if (newConnection == null) { throw new ArgumentNullException("newConnection"); } WebSocketConnection TypedConnection = new WebSocketConnection(); if (TypedConnection.Open(newConnection.GetSocket())) { // TODOX Start a proxy thread instead of a clientthread ClientThread NewClientThread = new ClientThread(TypedConnection, _ConnectionType); NewClientThread.Start(); } else { RMLog.Info("No carrier detected (probably a portscanner)"); TypedConnection.Close(); } }
protected override void HandleNewConnection(TcpConnection newConnection) { if (newConnection == null) { throw new ArgumentNullException("newConnection"); } TelnetConnection TypedConnection = new TelnetConnection(); if (TypedConnection.Open(newConnection.GetSocket())) { ClientThread NewClientThread = new ClientThread(TypedConnection, _ConnectionType); NewClientThread.Start(); } else { // TODOX Duplicated code. Maybe add method to base class and call it? RMLog.Info("No carrier detected (probably a portscanner)"); TypedConnection.Close(); } }
void ClientThread_FinishEvent(object sender, EventArgs e) { if (sender is WebSocketClientThread) { lock (_ClientThreadsLock) { if (_ClientThreads.Contains((WebSocketClientThread)sender)) { _ClientThreads.Remove((WebSocketClientThread)sender); RMLog.Info(_ClientThreads.Count.ToString() + " active connections"); } else { RMLog.Error("ClientThread_FinishEvent did not find sender in _ClientThreads (sender=" + sender.ToString() + ")"); } } } else { RMLog.Error("ClientThread_FinishEvent's sender is not a WebSocketClientThread (sender=" + sender.ToString() + ")"); } }
private void UpdateStatus(GameSrvStatus newStatus) { // Record the new status _Status = newStatus; StatusChangeEvent?.Invoke(this, new StatusEventArgs(newStatus)); switch (newStatus) { case GameSrvStatus.Paused: RMLog.Info("Server(s) are paused"); break; case GameSrvStatus.Pausing: RMLog.Info("Server(s) are pausing..."); break; case GameSrvStatus.Resuming: RMLog.Info("Server(s) are resuming..."); break; case GameSrvStatus.Started: RMLog.Info("Server(s) have started"); break; case GameSrvStatus.Starting: RMLog.Info("Server(s) are starting..."); break; case GameSrvStatus.Stopped: RMLog.Info("Server(s) have stopped"); break; case GameSrvStatus.Stopping: RMLog.Info("Server(s) are stopping..."); break; } }