Compare commits
1 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 39b58e1a77 |
@@ -5,6 +5,9 @@
|
|||||||
xray.zip
|
xray.zip
|
||||||
xray-embed.b64
|
xray-embed.b64
|
||||||
|
|
||||||
|
# wireproxy-awg binary (built from pinned source by build-wireproxy.ps1) - not stored in git
|
||||||
|
wireproxy-awg.exe
|
||||||
|
|
||||||
# preview / debug artifacts
|
# preview / debug artifacts
|
||||||
*-preview.png
|
*-preview.png
|
||||||
preview.png
|
preview.png
|
||||||
|
|||||||
@@ -13,3 +13,17 @@ This project bundles an **unmodified** binary of [Xray-core](https://github.com/
|
|||||||
Bundling and invoking Xray-core as an independent executable ("mere aggregation") does not place
|
Bundling and invoking Xray-core as an independent executable ("mere aggregation") does not place
|
||||||
this project's own code (MIT-licensed, see `LICENSE`) under the MPL. Redistribution of the Xray
|
this project's own code (MIT-licensed, see `LICENSE`) under the MPL. Redistribution of the Xray
|
||||||
binary is permitted by MPL-2.0 provided its license text is included, which it is.
|
binary is permitted by MPL-2.0 provided its license text is included, which it is.
|
||||||
|
|
||||||
|
## wireproxy-awg (AmneziaWG mode)
|
||||||
|
|
||||||
|
The AmneziaWG mode bundles **wireproxy-awg**, built from source and run as a separate process. It
|
||||||
|
is a thin fork of `wireproxy` that uses the official AmneziaWG userspace implementation.
|
||||||
|
|
||||||
|
- Built from a pinned, audited commit — see `build-wireproxy.ps1` (repo + commit hash).
|
||||||
|
- `wireproxy` / `wireproxy-awg`: **ISC** license (© Tsz Fung Wong).
|
||||||
|
- `github.com/amnezia-vpn/amneziawg-go` and `golang.zx2c4.com/wireguard`: **MIT** license.
|
||||||
|
- All run as independent executables ("mere aggregation"); this project's code stays MIT.
|
||||||
|
|
||||||
|
Reproducible build: `build-wireproxy.ps1` fetches a portable Go toolchain, checks out the pinned
|
||||||
|
commit, and compiles a static `wireproxy-awg.exe`. It is embedded as a resource by
|
||||||
|
`build-native.ps1` and is **not** stored in git.
|
||||||
|
|||||||
@@ -48,6 +48,20 @@ firewall-правила (при вкл/выкл рубильника).
|
|||||||
|
|
||||||
Парсинг ссылки: Reality (pbk/sni/sid/spx/fp/flow) и транспорты tcp/grpc/ws.
|
Парсинг ссылки: Reality (pbk/sni/sid/spx/fp/flow) и транспорты tcp/grpc/ws.
|
||||||
|
|
||||||
|
## Режим AmneziaWG
|
||||||
|
|
||||||
|
Третий режим — **AmneziaWG** (WireGuard с обфускацией против DPI). Тоже userspace, без TUN/драйвера/
|
||||||
|
админа: встроенный `wireproxy-awg` поднимает локальный SOCKS5 поверх AmneziaWG-туннеля, Claude идёт
|
||||||
|
в него (fail-closed как в VLESS).
|
||||||
|
|
||||||
|
1. Переключи режим на **AmneziaWG**.
|
||||||
|
2. Кнопка **«Обзор...»** → выбери свой `.conf` от AmneziaWG (экспорт из приложения Amnezia или
|
||||||
|
с сервера — с обфускацией Jc/Jmin/Jmax/S1/S2/H1–H4). «Сохранить».
|
||||||
|
3. Рубильник **ВКЛ** — бинарь распакуется из встроенного, поднимется туннель, Claude уйдёт в него.
|
||||||
|
|
||||||
|
`wireproxy-awg` собирается **из исходников** (`build-wireproxy.ps1`) из зафиксированного,
|
||||||
|
проаудированного коммита (форк известного `wireproxy` с официальным `amnezia-vpn/amneziawg-go`).
|
||||||
|
|
||||||
## Требования
|
## Требования
|
||||||
|
|
||||||
- Windows 10/11 (нужен встроенный .NET Framework 4.x — есть из коробки).
|
- Windows 10/11 (нужен встроенный .NET Framework 4.x — есть из коробки).
|
||||||
@@ -63,8 +77,12 @@ powershell -ExecutionPolicy Bypass -File build-native.ps1
|
|||||||
```
|
```
|
||||||
|
|
||||||
Компилит через `csc` из .NET Framework (`C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe`,
|
Компилит через `csc` из .NET Framework (`C:\Windows\Microsoft.NET\Framework64\v4.0.30319\csc.exe`,
|
||||||
есть на любой Windows — SDK не нужен). При первой сборке скачивает Xray-core (пинованную версию,
|
есть на любой Windows — SDK не нужен). При первой сборке:
|
||||||
с проверкой SHA256) и встраивает как ресурс; иконку генерит `make-icon.ps1`; манифест — asInvoker.
|
- скачивает **Xray-core** (пинованную версию, с проверкой SHA256);
|
||||||
|
- собирает **wireproxy-awg** из исходников (`build-wireproxy.ps1`: портативный Go + пиновый коммит) —
|
||||||
|
нужны `git` и интернет;
|
||||||
|
- оба бинаря встраивает как ресурсы; иконку генерит `make-icon.ps1`; манифест — asInvoker.
|
||||||
|
|
||||||
Результат — `dist\ClaudeProxy.exe`.
|
Результат — `dist\ClaudeProxy.exe`.
|
||||||
|
|
||||||
Исходники (`native/`):
|
Исходники (`native/`):
|
||||||
|
|||||||
+10
-1
@@ -34,6 +34,14 @@ if (-not (Test-Path $xrayZip)) {
|
|||||||
Write-Host " Xray SHA256 ok." -ForegroundColor DarkGray
|
Write-Host " Xray SHA256 ok." -ForegroundColor DarkGray
|
||||||
}
|
}
|
||||||
|
|
||||||
|
# wireproxy-awg (AmneziaWG userspace SOCKS). Built from pinned source; not stored in git.
|
||||||
|
$awgExe = Join-Path $root 'wireproxy-awg.exe'
|
||||||
|
if (-not (Test-Path $awgExe)) {
|
||||||
|
Write-Host "Building wireproxy-awg.exe (needs git; downloads a portable Go toolchain)..." -ForegroundColor Cyan
|
||||||
|
& powershell.exe -NoProfile -ExecutionPolicy Bypass -File (Join-Path $root 'build-wireproxy.ps1')
|
||||||
|
if (-not (Test-Path $awgExe)) { throw "wireproxy-awg.exe build failed." }
|
||||||
|
}
|
||||||
|
|
||||||
$out = Join-Path $dist 'ClaudeProxy.exe'
|
$out = Join-Path $dist 'ClaudeProxy.exe'
|
||||||
$refs = @('System.dll', 'System.Core.dll', 'System.Windows.Forms.dll', 'System.Drawing.dll',
|
$refs = @('System.dll', 'System.Core.dll', 'System.Windows.Forms.dll', 'System.Drawing.dll',
|
||||||
'System.IO.Compression.dll', 'System.IO.Compression.FileSystem.dll', 'Microsoft.CSharp.dll')
|
'System.IO.Compression.dll', 'System.IO.Compression.FileSystem.dll', 'Microsoft.CSharp.dll')
|
||||||
@@ -42,7 +50,8 @@ $args = @(
|
|||||||
'/nologo', '/target:winexe', '/platform:x64', "/out:$out",
|
'/nologo', '/target:winexe', '/platform:x64', "/out:$out",
|
||||||
"/win32icon:$(Join-Path $root 'icon.ico')",
|
"/win32icon:$(Join-Path $root 'icon.ico')",
|
||||||
"/win32manifest:$(Join-Path $root 'native\app.manifest')",
|
"/win32manifest:$(Join-Path $root 'native\app.manifest')",
|
||||||
"/resource:$(Join-Path $root 'xray.zip'),ClaudeProxy.xray.zip"
|
"/resource:$(Join-Path $root 'xray.zip'),ClaudeProxy.xray.zip",
|
||||||
|
"/resource:$(Join-Path $root 'wireproxy-awg.exe'),ClaudeProxy.awg.exe"
|
||||||
)
|
)
|
||||||
foreach ($r in $refs) { $args += "/reference:$r" }
|
foreach ($r in $refs) { $args += "/reference:$r" }
|
||||||
$args += (Join-Path $root 'native\Core.cs')
|
$args += (Join-Path $root 'native\Core.cs')
|
||||||
|
|||||||
@@ -0,0 +1,55 @@
|
|||||||
|
<#
|
||||||
|
build-wireproxy.ps1 - reproducibly build wireproxy-awg.exe from a pinned, audited commit.
|
||||||
|
Output: wireproxy-awg.exe in the repo root (embedded into ClaudeProxy.exe by build-native.ps1).
|
||||||
|
Uses a portable Go toolchain in a temp cache (does not touch the system Go).
|
||||||
|
#>
|
||||||
|
$ErrorActionPreference = 'Stop'
|
||||||
|
$root = Split-Path -Parent $MyInvocation.MyCommand.Path
|
||||||
|
$out = Join-Path $root 'wireproxy-awg.exe'
|
||||||
|
|
||||||
|
# pinned, audited source
|
||||||
|
$repo = 'https://github.com/artem-russkikh/wireproxy-awg.git'
|
||||||
|
$commit = '5d12c1937a72591342f1c78bc92a8e50e7ee92bb' # v1.0.17
|
||||||
|
$goVer = 'go1.26.5'
|
||||||
|
|
||||||
|
$cache = Join-Path $env:TEMP 'claude-proxy-gobuild'
|
||||||
|
New-Item -ItemType Directory -Force -Path $cache | Out-Null
|
||||||
|
[System.Net.ServicePointManager]::SecurityProtocol = [System.Net.SecurityProtocolType]::Tls12
|
||||||
|
|
||||||
|
# portable Go
|
||||||
|
$goExe = Join-Path $cache 'goroot\go\bin\go.exe'
|
||||||
|
if (-not (Test-Path $goExe)) {
|
||||||
|
Write-Host "Fetching portable $goVer..." -ForegroundColor Cyan
|
||||||
|
$zip = Join-Path $cache 'go.zip'
|
||||||
|
Invoke-WebRequest "https://go.dev/dl/$goVer.windows-amd64.zip" -OutFile $zip -UseBasicParsing
|
||||||
|
Expand-Archive $zip -DestinationPath (Join-Path $cache 'goroot') -Force
|
||||||
|
Remove-Item $zip
|
||||||
|
}
|
||||||
|
|
||||||
|
# fetch the exact pinned commit
|
||||||
|
$src = Join-Path $cache 'wireproxy-awg'
|
||||||
|
if (Test-Path $src) { Remove-Item $src -Recurse -Force }
|
||||||
|
New-Item -ItemType Directory -Force -Path $src | Out-Null
|
||||||
|
Push-Location $src
|
||||||
|
try {
|
||||||
|
git init -q
|
||||||
|
git remote add origin $repo
|
||||||
|
git fetch -q --depth 1 origin $commit
|
||||||
|
git checkout -q FETCH_HEAD
|
||||||
|
$head = (git rev-parse HEAD).Trim()
|
||||||
|
if ($head -ne $commit) { throw "commit mismatch: got $head, expected $commit" }
|
||||||
|
Write-Host "Source pinned at $commit" -ForegroundColor DarkGray
|
||||||
|
|
||||||
|
$env:GOROOT = Join-Path $cache 'goroot\go'
|
||||||
|
$env:GOPATH = Join-Path $cache 'gopath'
|
||||||
|
$env:GOCACHE = Join-Path $cache 'gocache'
|
||||||
|
$env:GOTOOLCHAIN = 'local'
|
||||||
|
$env:GOOS = 'windows'; $env:GOARCH = 'amd64'; $env:CGO_ENABLED = '0'
|
||||||
|
Write-Host "Building wireproxy-awg.exe..." -ForegroundColor Cyan
|
||||||
|
& $goExe build -trimpath -ldflags '-s -w' -o $out ./cmd/wireproxy
|
||||||
|
if ($LASTEXITCODE -ne 0) { throw "go build failed ($LASTEXITCODE)" }
|
||||||
|
}
|
||||||
|
finally { Pop-Location }
|
||||||
|
|
||||||
|
$sz = [math]::Round((Get-Item $out).Length / 1MB, 1)
|
||||||
|
Write-Host "OK: $out ($sz MB)" -ForegroundColor Green
|
||||||
+74
-1
@@ -89,6 +89,7 @@ namespace ClaudeProxy
|
|||||||
case "mode": c.Mode = v.Trim(); break;
|
case "mode": c.Mode = v.Trim(); break;
|
||||||
case "proxy": c.Proxy = v.Trim(); break;
|
case "proxy": c.Proxy = v.Trim(); break;
|
||||||
case "vless": c.Vless = v; 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 "localPort": int.TryParse(v.Trim(), out c.LocalPort); break;
|
||||||
case "failClosed": c.FailClosed = v.Trim() == "1" || v.Trim().ToLower() == "true"; 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("mode=" + c.Mode);
|
||||||
sb.AppendLine("proxy=" + c.Proxy);
|
sb.AppendLine("proxy=" + c.Proxy);
|
||||||
sb.AppendLine("vless=" + (c.Vless ?? ""));
|
sb.AppendLine("vless=" + (c.Vless ?? ""));
|
||||||
|
sb.AppendLine("awg=" + (c.Awg ?? ""));
|
||||||
sb.AppendLine("localPort=" + c.LocalPort);
|
sb.AppendLine("localPort=" + c.LocalPort);
|
||||||
sb.AppendLine("failClosed=" + (c.FailClosed ? "1" : "0"));
|
sb.AppendLine("failClosed=" + (c.FailClosed ? "1" : "0"));
|
||||||
File.WriteAllText(ConfigPath, sb.ToString());
|
File.WriteAllText(ConfigPath, sb.ToString());
|
||||||
@@ -271,7 +273,7 @@ namespace ClaudeProxy
|
|||||||
var app = GetClaudeApp();
|
var app = GetClaudeApp();
|
||||||
RemoveFirewall();
|
RemoveFirewall();
|
||||||
dynamic policy = FwPolicy();
|
dynamic policy = FwPolicy();
|
||||||
if (cfg.Mode == "vless")
|
if (cfg.Mode == "vless" || cfg.Mode == "awg")
|
||||||
{
|
{
|
||||||
// tunnel: block ALL external (loopback is exempt from firewall filtering)
|
// tunnel: block ALL external (loopback is exempt from firewall filtering)
|
||||||
AddBlockRule(policy, "ClaudeProxyLock: tunnel", app.Exe, null);
|
AddBlockRule(policy, "ClaudeProxyLock: tunnel", app.Exe, null);
|
||||||
@@ -389,5 +391,76 @@ namespace ClaudeProxy
|
|||||||
|
|
||||||
public static bool XrayRunning() { return Process.GetProcessesByName("xray").Any(p =>
|
public static bool XrayRunning() { return Process.GetProcessesByName("xray").Any(p =>
|
||||||
{ try { return p.MainModule.FileName.StartsWith(BinDir, StringComparison.OrdinalIgnoreCase); } catch { return false; } }); }
|
{ 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);
|
fPill = new Font("Segoe UI Semibold", 12f), fMono = new Font("Consolas", 8.5f);
|
||||||
|
|
||||||
bool switchOn = false, busy = false;
|
bool switchOn = false, busy = false;
|
||||||
RadioButton rbProxy, rbVless;
|
RadioButton rbProxy, rbVless, rbAwg;
|
||||||
BufferedPanel mark, tgl, card;
|
BufferedPanel mark, tgl, card;
|
||||||
Label title, subtitle, lblFieldCap, lblReach, rDot, lblFwCap, lblFw, lblClCap, lblCl, lblXrayCap, lblXray;
|
Label title, subtitle, lblFieldCap, lblReach, rDot, lblFwCap, lblFw, lblClCap, lblCl, lblXrayCap, lblXray;
|
||||||
TextBox txtField, log;
|
TextBox txtField, log;
|
||||||
@@ -32,7 +32,7 @@ namespace ClaudeProxy
|
|||||||
Panel sep;
|
Panel sep;
|
||||||
Timer timer;
|
Timer timer;
|
||||||
|
|
||||||
string Mode { get { return rbVless.Checked ? "vless" : "proxy"; } }
|
string Mode { get { return rbAwg.Checked ? "awg" : (rbVless.Checked ? "vless" : "proxy"); } }
|
||||||
|
|
||||||
public MainForm()
|
public MainForm()
|
||||||
{
|
{
|
||||||
@@ -53,9 +53,10 @@ namespace ClaudeProxy
|
|||||||
subtitle = MkLabel("весь трафик Claude через прокси", fSub, Muted, 84, 48);
|
subtitle = MkLabel("весь трафик Claude через прокси", fSub, Muted, 84, 48);
|
||||||
subtitle.AutoSize = false; subtitle.Size = new Size(196, 18); Controls.Add(subtitle);
|
subtitle.AutoSize = false; subtitle.Size = new Size(196, 18); Controls.Add(subtitle);
|
||||||
|
|
||||||
rbProxy = MkRadio("Прямой прокси", 292, 18);
|
rbProxy = MkRadio("Прямой прокси", 292, 14);
|
||||||
rbVless = MkRadio("VLESS + Reality", 292, 42);
|
rbVless = MkRadio("VLESS + Reality", 292, 36);
|
||||||
Controls.Add(rbProxy); Controls.Add(rbVless);
|
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 = 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);
|
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);
|
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);
|
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);
|
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);
|
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 };
|
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);
|
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 = new Timer { Interval = 4000 };
|
||||||
timer.Tick += (s, e) => { if (!busy) UpdateUi(false); };
|
timer.Tick += (s, e) => { if (!busy) UpdateUi(false); };
|
||||||
@@ -111,7 +113,7 @@ namespace ClaudeProxy
|
|||||||
Load += (s, e) => {
|
Load += (s, e) => {
|
||||||
var cfg = Core.LoadConfig();
|
var cfg = Core.LoadConfig();
|
||||||
chkFail.Checked = cfg.FailClosed;
|
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();
|
ApplyModeLayout();
|
||||||
Log("готово. рубильник управляет проксированием Claude.");
|
Log("готово. рубильник управляет проксированием Claude.");
|
||||||
UpdateUi(true);
|
UpdateUi(true);
|
||||||
@@ -182,20 +184,27 @@ namespace ClaudeProxy
|
|||||||
void ApplyModeLayout()
|
void ApplyModeLayout()
|
||||||
{
|
{
|
||||||
var cfg = Core.LoadConfig();
|
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")
|
if (Mode == "vless")
|
||||||
{
|
{
|
||||||
lblFieldCap.Text = "VLESS"; subtitle.Text = "через VLESS + Reality тоннель";
|
lblFieldCap.Text = "VLESS"; subtitle.Text = "через VLESS + Reality тоннель";
|
||||||
txtField.UseSystemPasswordChar = true; txtField.Text = cfg.Vless ?? "";
|
txtField.UseSystemPasswordChar = true; txtField.Text = cfg.Vless ?? "";
|
||||||
lblXrayCap.Visible = lblXray.Visible = btnXray.Visible = true;
|
lblXrayCap.Text = "Xray-core"; UpdateXrayStatus();
|
||||||
sep.Top = 100; lblFwCap.Top = lblFw.Top = 112; lblClCap.Top = lblCl.Top = 134;
|
}
|
||||||
UpdateXrayStatus();
|
else if (Mode == "awg")
|
||||||
|
{
|
||||||
|
lblFieldCap.Text = "Конфиг"; subtitle.Text = "через AmneziaWG тоннель";
|
||||||
|
txtField.UseSystemPasswordChar = false; txtField.Text = cfg.Awg ?? "";
|
||||||
|
lblXrayCap.Text = "AmneziaWG"; UpdateAwgStatus();
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
lblFieldCap.Text = "Прокси"; subtitle.Text = "весь трафик Claude через прокси";
|
lblFieldCap.Text = "Прокси"; subtitle.Text = "весь трафик Claude через прокси";
|
||||||
txtField.UseSystemPasswordChar = false; txtField.Text = cfg.Proxy ?? "";
|
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 = "Переустановить"; }
|
if (Core.XrayPresent()) { lblXray.Text = "готов (" + Core.XrayVersion + ")"; lblXray.ForeColor = OkC; btnXray.Text = "Переустановить"; }
|
||||||
else { 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 ----
|
// ---- helpers ----
|
||||||
@@ -216,13 +233,15 @@ namespace ClaudeProxy
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Mode can't be switched while the lock is active (ON) or during an operation.
|
// 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()
|
Config CaptureConfig()
|
||||||
{
|
{
|
||||||
var c = Core.LoadConfig();
|
var c = Core.LoadConfig();
|
||||||
c.Mode = Mode; c.FailClosed = chkFail.Checked;
|
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;
|
Core.SaveConfig(c); return c;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -246,13 +265,15 @@ namespace ClaudeProxy
|
|||||||
|
|
||||||
if (checkReach)
|
if (checkReach)
|
||||||
{
|
{
|
||||||
bool ok; string ep;
|
bool ok; string text;
|
||||||
if (Mode == "vless") ok = Vless.ServerReachable(txtField.Text, out ep);
|
if (Mode == "vless") { string ep; ok = Vless.ServerReachable(txtField.Text, out ep); text = (ok ? "сервер доступен " : "не отвечает ") + 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; } }
|
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;
|
rDot.ForeColor = ok ? OkC : RedC;
|
||||||
lblReach.Text = (ok ? "сервер доступен " : "не отвечает ") + ep;
|
lblReach.Text = text;
|
||||||
}
|
}
|
||||||
if (Mode == "vless") UpdateXrayStatus();
|
if (Mode == "vless") UpdateXrayStatus();
|
||||||
|
else if (Mode == "awg") UpdateAwgStatus();
|
||||||
}
|
}
|
||||||
catch (Exception ex) { Log("ошибка обновления: " + ex.Message); }
|
catch (Exception ex) { Log("ошибка обновления: " + ex.Message); }
|
||||||
}
|
}
|
||||||
@@ -275,6 +296,18 @@ namespace ClaudeProxy
|
|||||||
string ep; if (!Vless.ServerReachable(txtField.Text, out ep))
|
string ep; if (!Vless.ServerReachable(txtField.Text, out ep))
|
||||||
if (MessageBox.Show("Сервер " + ep + " не отвечает. Всё равно продолжить?", "Сервер недоступен", MessageBoxButtons.YesNo, MessageBoxIcon.Warning) != DialogResult.Yes) return false;
|
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
|
else
|
||||||
{
|
{
|
||||||
string h; int p;
|
string h; int p;
|
||||||
@@ -291,10 +324,11 @@ namespace ClaudeProxy
|
|||||||
var cfg = Core.LoadConfig();
|
var cfg = Core.LoadConfig();
|
||||||
string proxyArg;
|
string proxyArg;
|
||||||
if (cfg.Mode == "vless") { Core.EnsureXray(); Core.StartXray(cfg.Vless, cfg.LocalPort); proxyArg = "socks5://127.0.0.1:" + cfg.LocalPort; }
|
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);
|
else proxyArg = Core.ProxyArg(cfg.Proxy);
|
||||||
|
|
||||||
if (cfg.FailClosed)
|
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); }
|
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 { }
|
string exe = null; try { exe = Core.GetClaudeApp().Exe; } catch { }
|
||||||
if (Core.FirewallState(exe) != "absent")
|
if (Core.FirewallState(exe) != "absent")
|
||||||
if (!Core.ElevateSelf("fwremove")) throw new Exception("Снятие firewall отменено в UAC (лок остался).");
|
if (!Core.ElevateSelf("fwremove")) throw new Exception("Снятие firewall отменено в UAC (лок остался).");
|
||||||
Core.StopXray();
|
Core.StopXray(); Core.StopAwg();
|
||||||
if (restart) { Core.StopClaude(); Core.LaunchClaude(""); }
|
if (restart) { Core.StopClaude(); Core.LaunchClaude(""); }
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -368,6 +402,7 @@ namespace ClaudeProxy
|
|||||||
try
|
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 + "]"); }
|
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)); }
|
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("ВНИМАНИЕ: режим активен со старой настройкой — переключи рубильник, чтобы применить.");
|
if (switchOn) Log("ВНИМАНИЕ: режим активен со старой настройкой — переключи рубильник, чтобы применить.");
|
||||||
UpdateUi(true);
|
UpdateUi(true);
|
||||||
@@ -387,5 +422,20 @@ namespace ClaudeProxy
|
|||||||
catch (Exception ex) { Log("ОШИБКА: " + ex.Message); MessageBox.Show(ex.Message, "Ошибка Xray", MessageBoxButtons.OK, MessageBoxIcon.Error); }
|
catch (Exception ex) { Log("ОШИБКА: " + ex.Message); MessageBox.Show(ex.Message, "Ошибка Xray", MessageBoxButtons.OK, MessageBoxIcon.Error); }
|
||||||
finally { SetBusy(false); }
|
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
|
class Config
|
||||||
{
|
{
|
||||||
public string Mode = "proxy"; // proxy | vless
|
public string Mode = "proxy"; // proxy | vless | awg
|
||||||
public string Proxy = Core.DefaultProxy;
|
public string Proxy = Core.DefaultProxy;
|
||||||
public string Vless = "";
|
public string Vless = "";
|
||||||
|
public string Awg = ""; // path to the user's AmneziaWG .conf
|
||||||
public int LocalPort = 10808;
|
public int LocalPort = 10808;
|
||||||
public bool FailClosed = true;
|
public bool FailClosed = true;
|
||||||
}
|
}
|
||||||
@@ -163,24 +164,27 @@ namespace ClaudeProxy
|
|||||||
catch { }
|
catch { }
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
static HashSet<int> XrayPids()
|
static HashSet<int> ProcPids(string name)
|
||||||
{
|
{
|
||||||
var set = new HashSet<int>();
|
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); }
|
try { if (p.MainModule.FileName.StartsWith(Core.BinDir, StringComparison.OrdinalIgnoreCase)) set.Add(p.Id); }
|
||||||
catch { }
|
catch { }
|
||||||
return set;
|
return set;
|
||||||
}
|
}
|
||||||
|
static HashSet<int> XrayPids() { return ProcPids("xray"); }
|
||||||
|
static HashSet<int> AwgPids() { return ProcPids("wireproxy-awg"); }
|
||||||
|
|
||||||
public static VerifyResult Run(Config cfg)
|
public static VerifyResult Run(Config cfg)
|
||||||
{
|
{
|
||||||
var r = new VerifyResult { Mode = cfg.Mode };
|
var r = new VerifyResult { Mode = cfg.Mode };
|
||||||
HashSet<string> proxySet;
|
HashSet<string> proxySet;
|
||||||
bool loopIsProxy;
|
bool loopIsProxy;
|
||||||
if (cfg.Mode == "vless")
|
if (cfg.Mode == "vless" || cfg.Mode == "awg")
|
||||||
{
|
{
|
||||||
proxySet = new HashSet<string> { "127.0.0.1", "::1" };
|
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;
|
loopIsProxy = true;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
@@ -205,22 +209,23 @@ namespace ClaudeProxy
|
|||||||
r.Leaks.Add(entry);
|
r.Leaks.Add(entry);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (cfg.Mode == "vless")
|
if (cfg.Mode == "vless" || cfg.Mode == "awg")
|
||||||
{
|
{
|
||||||
var xp = XrayPids();
|
string proc = cfg.Mode == "awg" ? "wireproxy-awg" : "xray";
|
||||||
if (xp.Count == 0) r.Tunnel = "xray НЕ запущен";
|
var tp = cfg.Mode == "awg" ? AwgPids() : XrayPids();
|
||||||
|
if (tp.Count == 0) r.Tunnel = proc + " НЕ запущен";
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
var xc = TcpConns(xp).FirstOrDefault(c => c.State == "Established" && c.Remote != "127.0.0.1" && c.Remote != "::1");
|
var xc = TcpConns(tp).FirstOrDefault(c => c.State == "Established" && c.Remote != "127.0.0.1" && c.Remote != "::1");
|
||||||
r.Tunnel = xc != null ? "xray -> " + xc.Remote + ":" + xc.RemotePort : "xray запущен, внешнего коннекта пока нет";
|
r.Tunnel = xc != null ? proc + " -> " + xc.Remote + ":" + xc.RemotePort : proc + " запущен, внешнего коннекта пока нет";
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
bool tunnelDown = cfg.Mode == "vless" && r.Tunnel == "xray НЕ запущен";
|
bool tunnelDown = (cfg.Mode == "vless" || cfg.Mode == "awg") && r.Tunnel.EndsWith("НЕ запущен");
|
||||||
string modeLabel = cfg.Mode == "vless" ? "VLESS+Reality" : "прямой прокси";
|
string modeLabel = cfg.Mode == "awg" ? "AmneziaWG" : (cfg.Mode == "vless" ? "VLESS+Reality" : "прямой прокси");
|
||||||
r.Lines.Add("Проверка трафика Claude (" + modeLabel + ")");
|
r.Lines.Add("Проверка трафика Claude (" + modeLabel + ")");
|
||||||
r.Lines.Add(" вход : " + r.Endpoint);
|
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(" процессов Claude: " + r.Procs);
|
||||||
r.Lines.Add(" через прокси : " + r.ViaProxy.Count);
|
r.Lines.Add(" через прокси : " + r.ViaProxy.Count);
|
||||||
r.Lines.Add(" локальные : " + r.Local.Count);
|
r.Lines.Add(" локальные : " + r.Local.Count);
|
||||||
@@ -233,7 +238,7 @@ namespace ClaudeProxy
|
|||||||
else if (r.Leaks.Count > 0) r.Verdict = "ПРОВАЛ: есть прямой трафик мимо прокси";
|
else if (r.Leaks.Count > 0) r.Verdict = "ПРОВАЛ: есть прямой трафик мимо прокси";
|
||||||
else if (tunnelDown) r.Verdict = "ПРОВАЛ: тоннель (xray) не запущен";
|
else if (tunnelDown) r.Verdict = "ПРОВАЛ: тоннель (xray) не запущен";
|
||||||
else if (r.ViaProxy.Count == 0) r.Verdict = "НЕОПРЕДЕЛЁННО: нет активных коннектов (подожди загрузки Claude)";
|
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 идёт через прокси";
|
else r.Verdict = "OK: весь внешний трафик Claude идёт через прокси";
|
||||||
r.Lines.Add(" ВЕРДИКТ : " + r.Verdict);
|
r.Lines.Add(" ВЕРДИКТ : " + r.Verdict);
|
||||||
r.Ok = r.Leaks.Count == 0 && r.ViaProxy.Count > 0 && !tunnelDown;
|
r.Ok = r.Leaks.Count == 0 && r.ViaProxy.Count > 0 && !tunnelDown;
|
||||||
|
|||||||
Reference in New Issue
Block a user