using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Net.NetworkInformation; using System.Threading; using System.Threading.Tasks; using System.Windows; using AxCopilot.SDK; using AxCopilot.Services; namespace AxCopilot.Handlers; public class PortHandler : IActionHandler { private static readonly Dictionary _procCache = new Dictionary(); private static readonly Dictionary _pidMap = new Dictionary(); private static DateTime _cacheExpiry = DateTime.MinValue; public string? Prefix => "port"; public PluginMetadata Metadata => new PluginMetadata("PortChecker", "포트 & 프로세스 점검 — port 뒤에 포트번호 또는 프로세스명", "1.0", "AX"); public Task> GetItemsAsync(string query, CancellationToken ct) { RefreshProcessCache(); TcpConnectionInformation[] activeTcpConnections; try { IPGlobalProperties iPGlobalProperties = IPGlobalProperties.GetIPGlobalProperties(); activeTcpConnections = iPGlobalProperties.GetActiveTcpConnections(); } catch (Exception ex) { LogService.Warn("포트 조회 실패: " + ex.Message); return Task.FromResult((IEnumerable)new _003C_003Ez__ReadOnlySingleElementList(new LauncherItem("네트워크 정보를 가져올 수 없습니다", ex.Message, null, null, null, "\ue7ba"))); } string text = query.Trim(); if (string.IsNullOrWhiteSpace(text)) { List list = (from c in activeTcpConnections where c.State == TcpState.Established || c.State == TcpState.Listen orderby c.LocalEndPoint.Port select c).Take(20).Select(delegate(TcpConnectionInformation c) { string processNameForPort = GetProcessNameForPort(c.LocalEndPoint.Port); string text2 = ((c.State == TcpState.Listen) ? "LISTEN" : "ESTABLISHED"); return new LauncherItem($":{c.LocalEndPoint.Port} → {c.RemoteEndPoint}", text2 + " · " + processNameForPort + " · Enter로 포트번호 복사", null, c.LocalEndPoint.Port.ToString(), null, "\ue968"); }).ToList(); if (!list.Any()) { list.Add(new LauncherItem("활성 연결 없음", "TCP 연결이 감지되지 않았습니다", null, null, null, "\ue968")); } return Task.FromResult((IEnumerable)list); } if (int.TryParse(text, out var portNum)) { List source = activeTcpConnections.Where((TcpConnectionInformation c) => c.LocalEndPoint.Port == portNum || c.RemoteEndPoint.Port == portNum).ToList(); if (!source.Any()) { return Task.FromResult((IEnumerable)new _003C_003Ez__ReadOnlySingleElementList(new LauncherItem($"포트 {portNum} — 사용 중 아님", "해당 포트를 사용하는 TCP 연결이 없습니다", null, portNum.ToString(), null, "\ue946"))); } List result = source.Select(delegate(TcpConnectionInformation c) { string processNameForPort = GetProcessNameForPort(c.LocalEndPoint.Port); int pidForPort = GetPidForPort(c.LocalEndPoint.Port); return new LauncherItem($":{c.LocalEndPoint.Port} ←→ {c.RemoteEndPoint}", $"{c.State} · {processNameForPort} (PID {pidForPort}) · Enter로 PID 복사", null, (pidForPort > 0) ? pidForPort.ToString() : portNum.ToString(), null, "\ue968"); }).ToList(); return Task.FromResult((IEnumerable)result); } string procLower = text.ToLowerInvariant(); List list2 = activeTcpConnections.Where(delegate(TcpConnectionInformation c) { string text2 = GetProcessNameForPort(c.LocalEndPoint.Port).ToLowerInvariant(); return text2.Contains(procLower); }).Take(15).Select(delegate(TcpConnectionInformation c) { string processNameForPort = GetProcessNameForPort(c.LocalEndPoint.Port); return new LauncherItem($"{processNameForPort} : {c.LocalEndPoint.Port} → {c.RemoteEndPoint}", $"{c.State} · Enter로 포트번호 복사", null, c.LocalEndPoint.Port.ToString(), null, "\ue968"); }) .ToList(); if (!list2.Any()) { list2.Add(new LauncherItem("'" + text + "' — 연결 없음", "해당 프로세스의 TCP 연결이 없습니다", null, null, null, "\ue7ba")); } return Task.FromResult((IEnumerable)list2); } public Task ExecuteAsync(LauncherItem item, CancellationToken ct) { if (item.Data is string text) { try { Clipboard.SetText(text); } catch { } } return Task.CompletedTask; } private static void RefreshProcessCache() { if (DateTime.Now < _cacheExpiry) { return; } _procCache.Clear(); _pidMap.Clear(); try { Process[] processes = Process.GetProcesses(); foreach (Process process in processes) { try { _procCache[process.Id] = process.ProcessName; } catch { } } } catch (Exception ex) { LogService.Warn("프로세스 목록 갱신 실패: " + ex.Message); } try { ProcessStartInfo startInfo = new ProcessStartInfo("netstat", "-ano") { RedirectStandardOutput = true, UseShellExecute = false, CreateNoWindow = true }; using Process process2 = Process.Start(startInfo); if (process2 != null) { string text = process2.StandardOutput.ReadToEnd(); process2.WaitForExit(2000); string[] array = text.Split('\n'); foreach (string text2 in array) { string[] array2 = text2.Trim().Split(new char[2] { ' ', '\t' }, StringSplitOptions.RemoveEmptyEntries); if (array2.Length < 5 || !int.TryParse(array2[^1], out var result)) { continue; } string text3 = array2[1]; int num = text3.LastIndexOf(':'); if (num >= 0) { string text4 = text3; int num2 = num + 1; if (int.TryParse(text4.Substring(num2, text4.Length - num2), out var result2)) { _pidMap.TryAdd(result2, result); } } } } } catch (Exception ex2) { LogService.Warn("netstat 실행 실패: " + ex2.Message); } _cacheExpiry = DateTime.Now.AddSeconds(5.0); } private static string GetProcessNameForPort(int port) { int pidForPort = GetPidForPort(port); string value; return (pidForPort > 0 && _procCache.TryGetValue(pidForPort, out value)) ? value : "알 수 없음"; } private static int GetPidForPort(int port) { int value; return _pidMap.TryGetValue(port, out value) ? value : (-1); } }