implement front proxy & selector

This commit is contained in:
arm64v8a 2023-03-18 13:22:02 +09:00
parent c2f39c3bbc
commit b6a5f334ba
4 changed files with 96 additions and 18 deletions

View File

@ -47,7 +47,7 @@ class BaseService {
val receiver = broadcastReceiver { _, intent ->
when (intent.action) {
Intent.ACTION_SHUTDOWN -> service.persistStats()
Action.RELOAD -> service.forceLoad()
Action.RELOAD -> service.reload()
Action.SWITCH_WAKE_LOCK -> runOnDefaultDispatcher { service.switchWakeLock() }
else -> service.stopRunner()
}
@ -82,7 +82,7 @@ class BaseService {
cb.updateWakeLockStatus(data?.proxy?.service?.wakeLock != null)
}
val boardcastMutex = Mutex()
private val boardcastMutex = Mutex()
suspend fun broadcast(work: (ISagerNetServiceCallback) -> Unit) {
boardcastMutex.withLock {
@ -143,10 +143,23 @@ class BaseService {
fun onBind(intent: Intent): IBinder? =
if (intent.action == Action.SERVICE) data.binder else null
fun forceLoad() {
fun reload() {
if (DataStore.selectedProxy == 0L) {
stopRunner(false, (this as Context).getString(R.string.profile_empty))
}
if (canReloadSelector()) {
var tag = ""
data.proxy!!.config.trafficMap.forEach { (t, list) ->
if (list.map { it.id }.contains(DataStore.selectedProxy)) {
tag = t
}
}
if (tag.isNotBlank()) {
val success = data.proxy!!.box.selectOutbound(tag)
Logs.d("selectOutbound $tag $success")
}
return
}
val s = data.state
when {
s == State.Stopped -> startRunner()
@ -155,6 +168,18 @@ class BaseService {
}
}
fun canReloadSelector(): Boolean {
if ((data.proxy?.config?.selectorGroupId ?: -1L) < 0) return false
val ent = SagerDatabase.proxyDao.getById(DataStore.selectedProxy) ?: return false
val tmpBox = ProxyInstance(ent)
tmpBox.buildConfigTmp()
if (tmpBox.lastSelectorGroupId == data.proxy?.lastSelectorGroupId) {
return true
// TODO if profile changed?
}
return false
}
suspend fun startProcesses() {
data.proxy!!.launch()
}

View File

@ -8,18 +8,27 @@ import io.nekohasekai.sagernet.ktx.runOnIoDispatcher
import kotlinx.coroutines.runBlocking
import moe.matsuri.nb4a.utils.JavaUtil
class ProxyInstance(profile: ProxyEntity, val service: BaseService.Interface) :
class ProxyInstance(profile: ProxyEntity, var service: BaseService.Interface? = null) :
BoxInstance(profile) {
var lastSelectorGroupId = -1L
// for TrafficLooper
private var looper: TrafficLooper? = null
override fun buildConfig() {
super.buildConfig()
lastSelectorGroupId = super.config.selectorGroupId
//
Logs.d(config.config)
if (BuildConfig.DEBUG) Logs.d(JavaUtil.gson.toJson(config.trafficMap))
}
// only use this in temporary instance
fun buildConfigTmp() {
buildConfig()
}
override suspend fun init() {
super.init()
pluginConfigs.forEach { (_, plugin) ->
@ -32,7 +41,7 @@ class ProxyInstance(profile: ProxyEntity, val service: BaseService.Interface) :
box.setAsMain()
super.launch()
runOnIoDispatcher {
looper = TrafficLooper(service.data, this)
looper = service?.let { TrafficLooper(it.data, this) }
looper?.start()
}
}

View File

@ -54,6 +54,7 @@ class ConfigBuildResult(
var mainEntId: Long,
var trafficMap: Map<String, List<ProxyEntity>>,
val alerts: List<Pair<Int, String>>,
val selectorGroupId: Long,
) {
data class IndexEntity(var chain: LinkedHashMap<Int, ProxyEntity>)
}
@ -84,16 +85,19 @@ fun buildConfig(
listOf(),
proxy.id, //
mapOf(TAG_PROXY to listOf(proxy)), //
listOf()
listOf(),
-1L
)
}
}
val trafficMap = HashMap<String, MutableList<ProxyEntity>>()
val globalOutbounds = ArrayList<Long>()
val group = SagerDatabase.groupDao.getById(proxy.groupId)
var optionsToMerge = ""
fun ProxyEntity.resolveChain(): MutableList<ProxyEntity> {
val frontProxy = group?.frontProxy?.let { SagerDatabase.proxyDao.getById(it) }
val bean = requireBean()
if (bean is ChainBean) {
val beans = SagerDatabase.proxyDao.getEntities(bean.proxies)
@ -103,18 +107,26 @@ fun buildConfig(
val item = beansMap[proxyId] ?: continue
beanList.addAll(item.resolveChain())
}
return beanList.asReversed()
return if (frontProxy == null) {
beanList.asReversed()
} else {
beanList.add(0, frontProxy)
beanList.asReversed()
}
}
return if (frontProxy == null) {
mutableListOf(this)
} else {
mutableListOf(this, frontProxy)
}
return mutableListOf(this)
}
val proxies = proxy.resolveChain()
val extraRules = if (forTest) listOf() else SagerDatabase.rulesDao.enabledRules()
val extraProxies =
if (forTest) mapOf() else SagerDatabase.proxyDao.getEntities(extraRules.mapNotNull { rule ->
(if (forTest) mapOf() else SagerDatabase.proxyDao.getEntities(extraRules.mapNotNull { rule ->
rule.outbound.takeIf { it > 0 && it != proxy.id }
}.toHashSet().toList()).associate { it.id to it.resolveChain() }
}.toHashSet().toList()).associate { it.id to it.resolveChain() }).toMutableMap()
val buildSelector = !forTest && group?.isSelector == true
val uidListDNSRemote = mutableListOf<Int>()
val uidListDNSDirect = mutableListOf<Int>()
val domainListDNSRemote = mutableListOf<String>()
@ -444,8 +456,23 @@ fun buildConfig(
return chainTagOut
}
val tagProxy = buildChain(0, proxies)
// build outbounds
val tagMap = mutableMapOf<Long, String>()
if (buildSelector) {
val list = group?.id?.let { SagerDatabase.proxyDao.getByGroup(it) }
list?.forEach {
tagMap[it.id] = buildChain(it.id, it.resolveChain())
}
outbounds.add(0, Outbound_SelectorOptions().apply {
type = "selector"
tag = TAG_PROXY
default_ = tagMap[proxy.id]
outbounds = tagMap.values.toList()
}.asMap())
} else {
buildChain(0, proxy.resolveChain())
}
// build outbounds from route item
extraProxies.forEach { (key, entities) ->
tagMap[key] = buildChain(key, entities)
}
@ -521,10 +548,10 @@ fun buildConfig(
}
outbound = when (val outId = rule.outbound) {
0L -> tagProxy
0L -> TAG_PROXY
-1L -> TAG_BYPASS
-2L -> TAG_BLOCK
else -> if (outId == proxy.id) tagProxy else tagMap[outId]
else -> if (outId == proxy.id) TAG_PROXY else tagMap[outId]
?: throw Exception("invalid rule")
}
})
@ -569,7 +596,7 @@ fun buildConfig(
for (dns in remoteDns) {
if (!dns.isIpAddress()) continue
route.rules.add(Rule_DefaultOptions().apply {
outbound = tagProxy
outbound = TAG_PROXY
ip_cidr = listOf(dns)
})
}
@ -721,7 +748,8 @@ fun buildConfig(
externalIndexMap,
proxy.id,
trafficMap,
alerts
alerts,
if (buildSelector) group!!.id else -1L
)
}

View File

@ -24,6 +24,7 @@ import (
sblog "github.com/sagernet/sing-box/log"
"github.com/sagernet/sing-box/option"
"github.com/sagernet/sing-box/outbound"
)
var mainInstance *BoxInstance
@ -70,7 +71,8 @@ type BoxInstance struct {
cancel context.CancelFunc
state int
v2api *boxapi.SbV2rayServer
v2api *boxapi.SbV2rayServer
selector *outbound.Selector
ForTest bool
}
@ -111,6 +113,13 @@ func NewSingBoxInstance(config string) (b *BoxInstance, err error) {
platformFormatter := platformFormatter_.Interface().(*sblog.Formatter)
platformFormatter.DisableColors = true
// selector
if proxy, ok := b.Router().Outbound("proxy"); ok {
if selector, ok := proxy.(*outbound.Selector); ok {
b.selector = selector
}
}
return b, nil
}
@ -182,6 +191,13 @@ func (b *BoxInstance) QueryStats(tag, direct string) int64 {
return b.v2api.QueryStats(fmt.Sprintf("outbound>>>%s>>>traffic>>>%s", tag, direct))
}
func (b *BoxInstance) SelectOutbound(tag string) bool {
if b.selector != nil {
return b.selector.SelectOutbound(tag)
}
return false
}
func UrlTest(i *BoxInstance, link string, timeout int32) (int32, error) {
if i == nil {
// test current