diff --git a/README.md b/README.md index 45aff76..1fa5783 100644 --- a/README.md +++ b/README.md @@ -243,6 +243,9 @@ https://evilfox.win/ Некоторые AV (Bkav, Microsoft Wacapew/Wacatac, Ikarus Trojan.WinGo.Agent, Google, Trapmine) часто помечают **любые** неподписанные Go-бинарники с сетью и сменой системного прокси. +В 2.6.2: +- быстрое отключение AWG: принудительно закрываются прокси-соединения, UI больше не зависает. + В 2.6.1: - AmneziaWG: перед подключением Endpoint-хост резолвится в IP (предпочтительно IPv4) — обход сбоя IpcSet на Windows («No such host is known»). diff --git a/build-macos.bat b/build-macos.bat index 24ed0cf..bfd17b3 100644 --- a/build-macos.bat +++ b/build-macos.bat @@ -28,11 +28,11 @@ if errorlevel 1 exit /b 1 go build -o "tools\packmac\packmac.exe" .\tools\packmac if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-arm64\Navis" -out "dist\navis-release\darwin-arm64" -version 2.6.1 -arch arm64 +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-arm64\Navis" -out "dist\navis-release\darwin-arm64" -version 2.6.2 -arch arm64 if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-amd64\Navis" -out "dist\navis-release\darwin-amd64" -version 2.6.1 -arch amd64 +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-amd64\Navis" -out "dist\navis-release\darwin-amd64" -version 2.6.2 -arch amd64 if errorlevel 1 exit /b 1 -tools\packmac\packmac.exe -bin "dist\navis-release\darwin-universal\Navis" -out "dist\navis-release\darwin-universal" -version 2.6.1 -arch universal +tools\packmac\packmac.exe -bin "dist\navis-release\darwin-universal\Navis" -out "dist\navis-release\darwin-universal" -version 2.6.2 -arch universal if errorlevel 1 exit /b 1 echo Built: diff --git a/cmd/vpnapp/main_windows.go b/cmd/vpnapp/main_windows.go index 37c394d..ad3de2e 100644 --- a/cmd/vpnapp/main_windows.go +++ b/cmd/vpnapp/main_windows.go @@ -296,8 +296,7 @@ func (a *app) connectProfile(name string) error { } func (a *app) disconnect() error { - a.mu.Lock() - defer a.mu.Unlock() + // Do not hold a.mu across engine teardown — getState polling would freeze the UI. return a.mgr.Disconnect() } diff --git a/dist/navis-release/Navis.exe b/dist/navis-release/Navis.exe index 9618cfe..27d1a8b 100644 Binary files a/dist/navis-release/Navis.exe and b/dist/navis-release/Navis.exe differ diff --git a/dist/navis-release/darwin-amd64/Navis b/dist/navis-release/darwin-amd64/Navis index e522eda..a54f163 100644 Binary files a/dist/navis-release/darwin-amd64/Navis and b/dist/navis-release/darwin-amd64/Navis differ diff --git a/dist/navis-release/darwin-amd64/Navis.app.zip b/dist/navis-release/darwin-amd64/Navis.app.zip index 326265a..8b68f0c 100644 Binary files a/dist/navis-release/darwin-amd64/Navis.app.zip and b/dist/navis-release/darwin-amd64/Navis.app.zip differ diff --git a/dist/navis-release/darwin-amd64/Navis.dmg b/dist/navis-release/darwin-amd64/Navis.dmg index 2d4f06e..9194eab 100644 Binary files a/dist/navis-release/darwin-amd64/Navis.dmg and b/dist/navis-release/darwin-amd64/Navis.dmg differ diff --git a/dist/navis-release/darwin-arm64/Navis b/dist/navis-release/darwin-arm64/Navis index da13ab5..9555aa2 100644 Binary files a/dist/navis-release/darwin-arm64/Navis and b/dist/navis-release/darwin-arm64/Navis differ diff --git a/dist/navis-release/darwin-arm64/Navis.app.zip b/dist/navis-release/darwin-arm64/Navis.app.zip index d02074f..6231d1d 100644 Binary files a/dist/navis-release/darwin-arm64/Navis.app.zip and b/dist/navis-release/darwin-arm64/Navis.app.zip differ diff --git a/dist/navis-release/darwin-arm64/Navis.dmg b/dist/navis-release/darwin-arm64/Navis.dmg index db39d9b..3cfdc52 100644 Binary files a/dist/navis-release/darwin-arm64/Navis.dmg and b/dist/navis-release/darwin-arm64/Navis.dmg differ diff --git a/dist/navis-release/darwin-universal/Navis b/dist/navis-release/darwin-universal/Navis index 9922bc2..0067d27 100644 Binary files a/dist/navis-release/darwin-universal/Navis and b/dist/navis-release/darwin-universal/Navis differ diff --git a/dist/navis-release/darwin-universal/Navis.app.zip b/dist/navis-release/darwin-universal/Navis.app.zip index a1ed74d..be11e64 100644 Binary files a/dist/navis-release/darwin-universal/Navis.app.zip and b/dist/navis-release/darwin-universal/Navis.app.zip differ diff --git a/dist/navis-release/darwin-universal/Navis.dmg b/dist/navis-release/darwin-universal/Navis.dmg index e623d65..b352f3e 100644 Binary files a/dist/navis-release/darwin-universal/Navis.dmg and b/dist/navis-release/darwin-universal/Navis.dmg differ diff --git a/dist/navis-release/update.json b/dist/navis-release/update.json index 1403363..67a31f1 100644 --- a/dist/navis-release/update.json +++ b/dist/navis-release/update.json @@ -1,22 +1,22 @@ { - "version": "2.6.1", - "notes": "AWG: любой hostname Endpoint резолвится в IP до подключения (фикс No such host is known)", + "version": "2.6.2", + "notes": "Быстрое отключение AWG без зависания UI («приложение не отвечает»)", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "08f001a3850ca5bd4f73b7ba4d21e8664ee9fc8120a87877f35d8f20a5ed6993", + "sha256": "14fe9afb7978433a811662175ed21e9f65e66420c105ef943ca6f11aeeada072", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "08f001a3850ca5bd4f73b7ba4d21e8664ee9fc8120a87877f35d8f20a5ed6993", + "sha256": "14fe9afb7978433a811662175ed21e9f65e66420c105ef943ca6f11aeeada072", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "b67e8b3fb5db8b97f85dba4c0d307c137c67fb6b56b36f8b2a7adf327ff226d4", + "sha256": "497cdf66b74e28061622debbbc6fa922ae64ae532e867dec876188af15e85278", "os": "darwin", "arch": "arm64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", @@ -24,7 +24,7 @@ }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "37385c52cb397f60acc699adecb0ffc1843106d50288b1304393daf63083b4b7", + "sha256": "95ccb649116bd15cbcdddccdd57df77651bf5f41ec19a4c6a0e91f8139621df7", "os": "darwin", "arch": "amd64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg", @@ -32,7 +32,7 @@ }, "darwin-universal": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis", - "sha256": "ce250733cc7f5ed50b42f5cc106be9872b057966e7a73bcee78974d85748c2fb", + "sha256": "abdb4d5807145dfab7afa1fc9fd4d0382a4f5d9b4ce7621128b2b393c7f0c928", "os": "darwin", "arch": "universal", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis.dmg", diff --git a/dist/update.json b/dist/update.json index 1403363..67a31f1 100644 --- a/dist/update.json +++ b/dist/update.json @@ -1,22 +1,22 @@ { - "version": "2.6.1", - "notes": "AWG: любой hostname Endpoint резолвится в IP до подключения (фикс No such host is known)", + "version": "2.6.2", + "notes": "Быстрое отключение AWG без зависания UI («приложение не отвечает»)", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "08f001a3850ca5bd4f73b7ba4d21e8664ee9fc8120a87877f35d8f20a5ed6993", + "sha256": "14fe9afb7978433a811662175ed21e9f65e66420c105ef943ca6f11aeeada072", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "08f001a3850ca5bd4f73b7ba4d21e8664ee9fc8120a87877f35d8f20a5ed6993", + "sha256": "14fe9afb7978433a811662175ed21e9f65e66420c105ef943ca6f11aeeada072", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "b67e8b3fb5db8b97f85dba4c0d307c137c67fb6b56b36f8b2a7adf327ff226d4", + "sha256": "497cdf66b74e28061622debbbc6fa922ae64ae532e867dec876188af15e85278", "os": "darwin", "arch": "arm64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", @@ -24,7 +24,7 @@ }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "37385c52cb397f60acc699adecb0ffc1843106d50288b1304393daf63083b4b7", + "sha256": "95ccb649116bd15cbcdddccdd57df77651bf5f41ec19a4c6a0e91f8139621df7", "os": "darwin", "arch": "amd64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg", @@ -32,7 +32,7 @@ }, "darwin-universal": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis", - "sha256": "ce250733cc7f5ed50b42f5cc106be9872b057966e7a73bcee78974d85748c2fb", + "sha256": "abdb4d5807145dfab7afa1fc9fd4d0382a4f5d9b4ce7621128b2b393c7f0c928", "os": "darwin", "arch": "universal", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis.dmg", diff --git a/internal/appui/index.html b/internal/appui/index.html index 35f83a9..c7016a0 100644 --- a/internal/appui/index.html +++ b/internal/appui/index.html @@ -725,7 +725,7 @@

