diff --git a/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/36.json b/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/36.json index 61aec12a907..bb1277afaca 100644 --- a/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/36.json +++ b/app/schemas/io.nekohasekai.sagernet.database.SagerDatabase/36.json @@ -2,11 +2,11 @@ "formatVersion": 1, "database": { "version": 36, - "identityHash": "2ad66cce1d4ba5df1fd06814cfaae7e9", + "identityHash": "a767e2240b7af68735ec4f7b90813e3c", "entities": [ { "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, `frontProxy` INTEGER NOT NULL DEFAULT -1, `landingProxy` INTEGER NOT NULL DEFAULT -1)", + "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, `frontProxy` INTEGER NOT NULL DEFAULT -1, `landingProxy` INTEGER NOT NULL DEFAULT -1, `groupUtlsFingerprint` TEXT NOT NULL DEFAULT '')", "fields": [ { "fieldPath": "id", @@ -61,6 +61,13 @@ "affinity": "INTEGER", "notNull": true, "defaultValue": "-1" + }, + { + "fieldPath": "groupUtlsFingerprint", + "columnName": "groupUtlsFingerprint", + "affinity": "TEXT", + "notNull": true, + "defaultValue": "''" } ], "primaryKey": { @@ -455,7 +462,7 @@ ], "setupQueries": [ "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, '2ad66cce1d4ba5df1fd06814cfaae7e9')" + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'a767e2240b7af68735ec4f7b90813e3c')" ] } } \ No newline at end of file diff --git a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt index a5228b63275..b6f856ef723 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/Constants.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/Constants.kt @@ -295,6 +295,7 @@ object Key { const val GROUP_LANDING_PROXY_OUTBOUND = "groupLandingOutbound" const val GROUP_FRONT_PROXY = "groupFrontProxy" const val GROUP_LANDING_PROXY = "groupLandingProxy" + const val GROUP_UTLS_FINGERPRINT = "groupUtlsFingerprint" const val GROUP_SUBSCRIPTION = "groupSubscription" const val SUBSCRIPTION_TYPE = "subscriptionType" 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 50c3a5af3b0..d57a3228e3f 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/database/DataStore.kt @@ -436,6 +436,7 @@ object DataStore : OnPreferenceDataStoreChangeListener { var landingProxyOutbound by profileCacheStore.long(Key.GROUP_LANDING_PROXY_OUTBOUND) var frontProxy by profileCacheStore.stringToInt(Key.GROUP_FRONT_PROXY) var landingProxy by profileCacheStore.stringToInt(Key.GROUP_LANDING_PROXY) + var groupUtlsFingerprint by profileCacheStore.string(Key.GROUP_UTLS_FINGERPRINT) var serverConfig by profileCacheStore.string(Key.SERVER_CONFIG) diff --git a/app/src/main/java/io/nekohasekai/sagernet/database/ProxyGroup.kt b/app/src/main/java/io/nekohasekai/sagernet/database/ProxyGroup.kt index 977450d40eb..7d31c7cdd12 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/database/ProxyGroup.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/database/ProxyGroup.kt @@ -39,7 +39,8 @@ data class ProxyGroup( var subscription: SubscriptionBean? = null, var order: Int = GroupOrder.ORIGIN, @ColumnInfo(defaultValue = (-1L).toString()) var frontProxy: Long = -1L, - @ColumnInfo(defaultValue = (-1L).toString()) var landingProxy: Long = -1L + @ColumnInfo(defaultValue = (-1L).toString()) var landingProxy: Long = -1L, + @ColumnInfo(defaultValue = "") var groupUtlsFingerprint: String = "" ) : Serializable() { @Transient @@ -69,6 +70,7 @@ data class ProxyGroup( output.writeInt(order) output.writeLong(frontProxy) output.writeLong(landingProxy) + output.writeString(groupUtlsFingerprint) } } @@ -97,6 +99,13 @@ data class ProxyGroup( frontProxy = input.readLong() landingProxy = input.readLong() } + if (version >= 1) { + try { + groupUtlsFingerprint = input.readString() + } catch (e: Exception) { + groupUtlsFingerprint = "" + } + } } } 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 4b1a06edf4c..6a7a05f880b 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/fmt/ConfigBuilder.kt @@ -602,6 +602,10 @@ fun buildV2RayConfig( val bean = proxyEntity.requireBean() currentOutbound = OutboundObject() + // Получаем группу для применения группового uTLS + val proxyGroup = SagerDatabase.groupDao.getById(proxyEntity.groupId) + val groupUtls = proxyGroup?.groupUtlsFingerprint?.takeIf { it.isNotEmpty() } + val tagIn: String var needGlobal: Boolean @@ -888,6 +892,8 @@ fun buildV2RayConfig( val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForTLS") if (!overrideFingerprint.isNullOrEmpty()) { fingerprint = overrideFingerprint + } else if (groupUtls != null) { + fingerprint = groupUtls } else if (bean.utlsFingerprint.isNotEmpty()) { fingerprint = bean.utlsFingerprint } @@ -918,6 +924,8 @@ fun buildV2RayConfig( val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForREALITY") if (!overrideFingerprint.isNullOrEmpty()) { fingerprint = overrideFingerprint + } else if (groupUtls != null) { + fingerprint = groupUtls } else if (bean.realityFingerprint.isNotEmpty()) { fingerprint = bean.realityFingerprint } @@ -1616,6 +1624,8 @@ fun buildV2RayConfig( val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForTLS") if (!overrideFingerprint.isNullOrEmpty()) { fingerprint = overrideFingerprint + } else if (groupUtls != null) { + fingerprint = groupUtls } else if (bean.utlsFingerprint.isNotEmpty()) { fingerprint = bean.utlsFingerprint } @@ -1643,6 +1653,8 @@ fun buildV2RayConfig( val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForREALITY") if (!overrideFingerprint.isNullOrEmpty()) { fingerprint = overrideFingerprint + } else if (groupUtls != null) { + fingerprint = groupUtls } else if (bean.realityFingerprint.isNotEmpty()) { fingerprint = bean.realityFingerprint } @@ -1833,6 +1845,8 @@ fun buildV2RayConfig( val overrideFingerprint = DataStore.experimentalFlagsProperties.getProperty("overrideUTLSFingerprintForTLS") if (!overrideFingerprint.isNullOrEmpty()) { fingerprint = overrideFingerprint + } else if (groupUtls != null) { + fingerprint = groupUtls } else if (bean.utlsFingerprint.isNotEmpty()) { fingerprint = bean.utlsFingerprint } diff --git a/app/src/main/java/io/nekohasekai/sagernet/ui/GroupSettingsActivity.kt b/app/src/main/java/io/nekohasekai/sagernet/ui/GroupSettingsActivity.kt index 526d99614ba..bc39ba03bb6 100644 --- a/app/src/main/java/io/nekohasekai/sagernet/ui/GroupSettingsActivity.kt +++ b/app/src/main/java/io/nekohasekai/sagernet/ui/GroupSettingsActivity.kt @@ -102,6 +102,7 @@ class GroupSettingsActivity( DataStore.landingProxyOutbound = landingProxy DataStore.frontProxy = if (frontProxy >= 0) 1 else 0 DataStore.landingProxy = if (landingProxy >= 0) 1 else 0 + DataStore.groupUtlsFingerprint = groupUtlsFingerprint ?: "" } fun ProxyGroup.serialize() { @@ -116,6 +117,7 @@ class GroupSettingsActivity( frontProxy = if (DataStore.frontProxy == 1) DataStore.frontProxyOutbound else -1 landingProxy = if (DataStore.landingProxy == 1) DataStore.landingProxyOutbound else -1 + groupUtlsFingerprint = DataStore.groupUtlsFingerprint if (type == GroupType.SUBSCRIPTION) { subscription = SubscriptionBean().applyDefaultValues().apply { type = DataStore.subscriptionType diff --git a/app/src/main/res/values-ru/strings.xml b/app/src/main/res/values-ru/strings.xml index 800a22f6944..08a9ea5e93c 100644 --- a/app/src/main/res/values-ru/strings.xml +++ b/app/src/main/res/values-ru/strings.xml @@ -441,7 +441,7 @@ URL test Изменить ID URL - uTLS-отпечаток + Принудительный uTLS Правило маршрутизации `%s` требует доступа к вашему **Wi-Fi SSID**, но для этого требуется **включенное местоположение** Правило маршрутизации `%s` требует доступа к вашему **Wi-Fi SSID**, но для этого требуется **разрешение на точное определение местоположения** Загрузочный DNS diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index 499fe41b385..b25dac2ab81 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -334,7 +334,7 @@ Experimental settings Make the length of each payload segment no longer malleable. This experiment requires the server and client use the same version of v2ray-core. More breaking updates on this experiment is expected. Do not send connection single duplex termination signal for TCP connection when transferred over VMess. This will break some applications. - uTLS fingerprint + Force uTLS Sort By original order Alphabetically diff --git a/app/src/main/res/xml/group_preferences.xml b/app/src/main/res/xml/group_preferences.xml index 4f5c5bcf506..e538c200510 100644 --- a/app/src/main/res/xml/group_preferences.xml +++ b/app/src/main/res/xml/group_preferences.xml @@ -37,6 +37,14 @@ app:key="groupLandingProxy" app:title="@string/landing_proxy" /> + +