Repository navigation
Releases: tokyoxpa3/5G-Proxy-Client
Release list
v1.6.6
- Fix: IPv6 link-local detection (fe80::/10) now compares the numeric value of the first hextet instead of a textual prefix, so an address such as fe8::1 is no longer misread as link-local and dropped — it is really 0fe8::, which is outside fe80::/10 (over-filtering reported in the F-Droid 1.6.5 review; the existing test had asserted the buggy behaviour)
- Fix: when Wi-Fi and mobile data are both up with different DNS servers, re-resolution after a network change only ever worked for the first one — the rest waited out a 3-second timeout. The query socket was routed around the VPN but never bound to the network that owns that DNS server, so every query took the system default route. It is now bound to its owning network (a failed bind only logs a warning and keeps the default route — not silent, not fatal; the configured 8.8.8.8 / 1.1.1.1 are public resolvers and stay unbound)
- Diagnostics: each re-resolution now logs the packet's actual egress address (egress=), so "bound correctly", "never bound" and "swallowed by our own tunnel" no longer look identical in the log
繁體中文
- 修正:IPv6 連結本地位址(fe80::/10)的判定改為比對第一個 hextet 的數值,不再把 fe8::1 這類省略前導零的位址誤判成連結本地而丟掉 —— 它實際是 0fe8::,並不在 fe80::/10 之內(F-Droid 1.6.5 審閱指出的過度過濾;原有測試還把這個錯誤行為寫成了斷言)
- 修正:裝置同時有 Wi-Fi 與行動網路、且兩者的 DNS 不同時,換網後的重新解析只有排第一台查得到,後面那台會白等一個 3 秒逾時。原因是查詢 socket 只做了 VPN 繞行、沒有綁回該 DNS 所屬的網路,封包一律走系統預設路由。現在會綁回它來源的網路(綁定失敗只記警告並沿用預設路由,不靜默、不致命;設定值 8.8.8.8 / 1.1.1.1 屬公開解析器,維持不綁)
- 診斷:每次重新解析都會記錄查詢封包的實際出口位址(egress=),「綁對/沒綁/被自己的隧道吃掉」三種情況不再在記錄裡長得一模一樣
Signed APK attached below (reproducible F-Droid build).
v1.6.5
- Fix: after a network change, the DNS servers used to re-resolve the proxy server's hostname were the tunnel's own advertised servers (8.8.8.8 / 1.1.1.1 by default), not the DNS of the network the tunnel actually runs over. The intended "fall back to the local network's DHCP DNS" step had never taken effect, because it read the active network — which, for a VPN app, is its own VPN network. The underlying network's resolver is now tried first, so the hostname is not handed to a third-party resolver unless the local one gives no answer (and that fallback is logged)
- The launcher icon is now the brand mark used on the store page, plus a themed monochrome layer for Android 13+
繁體中文
- 修正:換網後重新解析代理伺服器主機名時,用的 DNS 其實是「隧道自己宣告的那兩個」(預設 8.8.8.8 / 1.1.1.1),而不是隧道實際走的那條網路的 DNS。原本那句「後備用本網路的 DHCP DNS」從來沒有生效過 —— 因為它讀的是 active network,而對 VPN App 而言那就是自己的 VPN 網路。現在會先問底層網路自己的解析器;除非它沒回應,否則主機名不會被送給第三方解析器(一旦用到後備會寫進記錄)
- App 啟動器圖示改為商店頁面上的品牌標誌,並補上 Android 13+ 的主題化單色圖層
Signed APK attached below (reproducible F-Droid build).
v1.6.4
- Fix: with Remote DNS on, toggling Wi-Fi or mobile data could kill DNS for the whole device until the tunnel was rebuilt, on a phone whose Android "Private DNS" is set to a hostname (DNS-over-TLS). The tunnel's fake-IP DNS was also answering the Private DNS endpoint's own hostname, so the encrypted-DNS endpoint became a fake address that died whenever the fake-IP table was reset. The Private DNS hostname is now resolved for real instead of being faked
- Fix: the tunnel could show "connected" while domain names failed to resolve, even though connections made straight to an IP address worked. When the engine tore a connection down it told the app with a TCP RST whose sequence number was hard-coded to 0, and a conforming stack discards such a RST (RFC 5961) — so the app kept using a connection that no longer existed until its own retransmission timeout (measured 3 min 38 s), and DNS was down the whole time. The RST now carries the correct sequence number, and a soft reconnect now sends one at all
- Fix: the tunnel advertised a 1 KB window even when its send buffer was completely full. The app read the resulting silence as packet loss and repeatedly collapsed its congestion window back to slow start, which is why uploads kept dropping to a crawl. It now honestly advertises a zero window and reopens it once the buffer drains
繁體中文
- 修正:開啟 Remote DNS 時,若裝置的 Android「私人 DNS」設為 hostname 模式(DNS-over-TLS),開關 Wi-Fi/行動數據後可能整台機的 DNS 全掛、要重建隧道才恢復。隧道的 fake IP DNS 連私人 DNS 端點自己的主機名也一併假造,於是加密 DNS 端點變成一個假位址,只要 fake 表被清就失效。現在私人 DNS 的主機名會走真實解析,不再被假造
- 修正:隧道可能顯示「已連線」,但網域名稱解析失敗、直接用 IP 連線卻正常。引擎拆掉連線時送給 App 的 TCP RST 序號被寫死為 0,而合乎規範的 TCP 堆疊會依 RFC 5961 丟棄這種 RST —— 於是 App 繼續使用一條早已不存在的連線,直到自己的重傳逾時(實測 3 分 38 秒),這段期間 DNS 全是斷的。現在 RST 會帶正確序號,且軟重連也會真的送出 RST
- 修正:引擎的傳送緩衝明明已滿,卻仍通告 1 KB 視窗。App 把隨之而來的沉默解讀成封包遺失,反覆把壅塞視窗砍回慢啟動,上傳因此一直掉回龜速。現在會誠實通告零視窗,並在緩衝排空後重新開啟
Signed APK attached below (reproducible F-Droid build).
Commits in this release
- chore(release): 1.6.4 (versionCode 17) (a353307)
- fix(tcp): 零視窗通告誠實回 0,不再謊報還有 1KB (1d830a7)
- fix(tcp): soft reset 要真的送 RST,且 seq 用 srv_next(RFC 5961) (9a86f10)
- fix(dns): 私人 DNS(DoT)主機名不再被 fake IP 攔截,換網不再全機 DNS 卡死 (f428eef)
- chore(tools): 加入 NDK clang -fsyntax-only 檢查腳本 (82f3527)
- chore(ci): 註解不再寫死 changelog 檔號,避免再次過期 (31cf51c)
- chore(tools): 自動化 fdroiddata metadata 版本更新與 MR 留言 (3b1665c)
v1.6.2
- Fix: with UDP-in-TCP enabled, Google search and other QUIC (HTTP/3) sites crawled. The client mistook a full receive buffer for a protocol violation and tore the UDP-in-TCP connection down, so every QUIC session was rebuilt moments after it started. A full buffer is now treated as back-pressure and the connection stays up
- Fix: the UDP-in-TCP receive buffer no longer shrinks as traffic flows, and was raised from 8 KB to 32 KB
- New: UDP-in-TCP disconnects now log the reason (peer closed, local send/recv error, epoll error) instead of failing silently
繁體中文
- 修正:開啟 UDP-in-TCP 後,Google 搜尋與其他走 QUIC(HTTP/3)的網站會明顯變卡。用戶端把「接收緩衝滿」誤判為協定違規並拆掉連線,導致每個 QUIC 會話剛建立就被重建。現在緩衝滿只視為背壓,連線不再被拆
- 修正:UDP-in-TCP 的接收緩衝不再隨流量單調縮小,並由 8 KB 提高到 32 KB
- 新增:UDP-in-TCP 斷線現在會記錄原因(對端關閉/本端收送錯誤/epoll 錯誤),不再靜默失敗
Signed APK attached below (reproducible F-Droid build).
v1.6.1
- New: "Same-device coexistence" switch on the main screen — one tap keeps 5G Proxy Pro outside the tunnel so Pro and Client can run on the same phone (off by default)
- New: allowlist mode coexists too — Pro is automatically locked outside the tunnel, and its row is dimmed and cannot be checked
- New: UDP Remote DNS now sends domain frames (ATYP=0x03), so QUIC and other UDP traffic is no longer dropped by IP-only servers
- Fix: the App list header was clipped by the status bar and ActionBar (most visible on Xiaomi/HyperOS); the screen now uses an ActionBar-less theme
- Fix: UDP-in-TCP fallback to standard UDP ASSOCIATE now opens a fresh connection and is classified as a protocol failure, so it no longer triggers auto-reconnect
- Fix: App list search now trims surrounding whitespace instead of returning zero results
繁體中文
- 新增:主頁「同機共存」開關 —— 一鍵把 5G Proxy Pro 留在隧道外,同一支手機同時跑 Pro 與 Client(預設不開啟)
- 新增:指定 App(白名單)模式也能同機共存 —— Pro 自動鎖在隧道外,清單中該列淡化且無法勾選
- 新增:UDP Remote DNS 以網域 frame(ATYP=0x03)送出,QUIC 等 UDP 流量不再被只認 IP 的伺服器丟棄
- 修正:App 清單頁頂部被狀態列與 ActionBar 遮擋(小米/HyperOS 上最明顯),改用無 ActionBar 主題
- 修正:UDP-in-TCP 退回標準 UDP ASSOCIATE 時改用新連線,且失敗歸類為協定層、不再誤觸發自動重連
- 修正:App 清單搜尋現在會忽略前後空白,不再因輸入法尾隨空白而顯示 0 筆
Signed APK attached below (reproducible F-Droid build).
Commits in this release
v1.6.0
What's Changed
- fix(app): isServerValid 改 isNotBlank 拒絕純空白 host (859dd5a)
- chore(app): bump version 1.6.0 (2179591)
- test(engine): 擴充端到端整合測試覆蓋 FIN/軟重連/認證/UDP-in-TCP/Remote DNS (fb7e39d)
- fix(engine): 修正 TCP FIN 半關閉誤發 RST 與 UDP-in-TCP 退回未吞 BND.ADDR (4a3ffe3)
- refactor(engine): udp_session 改名 udp_state 對齊 tcp_state 命名 (f84e0db)
- test(engine): 補 socks5_greet_auth 與 hs_pool 單元測試並擴充 check-symbols 守衛 (2168d27)
- refactor(engine): 拆分 TCP/UDP session 模組、handshake 執行緒池與共用 helper (a30178a)
- test(android): 新增 SecretCipher 儀器化測試與 androidTest 骨架 (8eba581)
- refactor(app): 抽離背景重試/表單驗證/App 過濾/KillSwitch 純邏輯並補測試 (9b5c439)
- refactor(engine): 抽離 SOCKS5 握手回覆分類與 ATYP 長度並補測試 (92ab002)
- fix(build): 補上 CMakeLists 遺漏的 tcp_buf.c 修正 Android 連結 (e161c6a)
- fix(app): 修復舊裝置 API 崩潰並清理 lint 技術債 (dbb7a04)
- fix(test): 修復 tun_loopback usleep 於 gcc 14 下的隱含宣告 (50ddd67)
- refactor(test): 抽離原生 C 測試統一 Makefile 入口並由 CI 呼叫 (469bd42)
- refactor(engine): 抽離 TCP 緩衝算術與收尾判定並補 golden/差分測試 (2d3e780)
- refactor(engine): 抽離 UDP-in-TCP frame 串流 parser 並納入 fuzz (51a5ef1)
- refactor(service): 空 catch 補 log 輸出靜默失敗 (6a95465)
- refactor(engine): 抽離 SOCKS5 greeting/RFC1929 認證共用 helper (fec21a6)
- refactor(engine): handle_relay_udp 改呼叫 socks5_parse_udp_datagram (08ea01b)
- refactor(engine): 抽離 TCP 進入封包分類器並補測試 (5d54802)
- refactor(engine): 抽離 ICMP/ICMPv6 回覆建構並納入 fuzz (e61bae5)
- refactor(build): 移除 CMAKE_C_FLAGS 多餘的 -latomic (d72ada2)
- refactor(engine): 抽離 DNS query 解析器並納入 fuzz (34c8f5b)
- docs: TUTORIAL.md 圖片改為內嵌顯示語法 (a4bfb5b)
- refactor(engine): 抽離 DNS-over-TCP frame 掃描並納入 fuzz (6dac656)
- refactor(engine): 抽離 fake DNS 表純邏輯並補 golden test (aa5cc83)
- docs: update tutorial + screenshots for Pro 1.6.3 / Client 1.5.1 (786a89f)
- fix(security): 匯出設定檔剔除明文憑證 + 封包解析器 fuzz 安全網 (c7ed7d3)
- feat(ipv6): 支援巢狀 extension header 的 fragment 重組 (eb6ddca)
- chore: 忽略 ZCode 工作區與暫存測試產物 (29fbcc1)
- fix(secret): 加密失敗不再降級明文,保留舊值避免機密明文落地 (b1c27c9)
- refactor(secret): 抽離 SecretCodec 純格式邏輯並收斂憑證寫入路徑 (eadb7ea)
- feat: 軟重連重新解析 hostname + 憑證 Keystore 加密 (ce74358)
- ci: bump upload-artifact to v5 (clear Node 20 deprecation) (5a6dc4e)
- ci: fix drift check (ip_hash_bucket) and bump actions to clear deprecation warnings (39b9507)
- ci: make release upload idempotent (re-runs won't fail on existing release) (b8ca290)
- ci: install NDK via sdkmanager (drop unsupported ndk-version input) (ba5a176)
- ci: add reproducible release workflow (Linux) for F-Droid (4d3cbaa)
Signed APK attached below (reproducible F-Droid build).
v1.5.1
release: 1.5.1 — LAN 繞過修復、UDP Fullcone 純邏輯抽取與 DNS client - tun: 路由改為只導公網單播(LanRoutes),私網/link-local/loopback 直連, 修復開啟隧道後無法連區網裝置與 adb over WiFi 卡死的問題 - engine: 抽取 UDP Fullcone 首包緩衝門檻與閒置逾時純函式(udp_session)並補測試 - 新增 DnsClient、LanRoutes 純邏輯模組與 JVM 單元測試 - ci.yml 納入 udp_session_test 與漂移檢查符號 - 升版 1.5.1(versionCode 12)
5G Proxy Client 1.5.0
- SOCKS5 錯誤分類浮上 UI:認證失敗、協定錯誤(非 SOCKS5 / REP≠0)不再與網路中斷混淆
- Kill Switch 自癒:Always-on VPN / lockdown 被系統撤銷時自動重連(Android 10+)
- 新增電池最佳化白名單引導
- 預設常數集中至 Config、CI 加入 lint 門禁
- 為 F-Droid 上架準備(可重現建置)
v1.4.1 核心引擎測試覆蓋補齊
v1.4.1 核心引擎測試覆蓋補齊
基於 v1.4.0,重點解決「核心引擎零覆蓋」風險,將最易回歸的 tun_socks.c 純邏輯抽離為可 host 編譯模組並補齊 golden 測試與 loopback 整合 harness,並在實機 192.168.1.192:44645 完成壓力驗證。
引擎重構 (tun_socks.c)
- 全域收攏
engine_ctx_t g:206:將g_tun_fd/g_running/g_epoll_fd/g_srv_*等 20+ 全域收至單一struct,engine_ctx_reset:2530啟動重置、tun_socks_start:2544/tun_socks_stop:2606生命週期清晰,為單元測試 harness 注入鋪路 - DNS-over-TCP 攔截:
tcp_sess_t.dns_tcp:97+dns_rx_buf:96+dns_tcp_emit:1702/dns_tcp_ingest:1724,Remote DNS 下port 53/tcp直接合成 fake 回覆,避免網域洩漏至上游 DNS - 資源釋放修復:
close_session_fds:1183/tcp_session_destroy:1416改僅release_java_socket不再close(fd),避免 Java 與 native 雙重 close 導致 fd 重用 UAF(同批次控制+relay 事件)— 已通過 100 併發驗證
測試覆蓋(效仿 checksum.c)
抽離 3 個 host 可編譯純模組(#include "checksum.h" 風格,無 android/log 依賴):
socks5_codec.{h,c}:socks5_build_udp_datagram:5/socks5_build_udp_frame:30/socks5_build_connect_request:47/socks5_parse_udp_datagram:88(ATYP 0x01/0x03/0x04)dns_synth.{h,c}:dns_build_reply_pure:13+dns_build_fake_ip6:5(fd00::5e:x),覆蓋 A/AAAA/HTTPS(65) 空答tcp_packet.{h,c}:tcp_build_segment:8/tcp_build_synack:54(MSS/WS)/udp_build_packet:102,含transport_checksum校驗
Golden tests(gcc -std=c11 -Wall -Wextra -I app/src/main/cpp):
checksum_test.c7 項 —0x126bRFC1071 /0x7e20UDPv4 /0x6875UDPv6 — PASSsocks5_test.c11 項 —hello/auth/CONNECT v4/v6/domain/UDP datagram/frame 00 14— PASSdns_synth_test.c9 項 —A C6120001/AAAA fd00::/HTTPS empty— PASStcp_packet_test.c7 組 —tcp 0x0fa1/synack 0xe54b/seq wraparound (int32_t)(a-b)>0/window 4096— PASStun_loopback_test.c— in-process 假 SOCKS5(HELLO 05 00/CONNECT 05 00 00 01echo /UDP ASSOCIATE 127.0.0.1:22222)+socketpair模擬 TUN,驗證tcp_build_synack seq 5000 ack 1001回環 — PASS(Windows 跳過 POSIX,Linux 全量)
建置:app/CMakeLists.txt:14 新增 3 源,ci.yml:47 擴充 native-unit-test 至 4 binaries,app/build.gradle:14 versionCode 8->9 v1.4.0->v1.4.1
壓力測試 (192.168.1.192:44645, SM-G9810, SD865, 192.168.1.178:1080)
- Monkey
500+200events throttle 120 — 100% 注入,無 ANR/tombstone for socksclient - TCP 併發
60× curl(example/hinet/google generate_204)—tun0 rx +44k tx +12k穩定,TunSocks僅正常tcp connect 完成/session 關閉 - DNS flood
30× dns_q.bin via nc -u+fakedns198.18.0.x— 100% 攔截 chk_testbad=0(IPhdr ae78),VmRSS 158MB PSS 67MB30s idle 無洩漏,FD 256→512 正常
APK 已以 release.keystore(storeFile=../release.keystore, apksigner verify v2 true)簽署,支援 arm64-v8a / armeabi-v7a / x86_64,max-page-size 16384 對齊(Android 15+ 16KB)。
Full Changelog: v1.4.0...v1.4.1
v1.4.0 性能與穩定性重大更新
v1.4.0 性能與穩定性重大更新
基於 v1.3.0,重點修復高併發與 IPv6 問題,並通過 SM-G9810 真機壓力測試。
引擎核心 (tun_socks.c)
- 握手線程池 (16 Worker):以固定
HS_POOL_WORKERS隊列取代 per-sessionpthread_create(DETACHED),避免突發 50-100 併發時線程爆炸。hs_submit()計數g_handshake_inflight,hs_pool_start/stop確定性排空 join。壓力測試 Threads 恆定 43-44,burst 100 99% 成功。 - IPv6 Extension Header 解析:實現 Hop-by-Hop(0)/Routing(43)/Destination(60)/AH(51) 鏈式解析,輸出
l4off,Fragment(44) 直接丟棄,修復含 ext header 封包被誤判丟棄問題。同步修復parse_ipv6/handle_tun_packet簽名。 - UDP Relay IPv6 支持:
relay_addr改為sockaddr_storage+socklen_t relay_len,支持 SOCKS5 ATYP 0x04 IPv6 relay (18B),sendto使用relay_len,UDP-in-TCP/IPv6 雙向打通。Fake DNS IPv6fd00::5e:x驗證通過。 - 兩階段回收:
tcp_graveyard_collect/udp_graveyard_collect每輪 epoll 後回收,hs_pool_stop在tun_socks_stop與engine_loop shutdown雙重保障無 UAF。
構建與 UI
compileSdk/targetSdk 34 -> 35,AGP8.1.2 -> 8.5.2,Gradle8.4 -> 8.7,新增androidx.core:core-ktx:1.13.1處理 edge-to-edgeWindowInsetsMainActivity.kt使用ViewCompat.setOnApplyWindowInsetsListener處理 targetSdk 35 強制 edge-to-edgeAndroidManifest.xml移除usesCleartextTraffic=true(僅 HTTPS)
壓力測試 (192.168.1.192:44645, SM-G9810, SD865, 隧道 192.168.1.178:1080, udp_in_tcp & remoteDns=true)
- 單流下載 Hinet 2048m: 550MB/15s = 36.6 MB/s (293 Mbps)
- 4併發 Cloudflare 30MB×4: 40 MB/s 320 Mbps;8併發 20MB×8: 43.9 MB/s 351 Mbps;4併發 Hinet: 46.3 MB/s 370 Mbps;CPU 峰值 87% 穩定
- 突發短連接 50併發 generate_204: 50/50 100% 6s;100混合併發: 99/100 99% 10s;4×50迭代 200 無指數增長
- DNS flood 30併發 (fakedns): 30/30 100% 回
198.18.0.x,udp建立+0正確攔截 - UDP relay 20併發
1.1.1.1:443: 20/20 100%UDP-in-TCP handshake全部完成 - 上傳 2MB: 1.36 MB/s;20MB: 4.32 MB/s 4.8s
- 長時 20s 閒置:VmRSS 158MB PSS 67MB 恆定,FDSize 256->512 自動擴容符合預期,無 ANR/crash/tombstone
APK 已以 release.keystore (v2簽名, apksigner verify 通過) 簽署,支持 arm64-v8a, armeabi-v7a, x86_64。
Full Changelog: v1.3.0...v1.4.0