implement front proxy & landing proxy

This commit is contained in:
arm64v8a 2023-03-18 16:46:19 +09:00
parent eec96213aa
commit bb51a38474
13 changed files with 124 additions and 46 deletions

View File

@ -2,11 +2,11 @@
"formatVersion": 1, "formatVersion": 1,
"database": { "database": {
"version": 2, "version": 2,
"identityHash": "062104587ad7088bb9926683389e995d", "identityHash": "937a517378a0cb35dc1f8bd181683882",
"entities": [ "entities": [
{ {
"tableName": "proxy_groups", "tableName": "proxy_groups",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL)", "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `userOrder` INTEGER NOT NULL, `ungrouped` INTEGER NOT NULL, `name` TEXT, `type` INTEGER NOT NULL, `subscription` BLOB, `order` INTEGER NOT NULL, `isSelector` INTEGER NOT NULL, `frontProxy` INTEGER NOT NULL, `landingProxy` INTEGER NOT NULL)",
"fields": [ "fields": [
{ {
"fieldPath": "id", "fieldPath": "id",
@ -61,13 +61,19 @@
"columnName": "frontProxy", "columnName": "frontProxy",
"affinity": "INTEGER", "affinity": "INTEGER",
"notNull": true "notNull": true
},
{
"fieldPath": "landingProxy",
"columnName": "landingProxy",
"affinity": "INTEGER",
"notNull": true
} }
], ],
"primaryKey": { "primaryKey": {
"autoGenerate": true,
"columnNames": [ "columnNames": [
"id" "id"
], ]
"autoGenerate": true
}, },
"indices": [], "indices": [],
"foreignKeys": [] "foreignKeys": []
@ -222,10 +228,10 @@
} }
], ],
"primaryKey": { "primaryKey": {
"autoGenerate": true,
"columnNames": [ "columnNames": [
"id" "id"
], ]
"autoGenerate": true
}, },
"indices": [ "indices": [
{ {
@ -324,10 +330,10 @@
} }
], ],
"primaryKey": { "primaryKey": {
"autoGenerate": true,
"columnNames": [ "columnNames": [
"id" "id"
], ]
"autoGenerate": true
}, },
"indices": [], "indices": [],
"foreignKeys": [] "foreignKeys": []
@ -336,7 +342,7 @@
"views": [], "views": [],
"setupQueries": [ "setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '062104587ad7088bb9926683389e995d')" "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '937a517378a0cb35dc1f8bd181683882')"
] ]
} }
} }

View File

@ -127,13 +127,14 @@ object Key {
const val ROUTE_SOURCE = "routeSource" const val ROUTE_SOURCE = "routeSource"
const val ROUTE_PROTOCOL = "routeProtocol" const val ROUTE_PROTOCOL = "routeProtocol"
const val ROUTE_OUTBOUND = "routeOutbound" const val ROUTE_OUTBOUND = "routeOutbound"
const val ROUTE_OUTBOUND_RULE = "routeOutboundRule"
const val ROUTE_PACKAGES = "routePackages" const val ROUTE_PACKAGES = "routePackages"
const val GROUP_NAME = "groupName" const val GROUP_NAME = "groupName"
const val GROUP_TYPE = "groupType" const val GROUP_TYPE = "groupType"
const val GROUP_ORDER = "groupOrder" const val GROUP_ORDER = "groupOrder"
const val GROUP_IS_SELECTOR = "groupIsSelector" const val GROUP_IS_SELECTOR = "groupIsSelector"
const val GROUP_FRONT_PROXY = "groupFrontProxy"
const val GROUP_LANDING_PROXY = "groupLandingProxy"
const val GROUP_SUBSCRIPTION = "groupSubscription" const val GROUP_SUBSCRIPTION = "groupSubscription"
const val SUBSCRIPTION_LINK = "subscriptionLink" const val SUBSCRIPTION_LINK = "subscriptionLink"

View File

@ -13,6 +13,7 @@ import io.nekohasekai.sagernet.aidl.ISagerNetService
import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback import io.nekohasekai.sagernet.aidl.ISagerNetServiceCallback
import io.nekohasekai.sagernet.bg.proto.ProxyInstance import io.nekohasekai.sagernet.bg.proto.ProxyInstance
import io.nekohasekai.sagernet.database.DataStore import io.nekohasekai.sagernet.database.DataStore
import io.nekohasekai.sagernet.database.ProxyEntity
import io.nekohasekai.sagernet.database.SagerDatabase import io.nekohasekai.sagernet.database.SagerDatabase
import io.nekohasekai.sagernet.ktx.* import io.nekohasekai.sagernet.ktx.*
import io.nekohasekai.sagernet.plugin.PluginManager import io.nekohasekai.sagernet.plugin.PluginManager
@ -149,14 +150,24 @@ class BaseService {
} }
if (canReloadSelector()) { if (canReloadSelector()) {
var tag = "" var tag = ""
var ent: ProxyEntity? = null
data.proxy!!.config.trafficMap.forEach { (t, list) -> data.proxy!!.config.trafficMap.forEach { (t, list) ->
if (list.map { it.id }.contains(DataStore.selectedProxy)) { for (it in list) {
tag = t if (it.id == DataStore.selectedProxy) {
ent = it
tag = t
break
}
} }
} }
if (tag.isNotBlank()) { if (tag.isNotBlank() && ent != null) {
val success = data.proxy!!.box.selectOutbound(tag) val success = data.proxy!!.box.selectOutbound(tag)
Logs.d("selectOutbound $tag $success") Logs.d("selectOutbound $tag $success")
runOnDefaultDispatcher {
data.binder.broadcast {
it.stateChanged(-1, ent!!.displayName(), null)
}
}
} }
return return
} }

