From 02d00612761f9e5ab7fc00938b5b3b387fdf2889 Mon Sep 17 00:00:00 2001
From: HnDK0 <212071365+HnDK0@users.noreply.github.com>
Date: Thu, 11 Jun 2026 01:22:34 +0300
Subject: [PATCH 1/4] fix: add auth to local mixed inbound to prevent IP leak
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit
All popular VLESS/xray/sing-box clients expose a local SOCKS5 proxy
without authentication. Any app on the device can connect to it directly,
bypassing VpnService, and discover the real outbound server IP.
Fix: generate a random secret on first run (UUID, stored in SharedPreferences)
and set it as the password for the mixed inbound. The secret is only applied
in VPN mode; in proxy-only mode the inbound stays unauthenticated.
A runtime flag (DataStore.runningAsVPN) is set in BoxInstance.buildConfig()
and cleared in BaseService.stopRunner() to propagate the mode to all HTTP
clients without re-reading the config.
Changed files:
- libcore/http.go — TrySocks5(port) -> TrySocks5(port, username, password)
- DataStore.kt — add mixedSecret (lazy UUID) and runningAsVPN flag
- ConfigBuilder.kt — set users=[{username=\"neko\", password=mixedSecret}]
on mixed inbound only when isVPN=true
- BoxInstance.kt — set DataStore.runningAsVPN after buildConfig()
- BaseService.kt — clear DataStore.runningAsVPN in stopRunner()
- RawUpdater.kt — pass credentials to trySocks5 when runningAsVPN
- AboutFragment.kt — pass credentials to trySocks5 when runningAsVPN
- AssetsActivity.kt — pass credentials to trySocks5 when runningAsVPN (2 calls)
---
.../io/nekohasekai/sagernet/bg/BaseService.kt | 1 +
.../sagernet/bg/proto/BoxInstance.kt | 7 +++++
.../sagernet/database/DataStore.kt | 26 +++++++++++++++++++
.../nekohasekai/sagernet/fmt/ConfigBuilder.kt | 8 ++++++
.../nekohasekai/sagernet/group/RawUpdater.kt | 6 ++++-
.../nekohasekai/sagernet/ui/AboutFragment.kt | 6 ++++-
.../nekohasekai/sagernet/ui/AssetsActivity.kt | 12 +++++++--
libcore/http.go | 6 ++---
8 files changed, 65 insertions(+), 7 deletions(-)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt
index 8af48d193..89b4dcc66 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt
@@ -236,6 +236,7 @@ class BaseService {
fun stopRunner(restart: Boolean = false, msg: String? = null) {
DataStore.baseService = null
DataStore.vpnService = null
+ DataStore.runningAsVPN = false
if (data.state == State.Stopping) return
data.notification?.destroy()
diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
index 9f16723de..ff10cc12e 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
@@ -4,6 +4,7 @@ import android.os.SystemClock
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.bg.AbstractInstance
import io.nekohasekai.sagernet.bg.GuardedProcessPool
+import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProxyEntity
import io.nekohasekai.sagernet.fmt.ConfigBuildResult
@@ -46,6 +47,12 @@ abstract class BoxInstance(
protected open fun buildConfig() {
config = buildConfig(profile)
+ // Snapshot the VPN mode that was baked into the config so that
+ // HTTP clients can use the correct credentials for the mixed inbound.
+ // Reading serviceMode here is consistent with the isVPN local variable
+ // inside buildConfig(profile), since both execute in the same thread
+ // before the box starts.
+ DataStore.runningAsVPN = DataStore.serviceMode == Key.MODE_VPN
}
protected open suspend fun loadConfig() {
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
index b34417e87..ab8991d3d 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
@@ -27,6 +27,12 @@ object DataStore : OnPreferenceDataStoreChangeListener {
@Volatile
var serviceState = BaseService.State.Idle
+ // Runtime flag: true when the service is running in VPN mode.
+ // Set once in BoxInstance.buildConfig(), cleared in BaseService.stopRunner().
+ // @Volatile ensures visibility across threads without locks.
+ @Volatile
+ var runningAsVPN: Boolean = false
+
val configurationStore = RoomPreferenceDataStore(PublicDatabase.kvPairDao)
val profileCacheStore = RoomPreferenceDataStore(TempDatabase.profileCacheDao)
@@ -134,6 +140,18 @@ object DataStore : OnPreferenceDataStoreChangeListener {
// hopefully hashCode = mHandle doesn't change, currently this is true from KitKat to Nougat
private val userIndex by lazy { Binder.getCallingUserHandle().hashCode() }
+ // Random secret for the mixed inbound in VPN mode.
+ // Lazily generated on first access and persisted to survive app restarts.
+ val mixedSecret: String
+ get() {
+ var s = configurationStore.getString("mixedSecret")
+ if (s.isNullOrEmpty()) {
+ s = java.util.UUID.randomUUID().toString().replace("-", "")
+ configurationStore.putString("mixedSecret", s)
+ }
+ return s
+ }
+
var mixedPort: Int
get() = getLocalPort(Key.MIXED_PORT, 2080)
set(value) = saveLocalPort(Key.MIXED_PORT, value)
@@ -142,6 +160,14 @@ object DataStore : OnPreferenceDataStoreChangeListener {
if (configurationStore.getString(Key.MIXED_PORT) == null) {
mixedPort = mixedPort
}
+ // Pre-generate the VPN inbound secret so it is ready before the service
+ // starts, even if the user never opens Settings.
+ if (configurationStore.getString("mixedSecret").isNullOrEmpty()) {
+ configurationStore.putString(
+ "mixedSecret",
+ java.util.UUID.randomUUID().toString().replace("-", "")
+ )
+ }
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
index 9d815a194..ef2283000 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
@@ -254,6 +254,14 @@ fun buildConfig(
domain_strategy = genDomainStrategy(DataStore.resolveDestination)
sniff = needSniff
sniff_override_destination = needSniffOverride
+ // In VPN mode the inbound is internal; protect it with credentials
+ // so that VPN-excluded apps cannot bypass the tunnel via the proxy port.
+ if (isVPN) {
+ users = listOf(User().also { u ->
+ u.username = "neko"
+ u.password = DataStore.mixedSecret
+ })
+ }
})
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
index 2eb029aef..7df4ae5b8 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
@@ -61,7 +61,11 @@ object RawUpdater : GroupUpdater() {
} else {
val response = Libcore.newHttpClient().apply {
- trySocks5(DataStore.mixedPort)
+ trySocks5(
+ DataStore.mixedPort,
+ if (DataStore.runningAsVPN) "neko" else "",
+ if (DataStore.runningAsVPN) DataStore.mixedSecret else ""
+ )
tryH3Direct()
when (DataStore.appTLSVersion) {
"1.3" -> restrictedTLS()
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
index 1c9beffa2..ad23b2fb2 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
@@ -216,7 +216,11 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
try {
val client = Libcore.newHttpClient().apply {
modernTLS()
- trySocks5(DataStore.mixedPort)
+ trySocks5(
+ DataStore.mixedPort,
+ if (DataStore.runningAsVPN) "neko" else "",
+ if (DataStore.runningAsVPN) DataStore.mixedSecret else ""
+ )
}
val response = client.newRequest().apply {
if (checkPreview) {
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt
index 267317f0c..4a5ff6723 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt
@@ -282,7 +282,11 @@ class AssetsActivity : ThemedActivity() {
val client = Libcore.newHttpClient().apply {
modernTLS()
keepAlive()
- trySocks5(DataStore.mixedPort)
+ trySocks5(
+ DataStore.mixedPort,
+ if (DataStore.runningAsVPN) "neko" else "",
+ if (DataStore.runningAsVPN) DataStore.mixedSecret else ""
+ )
}
try {
@@ -345,7 +349,11 @@ class AssetsActivity : ThemedActivity() {
val client = Libcore.newHttpClient().apply {
modernTLS()
keepAlive()
- trySocks5(DataStore.mixedPort)
+ trySocks5(
+ DataStore.mixedPort,
+ if (DataStore.runningAsVPN) "neko" else "",
+ if (DataStore.runningAsVPN) DataStore.mixedSecret else ""
+ )
}
try {
val response = client.newRequest().apply {
diff --git a/libcore/http.go b/libcore/http.go
index c4a8db500..ef8288952 100644
--- a/libcore/http.go
+++ b/libcore/http.go
@@ -36,7 +36,7 @@ type HTTPClient interface {
ModernTLS()
PinnedTLS12()
PinnedSHA256(sumHex string)
- TrySocks5(port int32)
+ TrySocks5(port int32, username string, password string)
TryH3Direct()
KeepAlive()
NewRequest() HTTPRequest
@@ -114,7 +114,7 @@ func (c *httpClient) PinnedSHA256(sumHex string) {
}
}
-func (c *httpClient) TrySocks5(port int32) {
+func (c *httpClient) TrySocks5(port int32, username string, password string) {
dialer := new(net.Dialer)
c.h1h2Transport.DialContext = func(ctx context.Context, network, addr string) (net.Conn, error) {
for {
@@ -125,7 +125,7 @@ func (c *httpClient) TrySocks5(port int32) {
}
break
}
- _, err = socks.ClientHandshake5(socksConn, socks5.CommandConnect, metadata.ParseSocksaddr(addr), "", "")
+ _, err = socks.ClientHandshake5(socksConn, socks5.CommandConnect, metadata.ParseSocksaddr(addr), username, password)
if err != nil {
if c.tryH3Direct {
return nil, errFailConnectSocks5
From 385459769c7ce3b1c7c19df026a8faf238b28813 Mon Sep 17 00:00:00 2001
From: starifly <2003879+starifly@users.noreply.github.com>
Date: Sat, 13 Jun 2026 15:32:35 +0800
Subject: [PATCH 2/4] update
---
app/src/main/java/io/nekohasekai/sagernet/Constants.kt | 2 ++
1 file changed, 2 insertions(+)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt
index 10fc646d0..ea521798d 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt
@@ -41,6 +41,8 @@ object Key {
const val CONCURRENT_DIAL = "concurrentDial"
const val MIXED_PORT = "mixedPort"
+ const val MIXED_SECRET = "mixedSecret" // storage key for the generated inbound secret
+ const val MIXED_USERNAME = "neko" // username presented to the authed mixed inbound
const val ALLOW_ACCESS = "allowAccess"
const val SPEED_INTERVAL = "speedInterval"
const val SHOW_DIRECT_SPEED = "showDirectSpeed"
From 553f6ab8974ea7e80e466f74349415add2078d46 Mon Sep 17 00:00:00 2001
From: starifly <2003879+starifly@users.noreply.github.com>
Date: Sat, 13 Jun 2026 16:14:44 +0800
Subject: [PATCH 3/4] update
---
.../io/nekohasekai/sagernet/bg/BaseService.kt | 2 +-
.../sagernet/bg/proto/BoxInstance.kt | 10 ++-----
.../sagernet/database/DataStore.kt | 29 ++++++++-----------
.../nekohasekai/sagernet/fmt/ConfigBuilder.kt | 6 ++--
.../nekohasekai/sagernet/group/RawUpdater.kt | 4 +--
.../nekohasekai/sagernet/ui/AboutFragment.kt | 6 ++--
.../nekohasekai/sagernet/ui/AssetsActivity.kt | 8 ++---
7 files changed, 26 insertions(+), 39 deletions(-)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt
index 89b4dcc66..12596a288 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/bg/BaseService.kt
@@ -236,7 +236,7 @@ class BaseService {
fun stopRunner(restart: Boolean = false, msg: String? = null) {
DataStore.baseService = null
DataStore.vpnService = null
- DataStore.runningAsVPN = false
+ DataStore.mixedInboundAuthed = false
if (data.state == State.Stopping) return
data.notification?.destroy()
diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
index ff10cc12e..d9aa300e2 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/BoxInstance.kt
@@ -4,7 +4,6 @@ import android.os.SystemClock
import io.nekohasekai.sagernet.SagerNet
import io.nekohasekai.sagernet.bg.AbstractInstance
import io.nekohasekai.sagernet.bg.GuardedProcessPool
-import io.nekohasekai.sagernet.Key
import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProxyEntity
import io.nekohasekai.sagernet.fmt.ConfigBuildResult
@@ -47,12 +46,7 @@ abstract class BoxInstance(
protected open fun buildConfig() {
config = buildConfig(profile)
- // Snapshot the VPN mode that was baked into the config so that
- // HTTP clients can use the correct credentials for the mixed inbound.
- // Reading serviceMode here is consistent with the isVPN local variable
- // inside buildConfig(profile), since both execute in the same thread
- // before the box starts.
- DataStore.runningAsVPN = DataStore.serviceMode == Key.MODE_VPN
+ DataStore.mixedInboundAuthed = DataStore.mixedInboundNeedsAuth
}
protected open suspend fun loadConfig() {
@@ -226,4 +220,4 @@ abstract class BoxInstance(
}
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
index ab8991d3d..e50a49e38 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt
@@ -1,6 +1,7 @@
package io.nekohasekai.sagernet.database
import android.os.Binder
+import android.os.Build
import androidx.preference.PreferenceDataStore
import io.nekohasekai.sagernet.CONNECTION_TEST_URL
import io.nekohasekai.sagernet.GroupType
@@ -27,11 +28,8 @@ object DataStore : OnPreferenceDataStoreChangeListener {
@Volatile
var serviceState = BaseService.State.Idle
- // Runtime flag: true when the service is running in VPN mode.
- // Set once in BoxInstance.buildConfig(), cleared in BaseService.stopRunner().
- // @Volatile ensures visibility across threads without locks.
@Volatile
- var runningAsVPN: Boolean = false
+ var mixedInboundAuthed: Boolean = false
val configurationStore = RoomPreferenceDataStore(PublicDatabase.kvPairDao)
val profileCacheStore = RoomPreferenceDataStore(TempDatabase.profileCacheDao)
@@ -140,14 +138,12 @@ object DataStore : OnPreferenceDataStoreChangeListener {
// hopefully hashCode = mHandle doesn't change, currently this is true from KitKat to Nougat
private val userIndex by lazy { Binder.getCallingUserHandle().hashCode() }
- // Random secret for the mixed inbound in VPN mode.
- // Lazily generated on first access and persisted to survive app restarts.
val mixedSecret: String
- get() {
- var s = configurationStore.getString("mixedSecret")
+ @Synchronized get() {
+ var s = configurationStore.getString(Key.MIXED_SECRET)
if (s.isNullOrEmpty()) {
s = java.util.UUID.randomUUID().toString().replace("-", "")
- configurationStore.putString("mixedSecret", s)
+ configurationStore.putString(Key.MIXED_SECRET, s)
}
return s
}
@@ -156,18 +152,17 @@ object DataStore : OnPreferenceDataStoreChangeListener {
get() = getLocalPort(Key.MIXED_PORT, 2080)
set(value) = saveLocalPort(Key.MIXED_PORT, value)
+ val mixedInboundNeedsAuth: Boolean
+ get() = serviceMode == Key.MODE_VPN &&
+ !(appendHttpProxy && Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q)
+
+ val mixedInboundUser: String get() = if (mixedInboundAuthed) Key.MIXED_USERNAME else ""
+ val mixedInboundPass: String get() = if (mixedInboundAuthed) mixedSecret else ""
+
fun initGlobal() {
if (configurationStore.getString(Key.MIXED_PORT) == null) {
mixedPort = mixedPort
}
- // Pre-generate the VPN inbound secret so it is ready before the service
- // starts, even if the user never opens Settings.
- if (configurationStore.getString("mixedSecret").isNullOrEmpty()) {
- configurationStore.putString(
- "mixedSecret",
- java.util.UUID.randomUUID().toString().replace("-", "")
- )
- }
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
index ef2283000..b34ae0124 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt
@@ -254,11 +254,9 @@ fun buildConfig(
domain_strategy = genDomainStrategy(DataStore.resolveDestination)
sniff = needSniff
sniff_override_destination = needSniffOverride
- // In VPN mode the inbound is internal; protect it with credentials
- // so that VPN-excluded apps cannot bypass the tunnel via the proxy port.
- if (isVPN) {
+ if (DataStore.mixedInboundNeedsAuth) {
users = listOf(User().also { u ->
- u.username = "neko"
+ u.username = Key.MIXED_USERNAME
u.password = DataStore.mixedSecret
})
}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
index 7df4ae5b8..069c3383a 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/group/RawUpdater.kt
@@ -63,8 +63,8 @@ object RawUpdater : GroupUpdater() {
val response = Libcore.newHttpClient().apply {
trySocks5(
DataStore.mixedPort,
- if (DataStore.runningAsVPN) "neko" else "",
- if (DataStore.runningAsVPN) DataStore.mixedSecret else ""
+ DataStore.mixedInboundUser,
+ DataStore.mixedInboundPass
)
tryH3Direct()
when (DataStore.appTLSVersion) {
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
index ad23b2fb2..af7a9d89d 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AboutFragment.kt
@@ -218,8 +218,8 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
modernTLS()
trySocks5(
DataStore.mixedPort,
- if (DataStore.runningAsVPN) "neko" else "",
- if (DataStore.runningAsVPN) DataStore.mixedSecret else ""
+ DataStore.mixedInboundUser,
+ DataStore.mixedInboundPass
)
}
val response = client.newRequest().apply {
@@ -280,4 +280,4 @@ class AboutFragment : ToolbarFragment(R.layout.layout_about) {
}
-}
\ No newline at end of file
+}
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt
index 4a5ff6723..22d7789ca 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/AssetsActivity.kt
@@ -284,8 +284,8 @@ class AssetsActivity : ThemedActivity() {
keepAlive()
trySocks5(
DataStore.mixedPort,
- if (DataStore.runningAsVPN) "neko" else "",
- if (DataStore.runningAsVPN) DataStore.mixedSecret else ""
+ DataStore.mixedInboundUser,
+ DataStore.mixedInboundPass
)
}
@@ -351,8 +351,8 @@ class AssetsActivity : ThemedActivity() {
keepAlive()
trySocks5(
DataStore.mixedPort,
- if (DataStore.runningAsVPN) "neko" else "",
- if (DataStore.runningAsVPN) DataStore.mixedSecret else ""
+ DataStore.mixedInboundUser,
+ DataStore.mixedInboundPass
)
}
try {
From 5f93cd7b17c9eec4163039e9e1d0deb6f1865b45 Mon Sep 17 00:00:00 2001
From: starifly <2003879+starifly@users.noreply.github.com>
Date: Sat, 13 Jun 2026 16:51:43 +0800
Subject: [PATCH 4/4] Add a security prompt when enabling appendHTTPProxy
---
.../sagernet/ui/SettingsPreferenceFragment.kt | 18 +++++++++++++++++-
app/src/main/res/values-fa/strings.xml | 3 +++
app/src/main/res/values-ja/strings.xml | 3 +++
app/src/main/res/values-ko/strings.xml | 3 +++
app/src/main/res/values-ru/strings.xml | 3 +++
app/src/main/res/values-zh-rCN/strings.xml | 3 +++
app/src/main/res/values-zh-rTW/strings.xml | 3 +++
app/src/main/res/values/strings.xml | 3 +++
8 files changed, 38 insertions(+), 1 deletion(-)
diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt
index dab195f43..aee7f3564 100644
--- a/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt
+++ b/app/src/main/java/io/nekohasekai/sagernet/ui/SettingsPreferenceFragment.kt
@@ -175,7 +175,23 @@ class SettingsPreferenceFragment : PreferenceFragmentCompat() {
}
mixedPort.onPreferenceChangeListener = reloadListener
- appendHttpProxy.onPreferenceChangeListener = reloadListener
+ appendHttpProxy.setOnPreferenceChangeListener { _, newValue ->
+ if (newValue as Boolean) {
+ MaterialAlertDialogBuilder(requireContext()).apply {
+ setTitle(R.string.append_http_proxy_security_title)
+ setMessage(R.string.append_http_proxy_security_message)
+ setNegativeButton(android.R.string.cancel, null)
+ setPositiveButton(R.string.enable_anyway) { _, _ ->
+ appendHttpProxy.isChecked = true
+ needReload()
+ }
+ }.show()
+ false
+ } else {
+ needReload()
+ true
+ }
+ }
strictRoute.onPreferenceChangeListener = reloadListener
showDirectSpeed.onPreferenceChangeListener = reloadListener
trafficSniffing.onPreferenceChangeListener = reloadListener
diff --git a/app/src/main/res/values-fa/strings.xml b/app/src/main/res/values-fa/strings.xml
index a8c85d2b4..66d48210e 100644
--- a/app/src/main/res/values-fa/strings.xml
+++ b/app/src/main/res/values-fa/strings.xml
@@ -326,6 +326,9 @@
بدون پاسخ
پراکسی HTTP را به VPN اضافه کنید
پروکسی HTTP مستقیماً از (مرورگر/برخی برنامههای پشتیبانیشده) بدون استفاده از دستگاه NIC مجازی (نسخه اندروید بالاتر از 10) استفاده میشود.
+ اطلاعیه امنیتی
+ پس از فعالسازی، برنامههای دیگر در این دستگاه میتوانند مستقیماً از پورت پروکسی محلی استفاده کنند.\n\nتوصیه: تنها در صورتی که به تمام برنامههای نصبشده اعتماد دارید، این گزینه را فعال کنید.
+ همچنان فعال کن
تنظیمات پروتکلها
تأمینکننده Trojan
ابتدایی
diff --git a/app/src/main/res/values-ja/strings.xml b/app/src/main/res/values-ja/strings.xml
index 857b6023f..d58b487f9 100644
--- a/app/src/main/res/values-ja/strings.xml
+++ b/app/src/main/res/values-ja/strings.xml
@@ -303,6 +303,9 @@
タイムアウト
VPN に HTTP プロキシを追加
HTTP プロキシは仮想 NIC デバイスを経由せず、(ブラウザ/一部の対応アプリ) から直接使用されます (Android 10+)
+ セキュリティに関する通知
+ 有効にすると、このデバイス上の他のアプリがローカルプロキシポートを直接使用できるようになります。\n\n推奨:インストールされているすべてのアプリを信頼している場合にのみ有効にしてください。
+ それでも有効にする
厳格なルーティング
プロトコル設定
Trojan プロバイダー
diff --git a/app/src/main/res/values-ko/strings.xml b/app/src/main/res/values-ko/strings.xml
index eb9c030ea..86ad67155 100644
--- a/app/src/main/res/values-ko/strings.xml
+++ b/app/src/main/res/values-ko/strings.xml
@@ -297,6 +297,9 @@
시간 초과
VPN에 HTTP 프록시 추가
HTTP 프록시는 가상 NIC 장치를 거치지 않고 (브라우저/일부 지원 앱)에서 직접 사용됩니다 (Android 10+)
+ 보안 알림
+ 활성화하면 이 기기의 다른 앱이 로컬 프록시 포트를 직접 사용할 수 있습니다.\n\n권장 사항: 설치된 모든 앱을 신뢰하는 경우에만 활성화하세요.
+ 그래도 활성화
엄격한 라우팅
프로토콜 설정
Trojan 공급자
diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml
index 975111f04..48f759740 100644
--- a/app/src/main/res/values-ru/strings.xml
+++ b/app/src/main/res/values-ru/strings.xml
@@ -41,6 +41,9 @@
Версия
Добавить HTTP-прокси к VPN
HTTP-прокси будет использоваться напрямую из (браузера / некоторых поддерживаемых приложений), без использования виртуального сетевого интерфейса (Android 10+)
+Уведомление о безопасности
+При включении другие приложения на этом устройстве смогут напрямую использовать локальный прокси-порт.\n\nРекомендация: Включайте эту функцию только если доверяете всем установленным приложениям.
+Всё равно включить
Применить
Приложения
%d приложений
diff --git a/app/src/main/res/values-zh-rCN/strings.xml b/app/src/main/res/values-zh-rCN/strings.xml
index 888024d6e..ad6e88709 100644
--- a/app/src/main/res/values-zh-rCN/strings.xml
+++ b/app/src/main/res/values-zh-rCN/strings.xml
@@ -303,6 +303,9 @@
开关
追加 HTTP 代理至 VPN
浏览器 / 一些支持的应用 将直接使用 HTTP 代理, 而不经过虚拟网卡设备 (Android 10+)
+ 安全提示
+ 开启后,本机其他应用可以直接使用本地代理端口。\n\n建议:仅在你信任所有已安装应用时开启。
+ 仍然开启
严格路由
ICMPing 不可用
协议设置
diff --git a/app/src/main/res/values-zh-rTW/strings.xml b/app/src/main/res/values-zh-rTW/strings.xml
index 831a80370..89c6b260b 100644
--- a/app/src/main/res/values-zh-rTW/strings.xml
+++ b/app/src/main/res/values-zh-rTW/strings.xml
@@ -291,6 +291,9 @@
逾時
為 VPN 附加 HTTP 代理
HTTP 代理將直接被支援的瀏覽器/應用程式使用,而無需經過虛擬網路卡(Android 10+)
+ 安全提示
+ 開啟後,本機其他應用程式可以直接使用本機代理連接埠。\n\n建議:僅在你信任所有已安裝應用程式時開啟。
+ 仍然開啟
Trojan 提供程式
協議設定
基本
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index 249a4d688..efecad9a7 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -358,6 +358,9 @@
Append HTTP Proxy to VPN
HTTP proxy will be used directly from (browser/ some
supported apps), without going through the virtual NIC device (Android 10+)
+ Security Notice
+ When enabled, other apps on this device can directly use the local proxy port.\n\nRecommendation: Only enable this if you trust all installed apps.
+ Enable Anyway
Strict Route
Protocol Settings
Trojan Provider