Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
34 changes: 22 additions & 12 deletions app/src/main/java/com/github/kr328/clash/remote/Broadcasts.kt
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import android.content.Intent
import android.content.IntentFilter
import com.github.kr328.clash.common.compat.registerReceiverCompat
import com.github.kr328.clash.common.constants.Intents
import com.github.kr328.clash.common.constants.Permissions
import com.github.kr328.clash.common.log.Log
import java.util.*

Expand Down Expand Up @@ -59,12 +60,16 @@ class Broadcasts(private val context: Application) {
Intents.ACTION_PROFILE_UPDATE_COMPLETED ->
receivers.forEach {
it.onProfileUpdateCompleted(
UUID.fromString(intent.getStringExtra(Intents.EXTRA_UUID)))
intent.getStringExtra(Intents.EXTRA_UUID)?.let {
runCatching { UUID.fromString(it) }.getOrNull()
})
}
Intents.ACTION_PROFILE_UPDATE_FAILED ->
receivers.forEach {
it.onProfileUpdateFailed(
UUID.fromString(intent.getStringExtra(Intents.EXTRA_UUID)),
intent.getStringExtra(Intents.EXTRA_UUID)?.let {
runCatching { UUID.fromString(it) }.getOrNull()
},
intent.getStringExtra(Intents.EXTRA_FAIL_REASON))
}
Intents.ACTION_PROFILE_LOADED -> {
Expand All @@ -89,15 +94,20 @@ class Broadcasts(private val context: Application) {
return

try {
context.registerReceiverCompat(broadcastReceiver, IntentFilter().apply {
addAction(Intents.ACTION_SERVICE_RECREATED)
addAction(Intents.ACTION_CLASH_STARTED)
addAction(Intents.ACTION_CLASH_STOPPED)
addAction(Intents.ACTION_PROFILE_CHANGED)
addAction(Intents.ACTION_PROFILE_UPDATE_COMPLETED)
addAction(Intents.ACTION_PROFILE_UPDATE_FAILED)
addAction(Intents.ACTION_PROFILE_LOADED)
})
context.registerReceiverCompat(
broadcastReceiver,
IntentFilter().apply {
addAction(Intents.ACTION_SERVICE_RECREATED)
addAction(Intents.ACTION_CLASH_STARTED)
addAction(Intents.ACTION_CLASH_STOPPED)
addAction(Intents.ACTION_PROFILE_CHANGED)
addAction(Intents.ACTION_PROFILE_UPDATE_COMPLETED)
addAction(Intents.ACTION_PROFILE_UPDATE_FAILED)
addAction(Intents.ACTION_PROFILE_LOADED)
},
Permissions.RECEIVE_SELF_BROADCASTS,
null,
)

clashRunning = StatusClient(context).currentProfile() != null
} catch (e: Exception) {
Expand All @@ -117,4 +127,4 @@ class Broadcasts(private val context: Application) {
Log.w("Unregister global receiver: $e", e)
}
}
}
}
5 changes: 5 additions & 0 deletions core/src/main/golang/native/config/process.go
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ func patchOverride(cfg *config.RawConfig, _ string) error {
func patchExternalController(cfg *config.RawConfig, _ string) error {
cfg.ExternalController = ""
cfg.ExternalControllerTLS = ""
cfg.ExternalControllerUnix = ""
cfg.ExternalControllerPipe = ""
cfg.ExternalControllerCors = config.RawCors{}
cfg.Secret = ""
cfg.DNS.Listen = ""

return nil
}
Expand Down
85 changes: 85 additions & 0 deletions core/src/main/golang/native/config/process_test.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,85 @@
package config

import (
"testing"

mihomoConfig "github.com/metacubex/mihomo/config"
)

func TestPatchExternalControllerStripsProfileSecurityState(t *testing.T) {
cfg := &mihomoConfig.RawConfig{
ExternalController: "0.0.0.0:9090",
ExternalControllerTLS: "0.0.0.0:9443",
ExternalControllerUnix: "/data/local/tmp/clash.sock",
ExternalControllerPipe: "clash-pipe",
ExternalControllerCors: mihomoConfig.RawCors{
AllowOrigins: []string{"https://attacker.example"},
AllowPrivateNetwork: true,
},
Secret: "profile-controlled-secret",
DNS: mihomoConfig.RawDNS{
Enable: true,
Listen: ":1053",
},
}

if err := patchExternalController(cfg, ""); err != nil {
t.Fatalf("patchExternalController failed: %v", err)
}

if cfg.ExternalController != "" || cfg.ExternalControllerTLS != "" ||
cfg.ExternalControllerUnix != "" || cfg.ExternalControllerPipe != "" {
t.Fatal("profile-controlled external controller endpoint was preserved")
}
if len(cfg.ExternalControllerCors.AllowOrigins) != 0 || cfg.ExternalControllerCors.AllowPrivateNetwork {
t.Fatal("profile-controlled external controller CORS was preserved")
}
if cfg.Secret != "" {
t.Fatal("profile-controlled external controller secret was preserved")
}
if cfg.DNS.Listen != "" {
t.Fatal("profile-controlled DNS listener was preserved")
}
}

func TestPatchOverrideCanRestoreTrustedSecurityState(t *testing.T) {
cfg := &mihomoConfig.RawConfig{
ExternalController: "0.0.0.0:9090",
Secret: "profile-controlled-secret",
DNS: mihomoConfig.RawDNS{
Enable: true,
Listen: ":1053",
},
}

if err := patchExternalController(cfg, ""); err != nil {
t.Fatalf("patchExternalController failed: %v", err)
}

WriteOverride(OverrideSlotSession, `{
"external-controller":"127.0.0.1:9090",
"external-controller-cors":{
"allow-origins":["https://dashboard.example"],
"allow-private-network":true
},
"secret":"trusted-secret",
"dns":{"listen":"127.0.0.1:1053"}
}`)
defer ClearOverride(OverrideSlotSession)

if err := patchOverride(cfg, ""); err != nil {
t.Fatalf("patchOverride failed: %v", err)
}

if cfg.ExternalController != "127.0.0.1:9090" || cfg.Secret != "trusted-secret" {
t.Fatal("trusted external controller override was not restored")
}
if len(cfg.ExternalControllerCors.AllowOrigins) != 1 ||
cfg.ExternalControllerCors.AllowOrigins[0] != "https://dashboard.example" ||
!cfg.ExternalControllerCors.AllowPrivateNetwork {
t.Fatal("trusted external controller CORS override was not restored")
}
if cfg.DNS.Listen != "127.0.0.1:1053" {
t.Fatal("trusted DNS listener override was not restored")
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -221,7 +221,11 @@ class TunService : VpnService(), CoroutineScope by CoroutineScope(Dispatchers.De
stack = store.tunStackMode,
gateway = "$TUN_GATEWAY/$TUN_SUBNET_PREFIX" + if (store.allowIpv6) ",$TUN_GATEWAY6/$TUN_SUBNET_PREFIX6" else "",
portal = TUN_PORTAL + if (store.allowIpv6) ",$TUN_PORTAL6" else "",
dns = if (store.dnsHijacking) NET_ANY else (TUN_DNS + if (store.allowIpv6) ",$TUN_DNS6" else ""),
dns = if (store.dnsHijacking) {
NET_ANY + if (store.allowIpv6) ",$NET_ANY6" else ""
} else {
TUN_DNS + if (store.allowIpv6) ",$TUN_DNS6" else ""
},
)
}

Expand Down