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"); } }
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); } }
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()"); } }
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()"); } }
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 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 } }
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 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; } } } } } }
public static bool IsIgnoredIP(string ip) { try { if (Helpers.IsTempIgnoredIP(ip)) { return(true); } string IgnoredIPsFileName = StringUtils.PathCombine(ProcessUtils.StartupPath, "config", "ignored-ips-combined.txt"); if (File.Exists(IgnoredIPsFileName)) { return(FileContainsIP(IgnoredIPsFileName, ip)); } else { // No file means not ignored return(false); } } catch (Exception ex) { RMLog.Exception(ex, "Unable to validate client IP against ignored-ips.txt"); return(false); // Give them the benefit of the doubt on error } }
public void Start() { RMLog.Handler += RMLog_Handler; RMLog.Info("fTelnetProxy Starting Up"); Config.Default.Load(); ParseEnvironmentVariables(); ParseCommandLineArgs(); if (!string.IsNullOrWhiteSpace(Config.Default.CertificateFilename) && File.Exists(Config.Default.CertificateFilename)) { try { if (OSUtils.IsUnix) { Environment.SetEnvironmentVariable("MONO_TLS_PROVIDER", "btls"); } // Try loading the certificate to ensure there are no problems var testCert = Config.Default.Certificate; } catch (Exception ex) { // Loading cert failed, so wipe the filename to ensure we don't try loading it again later RMLog.Exception(ex, "--Error loading cert file"); Config.Default.CertificateFilename = null; } } try { RMLog.Info("Starting WebSocket proxy thread"); _WebSocketServer = new WebSocketServerThread("0.0.0.0", Config.Default.ListenPort); _WebSocketServer.ListeningEvent += WebSocketServer_ListeningEvent; _WebSocketServer.Start(); } catch (Exception ex) { RMLog.Exception(ex, "Failed to start WebSocket proxy thread"); Environment.Exit(1); } }
public static bool IsBannedUser(string alias) { if (alias == null) { throw new ArgumentNullException("alias"); } try { alias = alias.Trim().ToLower(); if (string.IsNullOrEmpty(alias)) { return(false); // Don't ban for blank inputs } string BannedUsersFileName = StringUtils.PathCombine(ProcessUtils.StartupPath, "config", "banned-users.txt"); if (File.Exists(BannedUsersFileName)) { string[] BannedUsers = FileUtils.FileReadAllLines(BannedUsersFileName); foreach (string BannedUser in BannedUsers) { if (BannedUser.StartsWith(";")) { continue; } if (BannedUser.Trim().ToLower() == alias) { return(true); } } } } catch (Exception ex) { RMLog.Exception(ex, "Unable to validate alias against banned-users.txt"); } // If we get here, it's an OK name return(false); }
protected override void Execute() { try { // Handle non-proxy connections using (WebSocketConnection UserConnection = new WebSocketConnection(true, Config.Default.Certificate)) { if (UserConnection.Open(_Socket)) { _RemoteIP = UserConnection.GetRemoteIP(); RMLog.Debug("{" + _ConnectionId.ToString() + "} Opened connection from " + UserConnection.GetRemoteIP() + ":" + UserConnection.GetRemotePort()); if (UserConnection.Header["Path"] == "/ping") { // Handle ping requests (from proxy.ftelnet.ca most likely) string Ping = UserConnection.ReadLn(1000); if (UserConnection.ReadTimedOut) { RMLog.Debug("Answering a /ping (no time received) from " + UserConnection.GetRemoteIP() + ":" + UserConnection.GetRemotePort()); } else { RMLog.Debug("Answering a /ping (" + Ping + ") from " + UserConnection.GetRemoteIP() + ":" + UserConnection.GetRemotePort()); UserConnection.Write(Ping); } return; } } else { if (UserConnection.FlashPolicyFileRequest) { RMLog.Info("{" + _ConnectionId.ToString() + "} Answered flash policy file request from " + UserConnection.GetRemoteIP() + ":" + UserConnection.GetRemotePort().ToString()); } else { RMLog.Debug("{" + _ConnectionId.ToString() + "} Invalid WebSocket connection from " + UserConnection.GetRemoteIP() + ":" + UserConnection.GetRemotePort().ToString()); } return; } // If we get here it's a proxy connection, so handle it RMLog.Info("{" + _ConnectionId.ToString() + "} Connection accepted from " + UserConnection.GetRemoteIP() + ":" + UserConnection.GetRemotePort()); string MessageText = string.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\r\n", DateTime.Now.ToString(), UserConnection.GetRemoteIP(), UserConnection.GetRemotePort(), UserConnection.Header["Path"], UserConnection.Protocol, UserConnection.SubProtocol); FileUtils.FileAppendAllText(Path.Combine(ProcessUtils.StartupPath, "fTelnetProxy-Connections.log"), MessageText, Encoding.ASCII); // Defaults for redirect location _Hostname = Config.Default.TargetHostname; _Port = Config.Default.TargetPort; // Check if user is requesting a custom target if (UserConnection.Header["Path"] != "/") { bool CanRelay = false; // Extract the requested host and port string[] HostAndPort = UserConnection.Header["Path"].Split('/'); if ((HostAndPort.Length == 3) && (int.TryParse(HostAndPort[2], out _Port))) { _Hostname = HostAndPort[1]; if (Config.Default.TargetHostname.ToLower().Trim() == _Hostname.ToLower().Trim()) { // User is requesting the target defined by the proxy admin, so check if it's to an allowed port CanRelay = ((_Port > 0) && (_Port == Config.Default.TargetPort) || (_Port == Config.Default.RLoginPort)); } else if (!string.IsNullOrEmpty(Config.Default.RelayFilename)) { // proxy admin has relaying enabled, so check against the relay.cfg file try { // Read relay file if (File.Exists(Config.Default.RelayFilename)) { string[] AllowedHosts = File.ReadAllLines(Config.Default.RelayFilename); if (AllowedHosts.Length > 0) { // Check for a whitelisted port string[] AllowedPorts = AllowedHosts[0].Split(','); foreach (string AllowedPort in AllowedPorts) { if (AllowedPort == _Port.ToString()) { CanRelay = true; break; } } // Not a whitelisted port, check for a whitelisted host if (!CanRelay) { string RequestedHostPort = _Hostname.ToLower() + ":" + _Port.ToString(); foreach (string AllowedHost in AllowedHosts) { if (AllowedHost.Trim().ToLower() == RequestedHostPort) { CanRelay = true; break; } } } } else { RMLog.Error("{" + _ConnectionId.ToString() + "} Relay file is empty: '" + Config.Default.RelayFilename + "'"); } } else { RMLog.Error("{" + _ConnectionId.ToString() + "} Relay file does not exist: '" + Config.Default.RelayFilename + "'"); } } catch (Exception ex) { RMLog.Exception(ex, "{" + _ConnectionId.ToString() + "} Error reading relay file: '" + Config.Default.RelayFilename + "'"); } } } if (!CanRelay) { RMLog.Info("{" + _ConnectionId.ToString() + "} Rejecting request for " + _Hostname + ":" + _Port.ToString()); UserConnection.WriteLn("Sorry, for security reasons this proxy won't connect to " + _Hostname + ":" + _Port.ToString()); UserConnection.WriteLn("unless you contact me via the contact form on www.fTelnet.ca. Just let me"); UserConnection.WriteLn("know the hostname and port you're trying to connect to, and I'll add it to"); UserConnection.WriteLn("the whitelist for you."); Thread.Sleep(2500); return; } } // Try to connect to the desired Host and Port UserConnection.Write(Ansi.ClrScr() + "Connecting to " + _Hostname + ":" + _Port.ToString() + "..."); using (TcpConnection ServerConnection = new TcpConnection()) { if (ServerConnection.Connect(_Hostname, _Port)) { RMLog.Info("{" + _ConnectionId.ToString() + "} Connected to " + _Hostname + ":" + _Port.ToString()); UserConnection.WriteLn("connected!"); // Repeatedly move data around until a connection is closed (or a stop is requested) bool DoSleep = true; while (!_Stop && UserConnection.Connected && ServerConnection.Connected) { DoSleep = true; if (UserConnection.CanRead()) { ServerConnection.WriteBytes(UserConnection.ReadBytes()); _DateLastTX = DateTime.Now; DoSleep = false; } if (ServerConnection.CanRead()) { UserConnection.WriteBytes(ServerConnection.ReadBytes(1024)); // 1k at a time to allow non-stop screens to be aborted by user input _DateLastRX = DateTime.Now; DoSleep = false; } if (DoSleep) { Thread.Sleep(1); } // Check if we should abort due to idle times // TODOX Allow to be customized if (SecondsSinceLastRX > 600) { // 10 minutes of no server activity RMLog.Info("{" + _ConnectionId.ToString() + "} Disconnecting after 10 minutes of no activity from server"); UserConnection.Write(Ansi.GotoXY(1, 1) + Ansi.CursorDown(255) + "\r\nDisconnecting after 10 minutes of no activity from server..."); Thread.Sleep(2500); break; } else if (SecondsSinceLastTX > 600) { // 10 minutes of no user activity RMLog.Info("{" + _ConnectionId.ToString() + "} Disconnecting after 10 minutes of no activity from user"); UserConnection.Write(Ansi.GotoXY(1, 1) + Ansi.CursorDown(255) + "\r\nDisconnecting after 10 minutes of no activity from user..."); Thread.Sleep(2500); break; } else if (SecondsSinceConnecting > 21600) { // 6 hours since connecting RMLog.Info("{" + _ConnectionId.ToString() + "} Disconnecting after 6 hours"); UserConnection.Write(Ansi.GotoXY(1, 1) + Ansi.CursorDown(255) + "\r\nDisconnecting after 6 hours..."); Thread.Sleep(2500); break; } } // Check why we exited the loop if (_Stop) { RMLog.Info("{" + _ConnectionId.ToString() + "} Stop requested"); UserConnection.Write(Ansi.GotoXY(1, 1) + Ansi.CursorDown(255) + "\r\nProxy server shutting down..."); Thread.Sleep(2500); } else if (!UserConnection.Connected) { RMLog.Info("{" + _ConnectionId.ToString() + "} Client closed connection"); } else if (!ServerConnection.Connected) { RMLog.Info("{" + _ConnectionId.ToString() + "} Server closed connection"); UserConnection.Write(Ansi.GotoXY(1, 1) + Ansi.CursorDown(255) + "\r\nServer closed connection..."); Thread.Sleep(2500); } } else { RMLog.Info("{" + _ConnectionId.ToString() + "} Unable to connect to " + _Hostname + ":" + _Port.ToString()); UserConnection.WriteLn("unable to connect!"); Thread.Sleep(2500); } } // Display info about the connection we're closing DisplayConnectionInformation(); } } catch (Exception ex) { RMLog.Exception(ex, "{" + _ConnectionId.ToString() + "} Exception in client thread"); } }
/// <summary> /// The main entry point for the application. /// </summary> static void Main(string[] args) { try { // Check for service mode or console mode if (Environment.UserInteractive || OSUtils.IsUnix) { // Console mode, check for arguments if (args.Length > 0) { try { // Check entire parameter string for service install or uninstall request string ParameterString = string.Concat(args).TrimStart('/').TrimStart('-'); switch (ParameterString) { case "install": Console.WriteLine("Installing service..."); ManagedInstallerClass.InstallHelper(new string[] { Assembly.GetExecutingAssembly().Location }); Console.WriteLine("Service installed successfully!"); return; case "uninstall": Console.WriteLine("Uninstalling service..."); ManagedInstallerClass.InstallHelper(new string[] { "/u", Assembly.GetExecutingAssembly().Location }); Console.WriteLine("Service uninstalled successfully!"); return; } } catch (Exception ex) { Console.WriteLine("Error handling service request: " + ex.Message); return; } } // If we get here, we're running as console app using (var fTelnetProxy = new fTelnetProxy()) { fTelnetProxy.Start(); Console.WriteLine("Press A for Active Connections or Q to Quit..."); while (true) { if (Console.KeyAvailable) { var Ch = Console.ReadKey(true).Key; if (Ch == ConsoleKey.A) { fTelnetProxy.DisplayActiveConnections(); } else if (Ch == ConsoleKey.Q) { break; } else { Console.WriteLine(fTelnetProxy.ClientConnectionCount.ToString() + " active connections"); Console.WriteLine("Press A for Active Connections or Q to Quit..."); } } else { Thread.Sleep(1000); } } Console.WriteLine("Exiting..."); fTelnetProxy.Stop(); } } else { // Service mode using (var fTelnetProxyService = new Service()) { ServiceBase.Run(fTelnetProxyService); } } } catch (Exception ex) { RMLog.Exception(ex, "Unhandled exception in main program loop"); } }
protected override void Execute() { try { // Handle non-proxy connections using (WebSocketConnection NewConnection = new WebSocketConnection(true, Config.Default.Certificate)) { if (NewConnection.Open(_Socket)) { RMLog.Debug("{" + _ConnectionId.ToString() + "} Opened connection from " + NewConnection.GetRemoteIP() + ":" + NewConnection.GetRemotePort()); if (NewConnection.Header["Path"] == "/ping") { // Handle ping requests (from proxy.ftelnet.ca most likely) string Ping = NewConnection.ReadLn(1000); if (NewConnection.ReadTimedOut) { RMLog.Debug("Answering a /ping (no time received) from " + NewConnection.GetRemoteIP() + ":" + NewConnection.GetRemotePort()); } else { RMLog.Debug("Answering a /ping (" + Ping + ") from " + NewConnection.GetRemoteIP() + ":" + NewConnection.GetRemotePort()); NewConnection.Write(Ping); } return; } } else { if (NewConnection.FlashPolicyFileRequest) { RMLog.Info("{" + _ConnectionId.ToString() + "} Answered flash policy file request from " + NewConnection.GetRemoteIP() + ":" + NewConnection.GetRemotePort().ToString()); } else { RMLog.Debug("{" + _ConnectionId.ToString() + "} Invalid WebSocket connection from " + NewConnection.GetRemoteIP() + ":" + NewConnection.GetRemotePort().ToString()); } return; } // If we get here it's a proxy connection, so handle it RMLog.Info("{" + _ConnectionId.ToString() + "} Connection accepted from " + NewConnection.GetRemoteIP() + ":" + NewConnection.GetRemotePort()); string MessageText = string.Format("{0}\t{1}\t{2}\t{3}\t{4}\t{5}\r\n", DateTime.Now.ToString(), NewConnection.GetRemoteIP(), NewConnection.GetRemotePort(), NewConnection.Header["Path"], NewConnection.Protocol, NewConnection.SubProtocol); FileUtils.FileAppendAllText(Path.Combine(ProcessUtils.StartupPath, "fTelnetProxy-Connections.log"), MessageText, Encoding.ASCII); // Defaults for redirect location string Hostname = Config.Default.TargetHostname; int Port = Config.Default.TargetPort; // Check if user is requesting a custom target if (NewConnection.Header["Path"] != "/") { bool CanRelay = false; // Extract the requested host and port string[] HostAndPort = NewConnection.Header["Path"].Split('/'); if ((HostAndPort.Length == 3) && (int.TryParse(HostAndPort[2], out Port))) { Hostname = HostAndPort[1]; if (Config.Default.TargetHostname.ToLower().Trim() == Hostname.ToLower().Trim()) { // User is requesting the target defined by the proxy admin, so check if it's to an allowed port CanRelay = ((Port > 0) && (Port == Config.Default.TargetPort) || (Port == Config.Default.RLoginPort)); } else if (!string.IsNullOrEmpty(Config.Default.RelayFilename)) { // proxy admin has relaying enabled, so check against the relay.cfg file try { // Read relay file if (File.Exists(Config.Default.RelayFilename)) { string[] AllowedHosts = File.ReadAllLines(Config.Default.RelayFilename); if (AllowedHosts.Length > 0) { // Check for a whitelisted port string[] AllowedPorts = AllowedHosts[0].Split(','); foreach (string AllowedPort in AllowedPorts) { if (AllowedPort == Port.ToString()) { CanRelay = true; break; } } // Not a whitelisted port, check for a whitelisted host if (!CanRelay) { string RequestedHostPort = Hostname.ToLower() + ":" + Port.ToString(); foreach (string AllowedHost in AllowedHosts) { if (AllowedHost.Trim().ToLower() == RequestedHostPort) { CanRelay = true; break; } } } } else { RMLog.Error("{" + _ConnectionId.ToString() + "} Relay file is empty: '" + Config.Default.RelayFilename + "'"); } } else { RMLog.Error("{" + _ConnectionId.ToString() + "} Relay file does not exist: '" + Config.Default.RelayFilename + "'"); } } catch (Exception ex) { RMLog.Exception(ex, "{" + _ConnectionId.ToString() + "} Error reading relay file: '" + Config.Default.RelayFilename + "'"); } } } if (!CanRelay) { RMLog.Info("{" + _ConnectionId.ToString() + "} Rejecting request for " + Hostname + ":" + Port.ToString()); NewConnection.WriteLn("Sorry, for security reasons this proxy won't connect to " + Hostname + ":" + Port.ToString()); Thread.Sleep(2500); return; } } // Try to connect to the desired Host and Port NewConnection.Write(Ansi.ClrScr() + "Connecting to " + Hostname + ":" + Port.ToString() + "..."); using (TcpConnection _TcpConnection = new TcpConnection()) { if (_TcpConnection.Connect(Hostname, Port)) { RMLog.Info("{" + _ConnectionId.ToString() + "} Connected to " + Hostname + ":" + Port.ToString()); NewConnection.WriteLn("connected!"); // Repeatedly move data around until a connection is closed (or a stop is requested) bool DoSleep = true; while (!_Stop && NewConnection.Connected && _TcpConnection.Connected) { DoSleep = true; if (NewConnection.CanRead()) { _TcpConnection.WriteBytes(NewConnection.ReadBytes()); DoSleep = false; } if (_TcpConnection.CanRead()) { NewConnection.WriteBytes(_TcpConnection.ReadBytes()); DoSleep = false; } if (DoSleep) { Thread.Sleep(1); } } // Check why we exited the loop if (_Stop) { RMLog.Info("{" + _ConnectionId.ToString() + "} Stop requested"); NewConnection.Write(Ansi.GotoXY(1, 1) + Ansi.CursorDown(255) + "\r\nProxy server shutting down..."); Thread.Sleep(2500); } else if (!NewConnection.Connected) { RMLog.Info("{" + _ConnectionId.ToString() + "} Client closed connection"); } else if (!_TcpConnection.Connected) { RMLog.Info("{" + _ConnectionId.ToString() + "} Server closed connection"); NewConnection.Write(Ansi.GotoXY(1, 1) + Ansi.CursorDown(255) + "\r\nServer closed connection..."); Thread.Sleep(2500); } } else { RMLog.Info("{" + _ConnectionId.ToString() + "} Unable to connect to " + Hostname + ":" + Port.ToString()); NewConnection.WriteLn("unable to connect!"); Thread.Sleep(2500); } } } } catch (Exception ex) { RMLog.Exception(ex, "{" + _ConnectionId.ToString() + "} Exception in client thread"); } }
// TODOY Consolidate with ParseEnvironmentVariables private void ParseCommandLineArgs() { string[] Args = Environment.GetCommandLineArgs(); if (Args.Length > 1) { RMLog.Info("Overriding with settings from command-line"); for (int i = 1; i < Args.Length; i++) { string Arg = Args[i].TrimStart('/').TrimStart('-'); switch (Arg) { case "c": case "cert": i += 1; // If file doesn't exist, and it's relative, convert to absolute if (!File.Exists(Args[i]) && !Path.IsPathRooted(Args[i])) { Args[i] = StringUtils.PathCombine(ProcessUtils.StartupPath, Args[i]); } if (File.Exists(Args[i])) { Config.Default.CertificateFilename = Args[i]; RMLog.Info("-Cert file......" + Config.Default.CertificateFilename); } else { RMLog.Error("-Cert file not found: '" + Args[i] + "'"); } break; case "?": case "h": case "help": ShowHelp(); return; case "l": case "loglevel": i += 1; try { RMLog.Level = (LogLevel)Enum.Parse(typeof(LogLevel), Args[i]); RMLog.Info("-Log level......" + RMLog.Level.ToString()); } catch (Exception ex) { RMLog.Exception(ex, "-Invalid log level: '" + Args[i] + "'"); } break; case "p": case "port": i += 1; try { Config.Default.ListenPort = Convert.ToInt16(Args[i]); RMLog.Info("-Listen port...." + Config.Default.ListenPort.ToString()); } catch (Exception ex) { RMLog.Exception(ex, "-Invalid port: '" + Args[i] + "'"); } break; case "pw": case "password": i += 1; Config.Default.CertificatePassword = Args[i]; RMLog.Info("-Cert password..yes (hidden)"); break; case "r": case "relay": i += 1; // If file doesn't exist, and it's relative, convert to absolute if (!File.Exists(Args[i]) && !Path.IsPathRooted(Args[i])) { Args[i] = StringUtils.PathCombine(ProcessUtils.StartupPath, Args[i]); } if (File.Exists(Args[i])) { Config.Default.RelayFilename = Args[i]; RMLog.Info("-Relay file....." + Config.Default.RelayFilename); } else { Config.Default.RelayFilename = ""; RMLog.Error("-Relay file not found: '" + Args[i] + "'"); } break; case "rp": case "rlogin-port": i += 1; try { Config.Default.RLoginPort = Convert.ToInt16(Args[i]); if (Config.Default.RLoginPort > 0) { // TODOX If -rp is specified before -t, then this will display the wrong hostname RMLog.Info("-RLogin target.." + Config.Default.TargetHostname + ":" + Config.Default.RLoginPort.ToString()); } else { RMLog.Info("-RLogin target..DISABLED"); } } catch (Exception ex) { RMLog.Exception(ex, "-Invalid port: '" + Args[i] + "'"); } break; case "t": case "target": i += 1; string TargetHostname = Config.Default.TargetHostname; int TargetPort = Config.Default.TargetPort; WebUtils.ParseHostPort(Args[i], ref TargetHostname, ref TargetPort); Config.Default.TargetHostname = TargetHostname; Config.Default.TargetPort = TargetPort; if (Config.Default.TargetPort > 0) { RMLog.Info("-Telnet target.." + Config.Default.TargetHostname + ":" + Config.Default.TargetPort.ToString()); } else { RMLog.Info("-Telnet target..DISABLED"); } break; default: RMLog.Error("-Unknown parameter: '" + Args[i] + "'"); break; } } } }
// TODOY Consolidate with ParseCommandLine private void ParseEnvironmentVariables() { var EnvironmentVariables = Environment.GetEnvironmentVariables().Cast <DictionaryEntry>().Where(x => x.Key.ToString().ToLower().StartsWith("ftelnet_")).ToArray(); if (EnvironmentVariables.Length > 0) { RMLog.Info("Overriding with settings from environment variables"); for (int i = 0; i < EnvironmentVariables.Length; i++) { string Arg = EnvironmentVariables[i].Key.ToString().Substring(8).ToLower().Replace('_', '-'); // Substring off the leading ftelnet_ and replace _ with - string Value = EnvironmentVariables[i].Value.ToString(); switch (Arg) { case "c": case "cert": // If file doesn't exist, and it's relative, convert to absolute if (!File.Exists(Value) && !Path.IsPathRooted(Value)) { Value = StringUtils.PathCombine(ProcessUtils.StartupPath, Value); } if (File.Exists(Value)) { Config.Default.CertificateFilename = Value; RMLog.Info("-Cert file......" + Config.Default.CertificateFilename); } else { RMLog.Error("-Cert file not found: '" + Value + "'"); } break; case "l": case "loglevel": try { RMLog.Level = (LogLevel)Enum.Parse(typeof(LogLevel), Value); RMLog.Info("-Log level......" + RMLog.Level.ToString()); } catch (Exception ex) { RMLog.Exception(ex, "-Invalid log level: '" + Value + "'"); } break; case "p": case "port": try { Config.Default.ListenPort = Convert.ToInt16(Value); RMLog.Info("-Listen port...." + Config.Default.ListenPort.ToString()); } catch (Exception ex) { RMLog.Exception(ex, "-Invalid port: '" + Value + "'"); } break; case "pw": case "password": Config.Default.CertificatePassword = Value; RMLog.Info("-Cert password..yes (hidden)"); break; case "r": case "relay": // If file doesn't exist, and it's relative, convert to absolute if (!File.Exists(Value) && !Path.IsPathRooted(Value)) { Value = StringUtils.PathCombine(ProcessUtils.StartupPath, Value); } if (File.Exists(Value)) { Config.Default.RelayFilename = Value; RMLog.Info("-Relay file....." + Config.Default.RelayFilename); } else { Config.Default.RelayFilename = ""; RMLog.Error("-Relay file not found: '" + Value + "'"); } break; case "rp": case "rlogin-port": try { Config.Default.RLoginPort = Convert.ToInt16(Value); if (Config.Default.RLoginPort > 0) { // TODOX If -rp is specified before -t, then this will display the wrong hostname RMLog.Info("-RLogin target.." + Config.Default.TargetHostname + ":" + Config.Default.RLoginPort.ToString()); } else { RMLog.Info("-RLogin target..DISABLED"); } } catch (Exception ex) { RMLog.Exception(ex, "-Invalid port: '" + Value + "'"); } break; case "t": case "target": string TargetHostname = Config.Default.TargetHostname; int TargetPort = Config.Default.TargetPort; WebUtils.ParseHostPort(Value, ref TargetHostname, ref TargetPort); Config.Default.TargetHostname = TargetHostname; Config.Default.TargetPort = TargetPort; if (Config.Default.TargetPort > 0) { RMLog.Info("-Telnet target.." + Config.Default.TargetHostname + ":" + Config.Default.TargetPort.ToString()); } else { RMLog.Info("-Telnet target..DISABLED"); } break; default: RMLog.Error("-Unknown parameter: '" + Arg + "'"); break; } } } }