diff --git a/CHANGELOG.md b/CHANGELOG.md index ede7301..fda24c1 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,6 +5,24 @@ Versions follow [semver](https://semver.org/): MAJOR.MINOR.PATCH --- +## [3.3.6] — 2026-10-08 + +### Fixed +- **VPN service notification icon.** The ongoing notification now uses the + bundled `notification_icon` drawable instead of `notification_status_icon`, + and the vendored runtime resolves that drawable by name at runtime (falling + back to the platform icon only if it is missing), so the foreground-service + notification no longer shows the generic Android info icon. +- **"Test Local SOCKS" no longer claims an outbound it never tested.** Xray + answers a SOCKS `CONNECT` before it dials upstream, so trusting the `0x00` + reply reported success for unroutable addresses and for hostnames that can + never resolve — meaning the test said OK while the server was down, which is + exactly when it gets run. It now forces a request/response round trip through + the tunnel and reports distinctly when the outbound never replies, drops the + connection, or returns unexpected data. + +--- + ## [3.3.5] — 2026-09-06 ### Added diff --git a/lib/logic/local_socks_tester.dart b/lib/logic/local_socks_tester.dart index b672e88..52a2af8 100644 --- a/lib/logic/local_socks_tester.dart +++ b/lib/logic/local_socks_tester.dart @@ -86,15 +86,22 @@ abstract final class LocalSocksTester { } } - /// Full user-facing test: authenticated local SOCKS handshake plus an - /// outbound TCP CONNECT through the active ReVolt route. + /// Full user-facing test: authenticated local SOCKS handshake, an outbound + /// CONNECT through the active ReVolt route, and — critically — a real + /// request/response exchange over that CONNECT. + /// + /// The exchange is not optional politeness. Xray answers a SOCKS CONNECT + /// before it dials upstream, so the reply code alone says nothing about the + /// outbound: it returns success for unroutable addresses and for hostnames + /// that can never resolve. Without a round trip this test reports OK while + /// the server is down, which is precisely when a user runs it. static Future test({ String host = '127.0.0.1', required int port, required String username, required String password, String targetHost = 'example.com', - int targetPort = 443, + int targetPort = 80, }) async { const totalTimeout = Duration(seconds: 8); final stopwatch = Stopwatch()..start(); @@ -171,6 +178,43 @@ abstract final class LocalSocksTester { } await _consumeAddress(reader, replyHead[3], timeout: remaining()); + + // The CONNECT reply above is not evidence of an outbound. Force a round + // trip and require the peer's answer before claiming the route works. + socket.add( + utf8.encode( + 'GET / HTTP/1.1\r\nHost: $targetHost\r\nConnection: close\r\n\r\n', + ), + ); + await socket.flush(); + + List banner; + try { + banner = await reader.readExactly(5, timeout: remaining()); + } on TimeoutException { + return const LocalSocksTestResult( + ok: false, + latencyMs: null, + message: 'Local SOCKS5 is up, but the ReVolt outbound never replied.', + ); + } on SocketException { + return const LocalSocksTestResult( + ok: false, + latencyMs: null, + message: + 'Local SOCKS5 is up, but the ReVolt outbound dropped the connection.', + ); + } + + if (String.fromCharCodes(banner) != 'HTTP/') { + return const LocalSocksTestResult( + ok: false, + latencyMs: null, + message: + 'Local SOCKS5 is up, but the ReVolt outbound returned unexpected data.', + ); + } + stopwatch.stop(); return LocalSocksTestResult( ok: true, diff --git a/pubspec.yaml b/pubspec.yaml index ab53812..52a3186 100644 --- a/pubspec.yaml +++ b/pubspec.yaml @@ -15,7 +15,7 @@ publish_to: "none" # Remove this line if you wish to publish to pub.dev # In iOS, build-name is used as CFBundleShortVersionString while build-number used as CFBundleVersion. # Read more about iOS versioning at # https://developer.apple.com/library/archive/documentation/General/Reference/InfoPlistKeyReference/Articles/CoreFoundationKeys.html -version: 3.3.5+31 +version: 3.3.6+32 environment: sdk: ">=3.0.0 <4.0.0"