View File

@ -53,6 +53,7 @@ class SagerConnection(private var listenForDeath: Boolean = false) : ServiceConn
private val serviceCallback = object : ISagerNetServiceCallback.Stub() { private val serviceCallback = object : ISagerNetServiceCallback.Stub() {
override fun stateChanged(state: Int, profileName: String?, msg: String?) { override fun stateChanged(state: Int, profileName: String?, msg: String?) {
if (state < 0) return // skip private
val s = BaseService.State.values()[state] val s = BaseService.State.values()[state]
DataStore.serviceState = s DataStore.serviceState = s
val callback = callback ?: return val callback = callback ?: return

View File

@ -89,6 +89,9 @@ class ServiceNotification(
} }
override fun stateChanged(state: Int, profileName: String?, msg: String?) { override fun stateChanged(state: Int, profileName: String?, msg: String?) {
if (state == -1) {
builder.setContentTitle(profileName)
}
} }
override fun missingPlugin(profileName: String?, pluginName: String?) { override fun missingPlugin(profileName: String?, pluginName: String?) {

View File

@ -85,11 +85,11 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var nightTheme by configurationStore.stringToInt(Key.NIGHT_THEME) var nightTheme by configurationStore.stringToInt(Key.NIGHT_THEME)
var serviceMode by configurationStore.string(Key.SERVICE_MODE) { Key.MODE_VPN } var serviceMode by configurationStore.string(Key.SERVICE_MODE) { Key.MODE_VPN }
// var domainStrategy by configurationStore.string(Key.DOMAIN_STRATEGY) { "AsIs" } // var domainStrategy by configurationStore.string(Key.DOMAIN_STRATEGY) { "AsIs" }
var trafficSniffing by configurationStore.boolean(Key.TRAFFIC_SNIFFING) { true } var trafficSniffing by configurationStore.boolean(Key.TRAFFIC_SNIFFING) { true }
var resolveDestination by configurationStore.boolean(Key.RESOLVE_DESTINATION) var resolveDestination by configurationStore.boolean(Key.RESOLVE_DESTINATION)
// var tcpKeepAliveInterval by configurationStore.stringToInt(Key.TCP_KEEP_ALIVE_INTERVAL) { 15 } // var tcpKeepAliveInterval by configurationStore.stringToInt(Key.TCP_KEEP_ALIVE_INTERVAL) { 15 }
var mtu by configurationStore.stringToInt(Key.MTU) { 9000 } var mtu by configurationStore.stringToInt(Key.MTU) { 9000 }
var bypassLan by configurationStore.boolean(Key.BYPASS_LAN) var bypassLan by configurationStore.boolean(Key.BYPASS_LAN)
@ -225,9 +225,13 @@ object DataStore : OnPreferenceDataStoreChangeListener {
var routeSource by profileCacheStore.string(Key.ROUTE_SOURCE) var routeSource by profileCacheStore.string(Key.ROUTE_SOURCE)
var routeProtocol by profileCacheStore.string(Key.ROUTE_PROTOCOL) var routeProtocol by profileCacheStore.string(Key.ROUTE_PROTOCOL)
var routeOutbound by profileCacheStore.stringToInt(Key.ROUTE_OUTBOUND) var routeOutbound by profileCacheStore.stringToInt(Key.ROUTE_OUTBOUND)
var routeOutboundRule by profileCacheStore.long(Key.ROUTE_OUTBOUND_RULE) var routeOutboundRule by profileCacheStore.long(Key.ROUTE_OUTBOUND + "Long")
var routePackages by profileCacheStore.string(Key.ROUTE_PACKAGES) var routePackages by profileCacheStore.string(Key.ROUTE_PACKAGES)
var frontProxy by profileCacheStore.long(Key.GROUP_FRONT_PROXY + "Long")
var landingProxy by profileCacheStore.long(Key.GROUP_LANDING_PROXY + "Long")
var frontProxyTmp by profileCacheStore.stringToInt(Key.GROUP_FRONT_PROXY)
var landingProxyTmp by profileCacheStore.stringToInt(Key.GROUP_LANDING_PROXY)
var serverConfig by profileCacheStore.string(Key.SERVER_CONFIG) var serverConfig by profileCacheStore.string(Key.SERVER_CONFIG)

View File

@ -20,7 +20,8 @@ data class ProxyGroup(
var subscription: SubscriptionBean? = null, var subscription: SubscriptionBean? = null,
var order: Int = GroupOrder.ORIGIN, var order: Int = GroupOrder.ORIGIN,
var isSelector: Boolean = false, var isSelector: Boolean = false,
var frontProxy: Long = 0L var frontProxy: Long = -1L,
var landingProxy: Long = -1L
) : Serializable() { ) : Serializable() {
@Transient @Transient

View File

@ -96,8 +96,7 @@ fun buildConfig(
val group = SagerDatabase.groupDao.getById(proxy.groupId) val group = SagerDatabase.groupDao.getById(proxy.groupId)
var optionsToMerge = "" var optionsToMerge = ""
fun ProxyEntity.resolveChain(): MutableList<ProxyEntity> { fun ProxyEntity.resolveChainInternal(): MutableList<ProxyEntity> {
val frontProxy = group?.frontProxy?.let { SagerDatabase.proxyDao.getById(it) }
val bean = requireBean() val bean = requireBean()
if (bean is ChainBean) { if (bean is ChainBean) {
val beans = SagerDatabase.proxyDao.getEntities(bean.proxies) val beans = SagerDatabase.proxyDao.getEntities(bean.proxies)
@ -105,20 +104,24 @@ fun buildConfig(
val beanList = ArrayList<ProxyEntity>() val beanList = ArrayList<ProxyEntity>()
for (proxyId in bean.proxies) { for (proxyId in bean.proxies) {
val item = beansMap[proxyId] ?: continue val item = beansMap[proxyId] ?: continue
beanList.addAll(item.resolveChain()) beanList.addAll(item.resolveChainInternal())
}
return if (frontProxy == null) {
beanList.asReversed()
} else {
beanList.add(0, frontProxy)
beanList.asReversed()
} }
return beanList.asReversed()
} }
return if (frontProxy == null) { return mutableListOf(this)
mutableListOf(this) }
} else {
mutableListOf(this, frontProxy) fun ProxyEntity.resolveChain(): MutableList<ProxyEntity> {
val frontProxy = group?.frontProxy?.let { SagerDatabase.proxyDao.getById(it) }
val landingProxy = group?.landingProxy?.let { SagerDatabase.proxyDao.getById(it) }
val list = resolveChainInternal()
if (frontProxy != null) {
list.add(frontProxy)
} }
if (landingProxy != null) {
list.add(0, landingProxy)
}
return list
} }
val extraRules = if (forTest) listOf() else SagerDatabase.rulesDao.enabledRules() val extraRules = if (forTest) listOf() else SagerDatabase.rulesDao.enabledRules()

View File

@ -37,14 +37,18 @@ class GroupSettingsActivity(
OnPreferenceDataStoreChangeListener { OnPreferenceDataStoreChangeListener {
private lateinit var frontProxyPreference: OutboundPreference private lateinit var frontProxyPreference: OutboundPreference
private lateinit var landingProxyPreference: OutboundPreference
fun ProxyGroup.init() { fun ProxyGroup.init() {
DataStore.groupName = name ?: "" DataStore.groupName = name ?: ""
DataStore.groupType = type DataStore.groupType = type
DataStore.groupOrder = order DataStore.groupOrder = order
DataStore.groupIsSelector = isSelector DataStore.groupIsSelector = isSelector
DataStore.routeOutboundRule = frontProxy
DataStore.routeOutbound = if (frontProxy >= 0) 3 else 0 DataStore.frontProxy = frontProxy
DataStore.landingProxy = landingProxy
DataStore.frontProxyTmp = if (frontProxy >= 0) 3 else 0
DataStore.landingProxyTmp = if (landingProxy >= 0) 3 else 0
val subscription = subscription ?: SubscriptionBean().applyDefaultValues() val subscription = subscription ?: SubscriptionBean().applyDefaultValues()
DataStore.subscriptionLink = subscription.link DataStore.subscriptionLink = subscription.link
@ -61,7 +65,9 @@ class GroupSettingsActivity(
type = DataStore.groupType type = DataStore.groupType
order = DataStore.groupOrder order = DataStore.groupOrder
isSelector = DataStore.groupIsSelector isSelector = DataStore.groupIsSelector
frontProxy = if (DataStore.routeOutbound == 3) DataStore.routeOutboundRule else -1
frontProxy = if (DataStore.frontProxyTmp == 3) DataStore.frontProxy else -1
landingProxy = if (DataStore.landingProxyTmp == 3) DataStore.landingProxy else -1
val isSubscription = type == GroupType.SUBSCRIPTION val isSubscription = type == GroupType.SUBSCRIPTION
if (isSubscription) { if (isSubscription) {
@ -88,13 +94,28 @@ class GroupSettingsActivity(
) { ) {
addPreferencesFromResource(R.xml.group_preferences) addPreferencesFromResource(R.xml.group_preferences)
frontProxyPreference = findPreference(Key.ROUTE_OUTBOUND)!! frontProxyPreference = findPreference(Key.GROUP_FRONT_PROXY)!!
frontProxyPreference.apply { frontProxyPreference.apply {
entries = listOf("None", "Select...").toTypedArray() setEntries(R.array.front_proxy_entry)
entryValues = listOf("0", "3").toTypedArray() setEntryValues(R.array.front_proxy_value)
setOnPreferenceChangeListener { _, newValue -> setOnPreferenceChangeListener { _, newValue ->
if (newValue.toString() == "3") { if (newValue.toString() == "3") {
selectProfileForAdd.launch( selectProfileForAddFront.launch(
Intent(this@GroupSettingsActivity, ProfileSelectActivity::class.java)
)
false
} else {
true
}
}
}
landingProxyPreference = findPreference(Key.GROUP_LANDING_PROXY)!!
landingProxyPreference.apply {
setEntries(R.array.front_proxy_entry)
setEntryValues(R.array.front_proxy_value)
setOnPreferenceChangeListener { _, newValue ->
if (newValue.toString() == "3") {
selectProfileForAddLanding.launch(
Intent(this@GroupSettingsActivity, ProfileSelectActivity::class.java) Intent(this@GroupSettingsActivity, ProfileSelectActivity::class.java)
) )
false false
@ -119,8 +140,6 @@ class GroupSettingsActivity(
true true
} }
val subscriptionUserAgent =
findPreference<UserAgentPreference>(Key.SUBSCRIPTION_USER_AGENT)!!
val subscriptionAutoUpdate = val subscriptionAutoUpdate =
findPreference<SwitchPreference>(Key.SUBSCRIPTION_AUTO_UPDATE)!! findPreference<SwitchPreference>(Key.SUBSCRIPTION_AUTO_UPDATE)!!
val subscriptionAutoUpdateDelay = val subscriptionAutoUpdateDelay =
@ -339,20 +358,32 @@ class GroupSettingsActivity(
} }
val selectProfileForAdd = registerForActivityResult( val selectProfileForAddFront = registerForActivityResult(
ActivityResultContracts.StartActivityForResult() ActivityResultContracts.StartActivityForResult()
) { ) {
if (it.resultCode == Activity.RESULT_OK) runOnDefaultDispatcher { if (it.resultCode == Activity.RESULT_OK) runOnDefaultDispatcher {
val profile = ProfileManager.getProfile( val profile = ProfileManager.getProfile(
it.data!!.getLongExtra( it.data!!.getLongExtra(ProfileSelectActivity.EXTRA_PROFILE_ID, 0)
ProfileSelectActivity.EXTRA_PROFILE_ID, 0
)
) ?: return@runOnDefaultDispatcher ) ?: return@runOnDefaultDispatcher
DataStore.routeOutboundRule = profile.id DataStore.frontProxy = profile.id
onMainDispatcher { onMainDispatcher {
frontProxyPreference.value = "3" frontProxyPreference.value = "3"
} }
} }
} }
val selectProfileForAddLanding = registerForActivityResult(
ActivityResultContracts.StartActivityForResult()
) {
if (it.resultCode == Activity.RESULT_OK) runOnDefaultDispatcher {
val profile = ProfileManager.getProfile(
it.data!!.getLongExtra(ProfileSelectActivity.EXTRA_PROFILE_ID, 0)
) ?: return@runOnDefaultDispatcher
DataStore.landingProxy = profile.id
onMainDispatcher {
landingProxyPreference.value = "3"
}
}
}
} }

View File

@ -19,7 +19,7 @@ class OutboundPreference
override fun getSummary(): CharSequence? { override fun getSummary(): CharSequence? {
if (value == "3") { if (value == "3") {
val routeOutbound = DataStore.routeOutboundRule val routeOutbound = DataStore.profileCacheStore.getLong(key + "Long") ?: 0
if (routeOutbound > 0) { if (routeOutbound > 0) {
ProfileManager.getProfile(routeOutbound)?.displayName()?.let { ProfileManager.getProfile(routeOutbound)?.displayName()?.let {
return it return it

View File

@ -247,6 +247,16 @@
<item>3</item> <item>3</item>
</string-array> </string-array>
<string-array name="front_proxy_entry">
<item>@string/ssh_auth_type_none</item>
<item>@string/route_profile</item>
</string-array>
<string-array name="front_proxy_value">
<item>0</item>
<item>3</item>
</string-array>
<string-array name="networks_value"> <string-array name="networks_value">
<item>tcp</item> <item>tcp</item>
<item>ws</item> <item>ws</item>

View File

@ -504,5 +504,6 @@ Anyone can write advanced plugins, which can control NekoBox. please download an
<string name="need_restart">Restart APP to apply changes</string> <string name="need_restart">Restart APP to apply changes</string>
<string name="use_selector">Use selector</string> <string name="use_selector">Use selector</string>
<string name="front_proxy">Front proxy</string> <string name="front_proxy">Front proxy</string>
<string name="landing_proxy">Landing Proxy</string>
</resources> </resources>

View File

@ -22,10 +22,16 @@
<io.nekohasekai.sagernet.widget.OutboundPreference <io.nekohasekai.sagernet.widget.OutboundPreference
app:icon="@drawable/ic_hardware_router" app:icon="@drawable/ic_hardware_router"
app:key="routeOutbound" app:key="groupFrontProxy"
app:title="@string/front_proxy" app:title="@string/front_proxy"
app:useSimpleSummaryProvider="true" /> app:useSimpleSummaryProvider="true" />
<io.nekohasekai.sagernet.widget.OutboundPreference
app:icon="@drawable/baseline_public_24"
app:key="groupLandingProxy"
app:title="@string/landing_proxy"
app:useSimpleSummaryProvider="true" />
<PreferenceCategory <PreferenceCategory
app:key="groupSubscription" app:key="groupSubscription"
app:title="@string/subscription_settings"> app:title="@string/subscription_settings">