Add AmneziaWG mode (wireproxy-awg userspace SOCKS)
Third mode alongside proxy and VLESS+Reality. Bundles wireproxy-awg built from a pinned, audited source commit (fork of wireproxy over the official amneziawg-go), exposing a local SOCKS5 tunnel with no TUN/driver/admin. User picks their AmneziaWG .conf; the app appends a [Socks5] section and routes Claude through it with the same fail-closed firewall. build-wireproxy.ps1 reproducibly builds the binary (portable Go + pinned commit); it is embedded as a resource and not stored in git. Co-Authored-By: Claude Opus 4.8 <noreply@anthropic.com>
This commit is contained in:
+74
-1
@@ -89,6 +89,7 @@ namespace ClaudeProxy
|
||||
case "mode": c.Mode = v.Trim(); break;
|
||||
case "proxy": c.Proxy = v.Trim(); break;
|
||||
case "vless": c.Vless = v; break;
|
||||
case "awg": c.Awg = v.Trim(); break;
|
||||
case "localPort": int.TryParse(v.Trim(), out c.LocalPort); break;
|
||||
case "failClosed": c.FailClosed = v.Trim() == "1" || v.Trim().ToLower() == "true"; break;
|
||||
}
|
||||
@@ -108,6 +109,7 @@ namespace ClaudeProxy
|
||||
sb.AppendLine("mode=" + c.Mode);
|
||||
sb.AppendLine("proxy=" + c.Proxy);
|
||||
sb.AppendLine("vless=" + (c.Vless ?? ""));
|
||||
sb.AppendLine("awg=" + (c.Awg ?? ""));
|
||||
sb.AppendLine("localPort=" + c.LocalPort);
|
||||
sb.AppendLine("failClosed=" + (c.FailClosed ? "1" : "0"));
|
||||
File.WriteAllText(ConfigPath, sb.ToString());
|
||||
@@ -271,7 +273,7 @@ namespace ClaudeProxy
|
||||
var app = GetClaudeApp();
|
||||
RemoveFirewall();
|
||||
dynamic policy = FwPolicy();
|
||||
if (cfg.Mode == "vless")
|
||||
if (cfg.Mode == "vless" || cfg.Mode == "awg")
|
||||
{
|
||||
// tunnel: block ALL external (loopback is exempt from firewall filtering)
|
||||
AddBlockRule(policy, "ClaudeProxyLock: tunnel", app.Exe, null);
|
||||
@@ -389,5 +391,76 @@ namespace ClaudeProxy
|
||||
|
||||
public static bool XrayRunning() { return Process.GetProcessesByName("xray").Any(p =>
|
||||
{ try { return p.MainModule.FileName.StartsWith(BinDir, StringComparison.OrdinalIgnoreCase); } catch { return false; } }); }
|
||||
|
||||
// ---- AmneziaWG (wireproxy-awg userspace SOCKS) ----------------------------
|
||||
public static string AwgExe { get { return Path.Combine(BinDir, "wireproxy-awg.exe"); } }
|
||||
public static string AwgRuntimeCfg { get { return Path.Combine(ConfigDir, "awg-runtime.conf"); } }
|
||||
public static bool AwgPresent() { return File.Exists(AwgExe); }
|
||||
|
||||
public static void EnsureAwg()
|
||||
{
|
||||
if (AwgPresent()) return;
|
||||
Directory.CreateDirectory(BinDir);
|
||||
var asm = Assembly.GetExecutingAssembly();
|
||||
string res = asm.GetManifestResourceNames().FirstOrDefault(n => n.EndsWith("awg.exe", StringComparison.OrdinalIgnoreCase));
|
||||
if (res == null) throw new Exception("Встроенный wireproxy-awg не найден.");
|
||||
using (var rs = asm.GetManifestResourceStream(res))
|
||||
using (var fs = File.Create(AwgExe))
|
||||
rs.CopyTo(fs);
|
||||
if (!AwgPresent()) throw new Exception("Не удалось распаковать wireproxy-awg.");
|
||||
}
|
||||
|
||||
public static void StopAwg()
|
||||
{
|
||||
foreach (var p in Process.GetProcessesByName("wireproxy-awg"))
|
||||
try { if (p.MainModule.FileName.StartsWith(BinDir, StringComparison.OrdinalIgnoreCase)) p.Kill(); } catch { }
|
||||
Thread.Sleep(300);
|
||||
}
|
||||
public static bool AwgRunning() { return Process.GetProcessesByName("wireproxy-awg").Any(p =>
|
||||
{ try { return p.MainModule.FileName.StartsWith(BinDir, StringComparison.OrdinalIgnoreCase); } catch { return false; } }); }
|
||||
|
||||
// Take the user's AmneziaWG .conf, append a [Socks5] section, run wireproxy-awg on it.
|
||||
public static void StartAwg(string confPath, int localPort)
|
||||
{
|
||||
EnsureAwg();
|
||||
if (string.IsNullOrWhiteSpace(confPath) || !File.Exists(confPath))
|
||||
throw new Exception("Файл AmneziaWG-конфига не найден: " + confPath);
|
||||
string cfg = File.ReadAllText(confPath);
|
||||
if (cfg.IndexOf("[Socks5]", StringComparison.OrdinalIgnoreCase) < 0)
|
||||
cfg = cfg.TrimEnd() + "\r\n\r\n[Socks5]\r\nBindAddress = 127.0.0.1:" + localPort + "\r\n";
|
||||
File.WriteAllText(AwgRuntimeCfg, cfg);
|
||||
StopAwg();
|
||||
var psi = new ProcessStartInfo
|
||||
{
|
||||
FileName = AwgExe,
|
||||
Arguments = "-s -c \"" + AwgRuntimeCfg + "\"",
|
||||
UseShellExecute = false, CreateNoWindow = true, WindowStyle = ProcessWindowStyle.Hidden
|
||||
};
|
||||
Process.Start(psi);
|
||||
for (int i = 0; i < 40; i++) { if (LocalSocksUp(localPort)) return; Thread.Sleep(200); }
|
||||
StopAwg();
|
||||
throw new Exception("wireproxy-awg не поднял SOCKS на 127.0.0.1:" + localPort + ". Проверь конфиг AmneziaWG.");
|
||||
}
|
||||
|
||||
// Info line for the AmneziaWG config (its Peer Endpoint). WG is UDP, so we don't TCP-ping it.
|
||||
public static bool AwgConfigInfo(string confPath, out string info)
|
||||
{
|
||||
info = "не выбран";
|
||||
if (string.IsNullOrWhiteSpace(confPath) || !File.Exists(confPath)) return false;
|
||||
try
|
||||
{
|
||||
foreach (var line in File.ReadAllLines(confPath))
|
||||
{
|
||||
var l = line.Trim();
|
||||
if (l.StartsWith("Endpoint", StringComparison.OrdinalIgnoreCase))
|
||||
{
|
||||
int eq = l.IndexOf('=');
|
||||
if (eq >= 0) { info = l.Substring(eq + 1).Trim(); return true; }
|
||||
}
|
||||
}
|
||||
info = "выбран"; return true;
|
||||
}
|
||||
catch { info = "ошибка чтения"; return false; }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+71
-21
@@ -23,7 +23,7 @@ namespace ClaudeProxy
|
||||
fPill = new Font("Segoe UI Semibold", 12f), fMono = new Font("Consolas", 8.5f);
|
||||
|
||||
bool switchOn = false, busy = false;
|
||||
RadioButton rbProxy, rbVless;
|
||||
RadioButton rbProxy, rbVless, rbAwg;
|
||||
BufferedPanel mark, tgl, card;
|
||||
Label title, subtitle, lblFieldCap, lblReach, rDot, lblFwCap, lblFw, lblClCap, lblCl, lblXrayCap, lblXray;
|
||||
TextBox txtField, log;
|
||||
@@ -32,7 +32,7 @@ namespace ClaudeProxy
|
||||
Panel sep;
|
||||
Timer timer;
|
||||
|
||||
string Mode { get { return rbVless.Checked ? "vless" : "proxy"; } }
|
||||
string Mode { get { return rbAwg.Checked ? "awg" : (rbVless.Checked ? "vless" : "proxy"); } }
|
||||
|
||||
public MainForm()
|
||||
{
|
||||
@@ -53,9 +53,10 @@ namespace ClaudeProxy
|
||||
subtitle = MkLabel("весь трафик Claude через прокси", fSub, Muted, 84, 48);
|
||||
subtitle.AutoSize = false; subtitle.Size = new Size(196, 18); Controls.Add(subtitle);
|
||||
|
||||
rbProxy = MkRadio("Прямой прокси", 292, 18);
|
||||
rbVless = MkRadio("VLESS + Reality", 292, 42);
|
||||
Controls.Add(rbProxy); Controls.Add(rbVless);
|
||||
rbProxy = MkRadio("Прямой прокси", 292, 14);
|
||||
rbVless = MkRadio("VLESS + Reality", 292, 36);
|
||||
rbAwg = MkRadio("AmneziaWG", 292, 58);
|
||||
Controls.Add(rbProxy); Controls.Add(rbVless); Controls.Add(rbAwg);
|
||||
|
||||
tgl = new BufferedPanel { Size = new Size(172, 58), Location = new Point(24, 88), BackColor = Cream, Cursor = Cursors.Hand };
|
||||
tgl.Paint += (s, e) => PaintToggle(e.Graphics, tgl.Width, tgl.Height);
|
||||
@@ -83,7 +84,7 @@ namespace ClaudeProxy
|
||||
|
||||
lblXrayCap = MkLabel("Xray-core", fReg, Muted, 18, 68); lblXrayCap.AutoSize = true; card.Controls.Add(lblXrayCap);
|
||||
lblXray = MkLabel("", fSemi, Ink, 120, 68); lblXray.AutoSize = true; card.Controls.Add(lblXray);
|
||||
btnXray = MkButton("Распаковать", 300, 64, 96, 26, false); btnXray.Click += (s, e) => OnXray(); card.Controls.Add(btnXray);
|
||||
btnXray = MkButton("Распаковать", 300, 64, 96, 26, false); btnXray.Click += (s, e) => { if (Mode == "awg") OnBrowseAwg(); else OnXray(); }; card.Controls.Add(btnXray);
|
||||
|
||||
sep = new Panel { Location = new Point(18, 86), Size = new Size(378, 1), BackColor = Border }; card.Controls.Add(sep);
|
||||
lblFwCap = MkLabel("Firewall-лок", fReg, Muted, 18, 98); lblFwCap.AutoSize = true; card.Controls.Add(lblFwCap);
|
||||
@@ -102,7 +103,8 @@ namespace ClaudeProxy
|
||||
log = new TextBox { Multiline = true, ReadOnly = true, ScrollBars = ScrollBars.Vertical, Location = new Point(20, 430), Size = new Size(412, 66), BorderStyle = BorderStyle.FixedSingle, BackColor = Card, ForeColor = Muted, Font = fMono };
|
||||
Controls.Add(log);
|
||||
|
||||
rbVless.CheckedChanged += (s, e) => { ApplyModeLayout(); var c = Core.LoadConfig(); c.Mode = Mode; Core.SaveConfig(c); UpdateUi(true); };
|
||||
EventHandler modeCh = (s, e) => { if (!((RadioButton)s).Checked) return; ApplyModeLayout(); var c = Core.LoadConfig(); c.Mode = Mode; Core.SaveConfig(c); UpdateUi(true); };
|
||||
rbVless.CheckedChanged += modeCh; rbAwg.CheckedChanged += modeCh; rbProxy.CheckedChanged += modeCh;
|
||||
|
||||
timer = new Timer { Interval = 4000 };
|
||||
timer.Tick += (s, e) => { if (!busy) UpdateUi(false); };
|
||||
@@ -111,7 +113,7 @@ namespace ClaudeProxy
|
||||
Load += (s, e) => {
|
||||
var cfg = Core.LoadConfig();
|
||||
chkFail.Checked = cfg.FailClosed;
|
||||
if (cfg.Mode == "vless") rbVless.Checked = true; else rbProxy.Checked = true;
|
||||
if (cfg.Mode == "awg") rbAwg.Checked = true; else if (cfg.Mode == "vless") rbVless.Checked = true; else rbProxy.Checked = true;
|
||||
ApplyModeLayout();
|
||||
Log("готово. рубильник управляет проксированием Claude.");
|
||||
UpdateUi(true);
|
||||
@@ -182,20 +184,27 @@ namespace ClaudeProxy
|
||||
void ApplyModeLayout()
|
||||
{
|
||||
var cfg = Core.LoadConfig();
|
||||
bool tunnel = Mode == "vless" || Mode == "awg";
|
||||
lblXrayCap.Visible = lblXray.Visible = btnXray.Visible = tunnel;
|
||||
if (tunnel) { sep.Top = 100; lblFwCap.Top = lblFw.Top = 112; lblClCap.Top = lblCl.Top = 134; }
|
||||
else { sep.Top = 74; lblFwCap.Top = lblFw.Top = 86; lblClCap.Top = lblCl.Top = 108; }
|
||||
|
||||
if (Mode == "vless")
|
||||
{
|
||||
lblFieldCap.Text = "VLESS"; subtitle.Text = "через VLESS + Reality тоннель";
|
||||
txtField.UseSystemPasswordChar = true; txtField.Text = cfg.Vless ?? "";
|
||||
lblXrayCap.Visible = lblXray.Visible = btnXray.Visible = true;
|
||||
sep.Top = 100; lblFwCap.Top = lblFw.Top = 112; lblClCap.Top = lblCl.Top = 134;
|
||||
UpdateXrayStatus();
|
||||
lblXrayCap.Text = "Xray-core"; UpdateXrayStatus();
|
||||
}
|
||||
else if (Mode == "awg")
|
||||
{
|
||||
lblFieldCap.Text = "Конфиг"; subtitle.Text = "через AmneziaWG тоннель";
|
||||
txtField.UseSystemPasswordChar = false; txtField.Text = cfg.Awg ?? "";
|
||||
lblXrayCap.Text = "AmneziaWG"; UpdateAwgStatus();
|
||||
}
|
||||
else
|
||||
{
|
||||
lblFieldCap.Text = "Прокси"; subtitle.Text = "весь трафик Claude через прокси";
|
||||
txtField.UseSystemPasswordChar = false; txtField.Text = cfg.Proxy ?? "";
|
||||
lblXrayCap.Visible = lblXray.Visible = btnXray.Visible = false;
|
||||
sep.Top = 74; lblFwCap.Top = lblFw.Top = 86; lblClCap.Top = lblCl.Top = 108;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -203,6 +212,14 @@ namespace ClaudeProxy
|
||||
{
|
||||
if (Core.XrayPresent()) { lblXray.Text = "готов (" + Core.XrayVersion + ")"; lblXray.ForeColor = OkC; btnXray.Text = "Переустановить"; }
|
||||
else { lblXray.Text = "встроен (" + Core.XrayVersion + ")"; lblXray.ForeColor = OkC; btnXray.Text = "Распаковать"; }
|
||||
btnXray.Width = 96; btnXray.Left = 300;
|
||||
}
|
||||
|
||||
void UpdateAwgStatus()
|
||||
{
|
||||
bool ok = !string.IsNullOrWhiteSpace(txtField.Text) && System.IO.File.Exists(txtField.Text.Trim());
|
||||
lblXray.Text = ok ? "конфиг выбран" : "не выбран"; lblXray.ForeColor = ok ? OkC : RedC;
|
||||
btnXray.Text = "Обзор..."; btnXray.Width = 96; btnXray.Left = 300;
|
||||
}
|
||||
|
||||
// ---- helpers ----
|
||||
@@ -216,13 +233,15 @@ namespace ClaudeProxy
|
||||
}
|
||||
|
||||
// Mode can't be switched while the lock is active (ON) or during an operation.
|
||||
void UpdateModeLock() { rbProxy.Enabled = rbVless.Enabled = !switchOn && !busy; }
|
||||
void UpdateModeLock() { rbProxy.Enabled = rbVless.Enabled = rbAwg.Enabled = !switchOn && !busy; }
|
||||
|
||||
Config CaptureConfig()
|
||||
{
|
||||
var c = Core.LoadConfig();
|
||||
c.Mode = Mode; c.FailClosed = chkFail.Checked;
|
||||
if (Mode == "vless") c.Vless = txtField.Text; else c.Proxy = txtField.Text;
|
||||
if (Mode == "vless") c.Vless = txtField.Text;
|
||||
else if (Mode == "awg") c.Awg = txtField.Text;
|
||||
else c.Proxy = txtField.Text;
|
||||
Core.SaveConfig(c); return c;
|
||||
}
|
||||
|
||||
@@ -246,13 +265,15 @@ namespace ClaudeProxy
|
||||
|
||||
if (checkReach)
|
||||
{
|
||||
bool ok; string ep;
|
||||
if (Mode == "vless") ok = Vless.ServerReachable(txtField.Text, out ep);
|
||||
else { string h; int p; if (Core.TryParseProxy(txtField.Text, out h, out p)) { ep = h + ":" + p; ok = Core.TcpReachable(h, p, 1500); } else { ep = "некорректный адрес"; ok = false; } }
|
||||
bool ok; string text;
|
||||
if (Mode == "vless") { string ep; ok = Vless.ServerReachable(txtField.Text, out ep); text = (ok ? "сервер доступен " : "не отвечает ") + ep; }
|
||||
else if (Mode == "awg") { string ep; ok = Core.AwgConfigInfo(txtField.Text, out ep); text = ok ? ("конфиг: " + ep) : "конфиг не выбран"; }
|
||||
else { string h; int p; if (Core.TryParseProxy(txtField.Text, out h, out p)) { ok = Core.TcpReachable(h, p, 1500); text = (ok ? "сервер доступен " : "не отвечает ") + h + ":" + p; } else { ok = false; text = "некорректный адрес"; } }
|
||||
rDot.ForeColor = ok ? OkC : RedC;
|
||||
lblReach.Text = (ok ? "сервер доступен " : "не отвечает ") + ep;
|
||||
lblReach.Text = text;
|
||||
}
|
||||
if (Mode == "vless") UpdateXrayStatus();
|
||||
else if (Mode == "awg") UpdateAwgStatus();
|
||||
}
|
||||
catch (Exception ex) { Log("ошибка обновления: " + ex.Message); }
|
||||
}
|
||||
@@ -275,6 +296,18 @@ namespace ClaudeProxy
|
||||
string ep; if (!Vless.ServerReachable(txtField.Text, out ep))
|
||||
if (MessageBox.Show("Сервер " + ep + " не отвечает. Всё равно продолжить?", "Сервер недоступен", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes) return false;
|
||||
}
|
||||
else if (Mode == "awg")
|
||||
{
|
||||
if (string.IsNullOrWhiteSpace(txtField.Text) || !System.IO.File.Exists(txtField.Text.Trim()))
|
||||
{ MessageBox.Show("Выбери файл AmneziaWG .conf (кнопка «Обзор...»).", "Нужен конфиг", MessageBoxButtons.OK, MessageBoxIcon.Warning); return false; }
|
||||
if (!Core.AwgPresent())
|
||||
{
|
||||
SetBusy(true);
|
||||
try { Core.EnsureAwg(); UpdateAwgStatus(); }
|
||||
catch (Exception ex) { Log("ОШИБКА AmneziaWG: " + ex.Message); MessageBox.Show(ex.Message, "Ошибка", MessageBoxButtons.OK, MessageBoxIcon.Error); SetBusy(false); return false; }
|
||||
SetBusy(false);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
string h; int p;
|
||||
@@ -291,10 +324,11 @@ namespace ClaudeProxy
|
||||
var cfg = Core.LoadConfig();
|
||||
string proxyArg;
|
||||
if (cfg.Mode == "vless") { Core.EnsureXray(); Core.StartXray(cfg.Vless, cfg.LocalPort); proxyArg = "socks5://127.0.0.1:" + cfg.LocalPort; }
|
||||
else if (cfg.Mode == "awg") { Core.EnsureAwg(); Core.StartAwg(cfg.Awg, cfg.LocalPort); proxyArg = "socks5://127.0.0.1:" + cfg.LocalPort; }
|
||||
else proxyArg = Core.ProxyArg(cfg.Proxy);
|
||||
|
||||
if (cfg.FailClosed)
|
||||
if (!Core.ElevateSelf("fwapply")) { if (cfg.Mode == "vless") Core.StopXray(); throw new Exception("Установка firewall отменена в UAC."); }
|
||||
if (!Core.ElevateSelf("fwapply")) { Core.StopXray(); Core.StopAwg(); throw new Exception("Установка firewall отменена в UAC."); }
|
||||
|
||||
if (restart) { Core.StopClaude(); string a = "--proxy-server=" + proxyArg; Core.LaunchClaude(a); }
|
||||
}
|
||||
@@ -304,7 +338,7 @@ namespace ClaudeProxy
|
||||
string exe = null; try { exe = Core.GetClaudeApp().Exe; } catch { }
|
||||
if (Core.FirewallState(exe) != "absent")
|
||||
if (!Core.ElevateSelf("fwremove")) throw new Exception("Снятие firewall отменено в UAC (лок остался).");
|
||||
Core.StopXray();
|
||||
Core.StopXray(); Core.StopAwg();
|
||||
if (restart) { Core.StopClaude(); Core.LaunchClaude(""); }
|
||||
}
|
||||
|
||||
@@ -368,6 +402,7 @@ namespace ClaudeProxy
|
||||
try
|
||||
{
|
||||
if (Mode == "vless") { string h, u, rm; int p; Vless.Parse(txtField.Text, out h, out p, out u, out rm); CaptureConfig(); Log("VLESS сохранён: " + h + ":" + p + " [" + rm + "]"); }
|
||||
else if (Mode == "awg") { if (string.IsNullOrWhiteSpace(txtField.Text) || !System.IO.File.Exists(txtField.Text.Trim())) throw new Exception("Файл AmneziaWG-конфига не найден."); CaptureConfig(); Log("AmneziaWG-конфиг сохранён: " + txtField.Text.Trim()); }
|
||||
else { string h; int p; if (!Core.TryParseProxy(txtField.Text, out h, out p)) throw new Exception("Некорректный адрес."); CaptureConfig(); Log("прокси сохранён: " + Core.ProxyArg(txtField.Text)); }
|
||||
if (switchOn) Log("ВНИМАНИЕ: режим активен со старой настройкой — переключи рубильник, чтобы применить.");
|
||||
UpdateUi(true);
|
||||
@@ -387,5 +422,20 @@ namespace ClaudeProxy
|
||||
catch (Exception ex) { Log("ОШИБКА: " + ex.Message); MessageBox.Show(ex.Message, "Ошибка Xray", MessageBoxButtons.OK, MessageBoxIcon.Error); }
|
||||
finally { SetBusy(false); }
|
||||
}
|
||||
|
||||
void OnBrowseAwg()
|
||||
{
|
||||
using (var dlg = new OpenFileDialog())
|
||||
{
|
||||
dlg.Title = "Выбери AmneziaWG .conf";
|
||||
dlg.Filter = "AmneziaWG config (*.conf)|*.conf|Все файлы (*.*)|*.*";
|
||||
if (dlg.ShowDialog() != DialogResult.OK) return;
|
||||
txtField.Text = dlg.FileName;
|
||||
CaptureConfig();
|
||||
UpdateAwgStatus();
|
||||
Log("конфиг выбран: " + dlg.FileName);
|
||||
UpdateUi(true);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+19
-14
@@ -10,9 +10,10 @@ namespace ClaudeProxy
|
||||
{
|
||||
class Config
|
||||
{
|
||||
public string Mode = "proxy"; // proxy | vless
|
||||
public string Mode = "proxy"; // proxy | vless | awg
|
||||
public string Proxy = Core.DefaultProxy;
|
||||
public string Vless = "";
|
||||
public string Awg = ""; // path to the user's AmneziaWG .conf
|
||||
public int LocalPort = 10808;
|
||||
public bool FailClosed = true;
|
||||
}
|
||||
@@ -163,24 +164,27 @@ namespace ClaudeProxy
|
||||
catch { }
|
||||
return set;
|
||||
}
|
||||
static HashSet<int> XrayPids()
|
||||
static HashSet<int> ProcPids(string name)
|
||||
{
|
||||
var set = new HashSet<int>();
|
||||
foreach (var p in Process.GetProcessesByName("xray"))
|
||||
foreach (var p in Process.GetProcessesByName(name))
|
||||
try { if (p.MainModule.FileName.StartsWith(Core.BinDir, StringComparison.OrdinalIgnoreCase)) set.Add(p.Id); }
|
||||
catch { }
|
||||
return set;
|
||||
}
|
||||
static HashSet<int> XrayPids() { return ProcPids("xray"); }
|
||||
static HashSet<int> AwgPids() { return ProcPids("wireproxy-awg"); }
|
||||
|
||||
public static VerifyResult Run(Config cfg)
|
||||
{
|
||||
var r = new VerifyResult { Mode = cfg.Mode };
|
||||
HashSet<string> proxySet;
|
||||
bool loopIsProxy;
|
||||
if (cfg.Mode == "vless")
|
||||
if (cfg.Mode == "vless" || cfg.Mode == "awg")
|
||||
{
|
||||
proxySet = new HashSet<string> { "127.0.0.1", "::1" };
|
||||
r.Endpoint = "127.0.0.1:" + cfg.LocalPort + " (VLESS+Reality)";
|
||||
string lbl = cfg.Mode == "awg" ? "AmneziaWG" : "VLESS+Reality";
|
||||
r.Endpoint = "127.0.0.1:" + cfg.LocalPort + " (" + lbl + ")";
|
||||
loopIsProxy = true;
|
||||
}
|
||||
else
|
||||
@@ -205,22 +209,23 @@ namespace ClaudeProxy
|
||||
r.Leaks.Add(entry);
|
||||
}
|
||||
|
||||
if (cfg.Mode == "vless")
|
||||
if (cfg.Mode == "vless" || cfg.Mode == "awg")
|
||||
{
|
||||
var xp = XrayPids();
|
||||
if (xp.Count == 0) r.Tunnel = "xray НЕ запущен";
|
||||
string proc = cfg.Mode == "awg" ? "wireproxy-awg" : "xray";
|
||||
var tp = cfg.Mode == "awg" ? AwgPids() : XrayPids();
|
||||
if (tp.Count == 0) r.Tunnel = proc + " НЕ запущен";
|
||||
else
|
||||
{
|
||||
var xc = TcpConns(xp).FirstOrDefault(c => c.State == "Established" && c.Remote != "127.0.0.1" && c.Remote != "::1");
|
||||
r.Tunnel = xc != null ? "xray -> " + xc.Remote + ":" + xc.RemotePort : "xray запущен, внешнего коннекта пока нет";
|
||||
var xc = TcpConns(tp).FirstOrDefault(c => c.State == "Established" && c.Remote != "127.0.0.1" && c.Remote != "::1");
|
||||
r.Tunnel = xc != null ? proc + " -> " + xc.Remote + ":" + xc.RemotePort : proc + " запущен, внешнего коннекта пока нет";
|
||||
}
|
||||
}
|
||||
|
||||
bool tunnelDown = cfg.Mode == "vless" && r.Tunnel == "xray НЕ запущен";
|
||||
string modeLabel = cfg.Mode == "vless" ? "VLESS+Reality" : "прямой прокси";
|
||||
bool tunnelDown = (cfg.Mode == "vless" || cfg.Mode == "awg") && r.Tunnel.EndsWith("НЕ запущен");
|
||||
string modeLabel = cfg.Mode == "awg" ? "AmneziaWG" : (cfg.Mode == "vless" ? "VLESS+Reality" : "прямой прокси");
|
||||
r.Lines.Add("Проверка трафика Claude (" + modeLabel + ")");
|
||||
r.Lines.Add(" вход : " + r.Endpoint);
|
||||
if (cfg.Mode == "vless") r.Lines.Add(" тоннель : " + r.Tunnel);
|
||||
if (cfg.Mode == "vless" || cfg.Mode == "awg") r.Lines.Add(" тоннель : " + r.Tunnel);
|
||||
r.Lines.Add(" процессов Claude: " + r.Procs);
|
||||
r.Lines.Add(" через прокси : " + r.ViaProxy.Count);
|
||||
r.Lines.Add(" локальные : " + r.Local.Count);
|
||||
@@ -233,7 +238,7 @@ namespace ClaudeProxy
|
||||
else if (r.Leaks.Count > 0) r.Verdict = "ПРОВАЛ: есть прямой трафик мимо прокси";
|
||||
else if (tunnelDown) r.Verdict = "ПРОВАЛ: тоннель (xray) не запущен";
|
||||
else if (r.ViaProxy.Count == 0) r.Verdict = "НЕОПРЕДЕЛЁННО: нет активных коннектов (подожди загрузки Claude)";
|
||||
else if (cfg.Mode == "vless") r.Verdict = "OK: весь трафик Claude идёт в VLESS+Reality тоннель";
|
||||
else if (cfg.Mode == "vless" || cfg.Mode == "awg") r.Verdict = "OK: весь трафик Claude идёт в тоннель (" + modeLabel + ")";
|
||||
else r.Verdict = "OK: весь внешний трафик Claude идёт через прокси";
|
||||
r.Lines.Add(" ВЕРДИКТ : " + r.Verdict);
|
||||
r.Ok = r.Leaks.Count == 0 && r.ViaProxy.Count > 0 && !tunnelDown;
|
||||
|
||||
Reference in New Issue
Block a user