using SharpPcap.LibPcap; namespace gatuna_client; public partial class MainForm : Form { readonly SessionManager _sessions = new(); readonly ComboBox _deviceBox = new(); readonly Button _discoverBtn = new(); readonly Button _testBtn = new(); readonly Label _serverLabel = new(); readonly Label _pingStatsLabel = new(); readonly ListView _listView = new(); readonly Label _statusLabel = new(); TunnelLink? _link; PingTest? _pingTest; public MainForm() { Text = "gatuna"; Width = 520; Height = 420; FormBorderStyle = FormBorderStyle.FixedSingle; MaximizeBox = false; StartPosition = FormStartPosition.CenterScreen; Icon = SystemIcons.GetStockIcon(StockIconId.NetworkConnect, 32); InitializeComponents(); _sessions.Log += msg => this.Invoke(() => _statusLabel.Text = msg); _sessions.ManifestReceived += (hostname, mac, entries) => this.Invoke(() => PopulateList(hostname, mac, entries)); foreach (var d in TunnelLink.ListDevices()) { var friendly = d.Interface?.FriendlyName ?? ""; var desc = d.Interface?.Description ?? ""; var mac = d.MacAddress?.GetAddressBytes(); var macStr = mac != null ? string.Join(":", mac.Select(b => b.ToString("X2"))) : "??-??-??-??-??-??"; _deviceBox.Items.Add($"{friendly} — {desc} — {macStr}"); } if (_deviceBox.Items.Count > 0) _deviceBox.SelectedIndex = 0; } void InitializeComponents() { const int pad = 12; _deviceBox.Left = pad; _deviceBox.Top = 12; _deviceBox.Width = ClientSize.Width - pad * 2; _deviceBox.DropDownStyle = ComboBoxStyle.DropDownList; Controls.Add(_deviceBox); _discoverBtn.Text = "Discover"; _discoverBtn.Left = pad; _discoverBtn.Top = _deviceBox.Bottom + 8; _discoverBtn.Width = 80; _discoverBtn.Click += OnDiscover; Controls.Add(_discoverBtn); _testBtn.Text = "Test"; _testBtn.Left = _discoverBtn.Right + 8; _testBtn.Top = _discoverBtn.Top; _testBtn.Width = 60; _testBtn.Click += OnTest; Controls.Add(_testBtn); _serverLabel.Left = pad; _serverLabel.Top = _discoverBtn.Bottom + 8; _serverLabel.Width = ClientSize.Width - pad * 2; _serverLabel.AutoEllipsis = true; _serverLabel.Text = "Server: not connected"; Controls.Add(_serverLabel); _pingStatsLabel.Left = pad; _pingStatsLabel.Top = _serverLabel.Bottom + 4; _pingStatsLabel.Width = ClientSize.Width - pad * 2; _pingStatsLabel.AutoEllipsis = true; _pingStatsLabel.Text = ""; Controls.Add(_pingStatsLabel); _listView.Left = pad; _listView.Top = _pingStatsLabel.Bottom + 8; _listView.Width = ClientSize.Width - pad * 2; _listView.Height = 180; _listView.View = View.Details; _listView.FullRowSelect = true; _listView.CheckBoxes = true; _listView.Columns.Add("ID", 36); _listView.Columns.Add("Proto", 50); _listView.Columns.Add("Port", 56); _listView.Columns.Add("Label", 160); _listView.Columns.Add("Mirror", 70); _listView.ItemChecked += OnItemChecked; Controls.Add(_listView); _statusLabel.Left = pad; _statusLabel.Top = _listView.Bottom + 8; _statusLabel.Width = ClientSize.Width - pad * 2; _statusLabel.AutoEllipsis = true; Controls.Add(_statusLabel); } void OnDiscover(object? s, EventArgs e) { if (_deviceBox.SelectedIndex < 0) { MessageBox.Show("Select a network adapter first."); return; } if (_link != null) { _sessions.StopAll(); _sessions.DetachLink(); _link.Dispose(); } var devices = TunnelLink.ListDevices(); var device = devices[_deviceBox.SelectedIndex]; _link = new TunnelLink(device); _link.Log += msg => this.Invoke(() => _statusLabel.Text = msg); _sessions.AttachLink(_link); _link.Open(); _sessions.Discover(); _serverLabel.Text = "Server: discovering..."; _statusLabel.Text = "discovering..."; } void OnTest(object? s, EventArgs e) { if (_pingTest != null && _pingTest.Running) { _sessions.StopPing(); _pingTest = null; _testBtn.Text = "Test"; return; } _pingTest = _sessions.StartPing(); if (_pingTest == null) { MessageBox.Show("Discover a server first."); return; } _pingTest.StatsUpdated += stats => this.Invoke(() => { _pingStatsLabel.Text = $"sent: {stats.Sent} recv: {stats.Received} " + $"loss: {stats.LossPct:F1}% " + $"avg: {stats.AvgLatencyMs:F1}ms " + $"jitter: {stats.JitterMs:F1}ms"; }); _testBtn.Text = "Stop"; _pingStatsLabel.Text = "pinging..."; } void PopulateList(string hostname, byte[] mac, UpstreamEntry[] entries) { var macStr = string.Join(":", mac.Select(b => b.ToString("X2"))); _serverLabel.Text = $"Server: {hostname} — {macStr}"; _listView.BeginUpdate(); _listView.Items.Clear(); foreach (var up in entries) { var item = new ListViewItem(up.Id.ToString()); item.SubItems.Add(up.ProtoName); item.SubItems.Add(up.Port.ToString()); item.SubItems.Add(up.Label ?? ""); item.SubItems.Add(""); item.Tag = up; _listView.Items.Add(item); } _listView.EndUpdate(); } void OnItemChecked(object? s, ItemCheckedEventArgs e) { if (e.Item.Tag is not UpstreamEntry up) return; if (e.Item.Checked) { var port = _sessions.StartListener(up); e.Item.SubItems[4].Text = port.ToString(); } else { if (int.TryParse(e.Item.SubItems[4].Text, out var port) && port > 0) { _sessions.StopListener(port); e.Item.SubItems[4].Text = ""; } } } protected override void OnResize(EventArgs e) { base.OnResize(e); if (WindowState == FormWindowState.Minimized) { Hide(); WindowState = FormWindowState.Normal; } } protected override void OnFormClosing(FormClosingEventArgs e) { Shutdown(); base.OnFormClosing(e); } public void Shutdown() { _sessions.Dispose(); _link?.Dispose(); } }