diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/TestInstance.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/TestInstance.kt index ed7fc5a..57d1a17 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/TestInstance.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/TestInstance.kt @@ -12,7 +12,7 @@ import kotlinx.coroutines.delay import libcore.Libcore import kotlin.coroutines.suspendCoroutine -class TestInstance(profile: ProxyEntity, val link: String, val timeout: Int) : +class TestInstance(profile: ProxyEntity, val link: String, private val timeout: Int) : BoxInstance(profile) { suspend fun doTest(): Int { diff --git a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/UrlTest.kt b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/UrlTest.kt index 3cec7cf..73b6546 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/bg/proto/UrlTest.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/bg/proto/UrlTest.kt @@ -6,7 +6,7 @@ import io.nekohasekai.sagernet.database.ProxyEntity class UrlTest { val link = DataStore.connectionTestURL - val timeout = 3000 + private val timeout = 5000 suspend fun doTest(profile: ProxyEntity): Int { return TestInstance(profile, link, timeout).doTest() 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 7ac3eec..64bd0b9 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt @@ -182,12 +182,6 @@ fun buildConfig( } dns = DNSOptions().apply { - // TODO nb4a hosts? -// hosts = DataStore.hosts.split("\n") -// .filter { it.isNotBlank() } -// .associate { it.substringBefore(" ") to it.substringAfter(" ") } -// .toMutableMap() - servers = mutableListOf() rules = mutableListOf() independent_cache = true @@ -665,7 +659,8 @@ fun buildConfig( // remote dns obj remoteDns.firstOrNull().let { - dns.servers.add(DNSServerOptions().apply { + // Always use direct DNS for urlTest + if (!forTest) dns.servers.add(DNSServerOptions().apply { address = it ?: throw Exception("No remote DNS, check your settings!") tag = "dns-remote" address_resolver = "dns-direct" @@ -701,8 +696,6 @@ fun buildConfig( } if (forTest) { - // Always use direct DNS for urlTest - dns.servers.removeAt(0) dns.rules = listOf() } else { // built-in DNS rules diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt index ec53e1a..f9090d3 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/ConfigurationFragment.kt @@ -821,10 +821,6 @@ class ConfigurationFragment @JvmOverloads constructor( val testJobs = mutableListOf() val mainJob = runOnDefaultDispatcher { - if (DataStore.serviceState.started) { - stopService() - delay(500) // wait for service stop - } val group = DataStore.currentGroup() val profilesUnfiltered = SagerDatabase.proxyDao.getByGroup(group.id) test.proxyN = profilesUnfiltered.size diff --git a/libcore/go.mod b/libcore/go.mod index 57ee562..55f7308 100644 --- a/libcore/go.mod +++ b/libcore/go.mod @@ -14,6 +14,7 @@ require ( github.com/sagernet/sing-tun v0.6.1 github.com/ulikunitz/xz v0.5.11 golang.org/x/mobile v0.0.0-20231108233038-35478a0c49da + golang.org/x/sys v0.30.0 ) require ( @@ -78,7 +79,6 @@ require ( golang.org/x/mod v0.20.0 // indirect golang.org/x/net v0.34.0 // indirect golang.org/x/sync v0.10.0 // indirect - golang.org/x/sys v0.30.0 // indirect golang.org/x/text v0.21.0 // indirect golang.org/x/time v0.7.0 // indirect golang.org/x/tools v0.24.0 // indirect diff --git a/libcore/nb4a.go b/libcore/nb4a.go index cad7d3d..d049dca 100644 --- a/libcore/nb4a.go +++ b/libcore/nb4a.go @@ -1,6 +1,7 @@ package libcore import ( + "fmt" "libcore/device" "os" "path/filepath" @@ -13,11 +14,14 @@ import ( "github.com/matsuridayo/libneko/neko_common" "github.com/matsuridayo/libneko/neko_log" "github.com/sagernet/sing-box/nekoutils" + "golang.org/x/sys/unix" ) //go:linkname resourcePaths github.com/sagernet/sing-box/constant.resourcePaths var resourcePaths []string +var isBgProcess bool + func NekoLogPrintln(s string) { log.Println(s) } @@ -35,7 +39,7 @@ func InitCore(process, cachePath, internalAssets, externalAssets string, if1 NB4AInterface, if2 BoxPlatformInterface, ) { defer device.DeferPanicToError("InitCore", func(err error) { log.Println(err) }) - isBgProcess := strings.HasSuffix(process, ":bg") + isBgProcess = strings.HasSuffix(process, ":bg") neko_common.RunMode = neko_common.RunMode_NekoBoxForAndroid intfNB4A = if1 @@ -81,3 +85,37 @@ func InitCore(process, cachePath, internalAssets, externalAssets string, } }() } + +func sendFdToProtect(fd int, path string) error { + socketFd, err := unix.Socket(unix.AF_UNIX, unix.SOCK_STREAM, 0) + if err != nil { + return fmt.Errorf("failed to create unix socket: %w", err) + } + defer unix.Close(socketFd) + + var timeout unix.Timeval + timeout.Usec = 100 * 1000 + + _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_RCVTIMEO, &timeout) + _ = unix.SetsockoptTimeval(socketFd, unix.SOL_SOCKET, unix.SO_SNDTIMEO, &timeout) + + err = unix.Connect(socketFd, &unix.SockaddrUnix{Name: path}) + if err != nil { + return fmt.Errorf("failed to connect: %w", err) + } + + err = unix.Sendmsg(socketFd, nil, unix.UnixRights(fd), nil, 0) + if err != nil { + return fmt.Errorf("failed to send: %w", err) + } + + dummy := []byte{1} + n, err := unix.Read(socketFd, dummy) + if err != nil { + return fmt.Errorf("failed to receive: %w", err) + } + if n != 1 { + return fmt.Errorf("socket closed unexpectedly") + } + return nil +} diff --git a/libcore/platform_box.go b/libcore/platform_box.go index d8b1348..7e7378c 100644 --- a/libcore/platform_box.go +++ b/libcore/platform_box.go @@ -44,7 +44,12 @@ func (w *boxPlatformInterfaceWrapper) UsePlatformAutoDetectInterfaceControl() bo } func (w *boxPlatformInterfaceWrapper) AutoDetectInterfaceControl(fd int) error { - // "protect" + // call protect_path + if !isBgProcess { + _ = sendFdToProtect(fd, "protect_path") + return nil + } + // bg process call VPNService return intfBox.AutoDetectInterfaceControl(int32(fd)) }