Navis

- 2.6.1 + 2.6.2

Быстрый клиент · Naive · Hy2 · AWG · Xray

diff --git a/internal/core/manager.go b/internal/core/manager.go index d5fdd4e..67bbe5e 100644 --- a/internal/core/manager.go +++ b/internal/core/manager.go @@ -100,21 +100,33 @@ func (m *Manager) Connect(ctx context.Context, profileName string) error { func (m *Manager) Disconnect() error { m.mu.Lock() - defer m.mu.Unlock() + sys := m.sys + engine := m.engine + m.engine = nil + m.profile = nil + m.mu.Unlock() var first error - if m.sys.Enabled() { - if err := m.sys.Disable(); err != nil && first == nil { + if sys != nil && sys.Enabled() { + if err := sys.Disable(); err != nil && first == nil { first = err } } - if m.engine != nil { - if err := m.engine.Stop(); err != nil && first == nil { - first = err + if engine != nil { + done := make(chan error, 1) + go func() { done <- engine.Stop() }() + select { + case err := <-done: + if err != nil && first == nil { + first = err + } + case <-time.After(3 * time.Second): + // Engine Stop can block on userspace relays; don't freeze the UI. + if first == nil { + first = fmt.Errorf("отключение заняло слишком много времени") + } } - m.engine = nil } - m.profile = nil return first } diff --git a/internal/protocols/awg/engine.go b/internal/protocols/awg/engine.go index 12efa94..988eee8 100644 --- a/internal/protocols/awg/engine.go +++ b/internal/protocols/awg/engine.go @@ -120,15 +120,16 @@ func (e *Engine) stopLocked() error { if !e.running { return nil } - if e.proxies != nil { - _ = e.proxies.Close() - e.proxies = nil - } + // Close device first so tun/netstack dials fail and relays exit promptly. if e.dev != nil { e.dev.Close() e.dev = nil } e.tnet = nil + if e.proxies != nil { + _ = e.proxies.Close() + e.proxies = nil + } e.running = false return nil } diff --git a/internal/protocols/awg/proxy.go b/internal/protocols/awg/proxy.go index 8c2afc8..69c8098 100644 --- a/internal/protocols/awg/proxy.go +++ b/internal/protocols/awg/proxy.go @@ -17,18 +17,22 @@ import ( type DialFunc func(ctx context.Context, network, address string) (net.Conn, error) type proxyServer struct { - mu sync.Mutex - socksLn net.Listener - httpLn net.Listener - dial DialFunc - wg sync.WaitGroup - closed bool + mu sync.Mutex + socksLn net.Listener + httpLn net.Listener + dial DialFunc + wg sync.WaitGroup + closed bool socksAddr string httpAddr string + conns map[net.Conn]struct{} } func startProxies(socksHostPort, httpHostPort string, dial DialFunc) (*proxyServer, error) { - s := &proxyServer{dial: dial} + s := &proxyServer{ + dial: dial, + conns: make(map[net.Conn]struct{}), + } if socksHostPort != "" { ln, err := net.Listen("tcp", socksHostPort) if err != nil { @@ -53,6 +57,22 @@ func startProxies(socksHostPort, httpHostPort string, dial DialFunc) (*proxyServ return s, nil } +func (s *proxyServer) track(c net.Conn) { + s.mu.Lock() + defer s.mu.Unlock() + if s.closed { + _ = c.Close() + return + } + s.conns[c] = struct{}{} +} + +func (s *proxyServer) untrack(c net.Conn) { + s.mu.Lock() + defer s.mu.Unlock() + delete(s.conns, c) +} + func (s *proxyServer) Close() error { s.mu.Lock() if s.closed { @@ -66,8 +86,23 @@ func (s *proxyServer) Close() error { if s.httpLn != nil { _ = s.httpLn.Close() } + // Force-close active relays so io.Copy / dial wake up immediately. + for c := range s.conns { + _ = c.Close() + } + s.conns = nil s.mu.Unlock() - s.wg.Wait() + + done := make(chan struct{}) + go func() { + s.wg.Wait() + close(done) + }() + select { + case <-done: + case <-time.After(1500 * time.Millisecond): + // Don't hang UI / Disconnect on stuck netstack copies. + } return nil } @@ -81,6 +116,8 @@ func (s *proxyServer) serveSOCKS() { s.wg.Add(1) go func(conn net.Conn) { defer s.wg.Done() + s.track(conn) + defer s.untrack(conn) _ = handleSOCKS(conn, s.dial) }(c) } @@ -96,6 +133,8 @@ func (s *proxyServer) serveHTTP() { s.wg.Add(1) go func(conn net.Conn) { defer s.wg.Done() + s.track(conn) + defer s.untrack(conn) _ = handleHTTPProxy(conn, s.dial) }(c) } @@ -156,7 +195,7 @@ func handleSOCKS(client net.Conn, dial DialFunc) error { port := binary.BigEndian.Uint16(buf[:2]) _ = client.SetDeadline(time.Time{}) - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() remote, err := dial(ctx, "tcp", net.JoinHostPort(host, fmt.Sprintf("%d", port))) if err != nil { @@ -185,7 +224,7 @@ func handleHTTPProxy(client net.Conn, dial DialFunc) error { if !strings.Contains(host, ":") { host += ":443" } - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() remote, err := dial(ctx, "tcp", host) if err != nil { @@ -212,7 +251,7 @@ func handleHTTPProxy(client net.Conn, dial DialFunc) error { if !strings.Contains(host, ":") { host += ":80" } - ctx, cancel := context.WithTimeout(context.Background(), 30*time.Second) + ctx, cancel := context.WithTimeout(context.Background(), 15*time.Second) defer cancel() remote, err := dial(ctx, "tcp", host) if err != nil { diff --git a/internal/update/update.go b/internal/update/update.go index ad1dd66..665495c 100644 --- a/internal/update/update.go +++ b/internal/update/update.go @@ -17,7 +17,7 @@ import ( ) // CurrentVersion is the shipped client version. -const CurrentVersion = "2.6.1" +const CurrentVersion = "2.6.2" // DefaultManifestURL is the update feed (hosted in the project git repo). const DefaultManifestURL = "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/update.json" diff --git a/server/update.json b/server/update.json index 1403363..67a31f1 100644 --- a/server/update.json +++ b/server/update.json @@ -1,22 +1,22 @@ { - "version": "2.6.1", - "notes": "AWG: любой hostname Endpoint резолвится в IP до подключения (фикс No such host is known)", + "version": "2.6.2", + "notes": "Быстрое отключение AWG без зависания UI («приложение не отвечает»)", "platform": "windows-amd64", "os": "windows", "arch": "amd64", "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "08f001a3850ca5bd4f73b7ba4d21e8664ee9fc8120a87877f35d8f20a5ed6993", + "sha256": "14fe9afb7978433a811662175ed21e9f65e66420c105ef943ca6f11aeeada072", "mandatory": false, "platforms": { "windows-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/Navis.exe", - "sha256": "08f001a3850ca5bd4f73b7ba4d21e8664ee9fc8120a87877f35d8f20a5ed6993", + "sha256": "14fe9afb7978433a811662175ed21e9f65e66420c105ef943ca6f11aeeada072", "os": "windows", "arch": "amd64" }, "darwin-arm64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis", - "sha256": "b67e8b3fb5db8b97f85dba4c0d307c137c67fb6b56b36f8b2a7adf327ff226d4", + "sha256": "497cdf66b74e28061622debbbc6fa922ae64ae532e867dec876188af15e85278", "os": "darwin", "arch": "arm64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-arm64/Navis.dmg", @@ -24,7 +24,7 @@ }, "darwin-amd64": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis", - "sha256": "37385c52cb397f60acc699adecb0ffc1843106d50288b1304393daf63083b4b7", + "sha256": "95ccb649116bd15cbcdddccdd57df77651bf5f41ec19a4c6a0e91f8139621df7", "os": "darwin", "arch": "amd64", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-amd64/Navis.dmg", @@ -32,7 +32,7 @@ }, "darwin-universal": { "url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis", - "sha256": "ce250733cc7f5ed50b42f5cc106be9872b057966e7a73bcee78974d85748c2fb", + "sha256": "abdb4d5807145dfab7afa1fc9fd4d0382a4f5d9b4ce7621128b2b393c7f0c928", "os": "darwin", "arch": "universal", "dmg_url": "https://git.evilfox.cc/test2/navi/raw/branch/main/dist/navis-release/darwin-universal/Navis.dmg", diff --git a/versioninfo.json b/versioninfo.json index f4a2ba2..fd639be 100644 --- a/versioninfo.json +++ b/versioninfo.json @@ -1,7 +1,7 @@ { "FixedFileInfo": { - "FileVersion": { "Major": 2, "Minor": 6, "Patch": 1, "Build": 0 }, - "ProductVersion": { "Major": 2, "Minor": 6, "Patch": 1, "Build": 0 }, + "FileVersion": { "Major": 2, "Minor": 6, "Patch": 2, "Build": 0 }, + "ProductVersion": { "Major": 2, "Minor": 6, "Patch": 2, "Build": 0 }, "FileFlagsMask": "3f", "FileFlags": "00", "FileOS": "40004", @@ -11,12 +11,12 @@ "StringFileInfo": { "CompanyName": "EvilFox", "FileDescription": "Navis 2 — VPN client (Naive / Hy2 / AWG / VLESS / VMess / Trojan)", - "FileVersion": "2.6.1.0", + "FileVersion": "2.6.2.0", "InternalName": "Navis", "LegalCopyright": "Copyright (c) EvilFox", "OriginalFilename": "Navis.exe", "ProductName": "Navis", - "ProductVersion": "2.6.1.0", + "ProductVersion": "2.6.2.0", "Comments": "Open-source VPN/proxy client. https://evilfox.win/" }, "VarFileInfo": {