diff --git a/V2rayNG/app/build.gradle b/V2rayNG/app/build.gradle index 54441972..f7ac3a38 100644 --- a/V2rayNG/app/build.gradle +++ b/V2rayNG/app/build.gradle @@ -9,7 +9,7 @@ android { buildToolsVersion "$buildToolsVer" compileOptions { - targetCompatibility = "8" + targetCompatibility = "11" sourceCompatibility = "8" } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt index fc9c7840..fe5815c5 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/V2rayConfig.kt @@ -259,11 +259,12 @@ data class V2rayConfig( return sni } - fun populateTlsSettings(streamSecurity: String, allowInsecure: Boolean, sni: String) { + fun populateTlsSettings(streamSecurity: String, allowInsecure: Boolean, sni: String, fingerprint: String?) { security = streamSecurity val tlsSetting = TlsSettingsBean( allowInsecure = allowInsecure, - serverName = sni + serverName = sni, + fingerprint = fingerprint ) if (security == TLS) { tlsSettings = tlsSetting diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt index 6f44a090..3c5768de 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/ServerActivity.kt @@ -67,7 +67,9 @@ class ServerActivity : BaseActivity() { private val allowinsecures: Array by lazy { resources.getStringArray(R.array.allowinsecures) } - + private val uTlsItems: Array by lazy { + resources.getStringArray(R.array.streamsecurity_utls) + } // Kotlin synthetics was used, but since it is removed in 1.8. We switch to old manual approach. // We don't use AndroidViewBinding because, it is better to share similar logics for different // protocols. Use findViewById manually ensures the xml are de-coupled with the activity logic. @@ -82,6 +84,7 @@ class ServerActivity : BaseActivity() { private val sp_stream_security: Spinner? by lazy { findViewById(R.id.sp_stream_security) } private val sp_allow_insecure: Spinner? by lazy { findViewById(R.id.sp_allow_insecure) } private val et_sni: EditText? by lazy { findViewById(R.id.et_sni) } + private val sp_stream_fingerprint: Spinner? by lazy { findViewById(R.id.sp_stream_fingerprint) } //uTLS private val sp_network: Spinner? by lazy { findViewById(R.id.sp_network) } private val sp_header_type: Spinner? by lazy { findViewById(R.id.sp_header_type) } private val sp_header_type_title: TextView? by lazy { findViewById(R.id.sp_header_type_title) } @@ -170,6 +173,12 @@ class ServerActivity : BaseActivity() { sp_allow_insecure?.setSelection(allowinsecure) } et_sni?.text = Utils.getEditable(tlsSetting.serverName) + + tlsSetting.fingerprint?.let { + val utlsIndex = Utils.arrayFind(uTlsItems, tlsSetting.fingerprint) + sp_stream_fingerprint?.setSelection(utlsIndex) + } + } } val network = Utils.arrayFind(networks, streamSetting.network) @@ -310,6 +319,7 @@ class ServerActivity : BaseActivity() { val sniField = et_sni?.text?.toString()?.trim() ?: return val allowInsecureField = sp_allow_insecure?.selectedItemPosition ?: return val streamSecurity = sp_stream_security?.selectedItemPosition ?: return + var utlsIndex = sp_stream_fingerprint?.selectedItemPosition ?: return var sni = streamSetting.populateTransportSettings( transport = networks[network], @@ -330,7 +340,8 @@ class ServerActivity : BaseActivity() { } else { allowinsecures[allowInsecureField].toBoolean() } - streamSetting.populateTlsSettings(streamSecuritys[streamSecurity], allowInsecure, sni) + + streamSetting.populateTlsSettings(streamSecuritys[streamSecurity], allowInsecure, sni, uTlsItems[utlsIndex]) } private fun transportTypes(network: String?): Array { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt index 63f41427..43bff542 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/AngConfigManager.kt @@ -143,8 +143,9 @@ object AngConfigManager { } else { vmessBean.allowInsecure.toBoolean() } + var fingerprint = streamSetting.tlsSettings?.fingerprint streamSetting.populateTlsSettings(vmessBean.streamSecurity, allowInsecure, - vmessBean.sni.ifBlank { sni }) + vmessBean.sni.ifBlank { sni }, fingerprint) } } val key = MmkvManager.encodeServerConfig(vmessBean.guid, config) @@ -185,6 +186,9 @@ object AngConfigManager { config = ServerConfig.create(EConfigType.VMESS) val streamSetting = config.outboundBean?.streamSettings ?: return -1 + var fingerprint = streamSetting.tlsSettings?.fingerprint + + if (!tryParseNewVmess(str, config, allowInsecure)) { if (str.indexOf("?") > 0) { if (!tryResolveVmess4Kitsunebi(str, config)) { @@ -216,8 +220,10 @@ object AngConfigManager { } val sni = streamSetting.populateTransportSettings(vmessQRCode.net, vmessQRCode.type, vmessQRCode.host, vmessQRCode.path, vmessQRCode.path, vmessQRCode.host, vmessQRCode.path, vmessQRCode.type, vmessQRCode.path) + + streamSetting.populateTlsSettings(vmessQRCode.tls, allowInsecure, - if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni) + if (TextUtils.isEmpty(vmessQRCode.sni)) sni else vmessQRCode.sni, fingerprint) } } } else if (str.startsWith(EConfigType.SHADOWSOCKS.protocolScheme)) { @@ -292,6 +298,7 @@ object AngConfigManager { config.remarks = Utils.urlDecode(uri.fragment ?: "") var flow = "" + var fingerprint = config.outboundBean?.streamSettings?.tlsSettings?.fingerprint if (uri.rawQuery != null) { val queryParam = uri.rawQuery.split("&") .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } } @@ -299,10 +306,11 @@ object AngConfigManager { val sni = config.outboundBean?.streamSettings?.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"], queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"], queryParam["mode"], queryParam["serviceName"]) - config.outboundBean?.streamSettings?.populateTlsSettings(queryParam["security"] ?: TLS, allowInsecure, queryParam["sni"] ?: sni!!) + config.outboundBean?.streamSettings?.populateTlsSettings(queryParam["security"] ?: TLS, allowInsecure, queryParam["sni"] ?: sni!!, fingerprint) flow = queryParam["flow"] ?: "" } else { - config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, "") + + config.outboundBean?.streamSettings?.populateTlsSettings(TLS, allowInsecure, "", fingerprint) } config.outboundBean?.settings?.servers?.get(0)?.let { server -> @@ -317,6 +325,8 @@ object AngConfigManager { .associate { it.split("=").let { (k, v) -> k to Utils.urlDecode(v) } } config = ServerConfig.create(EConfigType.VLESS) val streamSetting = config.outboundBean?.streamSettings ?: return -1 + var fingerprint = streamSetting.tlsSettings?.fingerprint + config.remarks = Utils.urlDecode(uri.fragment ?: "") config.outboundBean?.settings?.vnext?.get(0)?.let { vnext -> vnext.address = uri.idnHost @@ -329,7 +339,7 @@ object AngConfigManager { val sni = streamSetting.populateTransportSettings(queryParam["type"] ?: "tcp", queryParam["headerType"], queryParam["host"], queryParam["path"], queryParam["seed"], queryParam["quicSecurity"], queryParam["key"], queryParam["mode"], queryParam["serviceName"]) - streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, queryParam["sni"] ?: sni) + streamSetting.populateTlsSettings(queryParam["security"] ?: "", allowInsecure, queryParam["sni"] ?: sni, fingerprint) } if (config == null){ return R.string.toast_incorrect_protocol @@ -369,12 +379,12 @@ object AngConfigManager { vnext.users[0].security = DEFAULT_SECURITY vnext.users[0].alterId = alterId.toInt() } - + var fingerprint = streamSetting.tlsSettings?.fingerprint val sni = streamSetting.populateTransportSettings(protocol, queryParam["type"], queryParam["host"]?.split("|")?.get(0) ?: "", queryParam["path"]?.takeIf { it.trim() != "/" } ?: "", queryParam["seed"], queryParam["security"], queryParam["key"], queryParam["mode"], queryParam["serviceName"]) - streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni) + streamSetting.populateTlsSettings(if (tls) TLS else "", allowInsecure, sni, fingerprint) true }.getOrElse { false } } diff --git a/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml b/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml index fbf6bcaf..757a6c78 100644 --- a/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml +++ b/V2rayNG/app/src/main/res/layout/activity_server_trojan.xml @@ -202,64 +202,7 @@ android:inputType="text" /> - - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - + - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + \ No newline at end of file diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml index d4487fb6..ad0886e1 100644 --- a/V2rayNG/app/src/main/res/values/arrays.xml +++ b/V2rayNG/app/src/main/res/values/arrays.xml @@ -60,6 +60,14 @@ xtls + + + chrome + firefox + safari + randomized + + true diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index 6e047f2b..258d275a 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -49,6 +49,7 @@ request host(host/ws host/h2 host)/QUIC security path(ws path/h2 path)/QUIC key/kcp seed/gRPC serviceName tls + uTLS allowInsecure SNI address