diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt index 4dd19c8f..0e6f577c 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/ProfileItem.kt @@ -42,6 +42,7 @@ data class ProfileItem( var insecure: Boolean? = null, var echConfigList: String? = null, var echForceQuery: String? = null, + var pinnedCA256: String? = null, var publicKey: String? = null, var shortId: String? = null, diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt index 4affbaa8..9455e58e 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/dto/V2rayConfig.kt @@ -271,6 +271,7 @@ data class V2rayConfig( val enableSessionResumption: Boolean? = null, var echConfigList: String? = null, var echForceQuery: String? = null, + var pinnedPeerCertSha256: String? = null, // REALITY settings val show: Boolean = false, var publicKey: String? = null, diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt index 75e60923..c78661ca 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/extension/_Ext.kt @@ -11,6 +11,7 @@ import com.v2ray.ang.AngApplication import es.dmoral.toasty.Toasty import java.io.Serializable import java.net.URI +import java.util.Locale val Context.v2RayApplication: AngApplication? get() = applicationContext as? AngApplication @@ -92,7 +93,7 @@ fun Long.toTrafficString(): String { size /= DIVISOR unitIndex++ } - return String.format("%.1f %s", size, units[unitIndex]) + return String.format(Locale.getDefault(), "%.1f %s", size, units[unitIndex]) } val URI.idnHost: String @@ -105,6 +106,13 @@ val URI.idnHost: String */ fun String?.removeWhiteSpace(): String? = this?.replace(" ", "") +/** + * Returns null if the string is null or blank, otherwise returns the string itself. + * + * @return The string or null. + */ +fun String?.nullIfBlank(): String? = this?.takeIf { it.isNotBlank() } + /** * Converts the string to a Long value, or returns 0 if the conversion fails. * diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt index 6d1fb4d3..97357cb4 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/FmtBase.kt @@ -83,6 +83,7 @@ open class FmtBase { config.fingerPrint = queryParam["fp"] config.alpn = queryParam["alpn"] config.echConfigList = queryParam["ech"] + config.pinnedCA256 = queryParam["pcs"] config.publicKey = queryParam["pbk"] config.shortId = queryParam["sid"] config.spiderX = queryParam["spx"] @@ -102,6 +103,7 @@ open class FmtBase { config.sni.let { if (it.isNotNullEmpty()) dicQuery["sni"] = it.orEmpty() } config.alpn.let { if (it.isNotNullEmpty()) dicQuery["alpn"] = it.orEmpty() } config.echConfigList.let { if (it.isNotNullEmpty()) dicQuery["ech"] = it.orEmpty() } + config.pinnedCA256.let { if (it.isNotNullEmpty()) dicQuery["pcs"] = it.orEmpty() } config.fingerPrint.let { if (it.isNotNullEmpty()) dicQuery["fp"] = it.orEmpty() } config.publicKey.let { if (it.isNotNullEmpty()) dicQuery["pbk"] = it.orEmpty() } config.shortId.let { if (it.isNotNullEmpty()) dicQuery["sid"] = it.orEmpty() } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt index e97b6c56..6ec3eac7 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/handler/V2rayConfigManager.kt @@ -16,6 +16,7 @@ import com.v2ray.ang.dto.V2rayConfig.OutboundBean.OutSettingsBean import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean import com.v2ray.ang.dto.V2rayConfig.RoutingBean.RulesBean import com.v2ray.ang.extension.isNotNullEmpty +import com.v2ray.ang.extension.nullIfBlank import com.v2ray.ang.fmt.HttpFmt import com.v2ray.ang.fmt.Hysteria2Fmt import com.v2ray.ang.fmt.ShadowsocksFmt @@ -1314,6 +1315,7 @@ object V2rayConfigManager { alpn = if (alpns.isNullOrEmpty()) null else alpns.split(",").map { it.trim() }.filter { it.isNotEmpty() }, echConfigList = if (echConfigList.isNullOrEmpty()) null else echConfigList, echForceQuery = if (echForceQuery.isNullOrEmpty()) null else echForceQuery, + pinnedPeerCertSha256 = profileItem.pinnedCA256?.nullIfBlank(), publicKey = if (publicKey.isNullOrEmpty()) null else publicKey, shortId = if (shortId.isNullOrEmpty()) null else shortId, spiderX = if (spiderX.isNullOrEmpty()) null else spiderX, diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt index 7614f19a..8852f972 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/ui/ServerActivity.kt @@ -137,6 +137,8 @@ class ServerActivity : BaseActivity() { private val container_ech_config_list: LinearLayout? by lazy { findViewById(R.id.lay_ech_config_list) } private val sp_ech_force_query: Spinner? by lazy { findViewById(R.id.sp_ech_force_query) } private val container_ech_force_query: LinearLayout? by lazy { findViewById(R.id.lay_ech_force_query) } + private val et_pinned_ca256: EditText? by lazy { findViewById(R.id.et_pinned_ca256) } + private val container_pinned_ca256: LinearLayout? by lazy { findViewById(R.id.lay_pinned_ca256) } override fun onCreate(savedInstanceState: Bundle?) { @@ -274,7 +276,8 @@ class ServerActivity : BaseActivity() { container_spider_x, container_mldsa65_verify, container_ech_config_list, - container_ech_force_query + container_ech_force_query, + container_pinned_ca256 ).forEach { it?.visibility = View.GONE } } @@ -286,7 +289,8 @@ class ServerActivity : BaseActivity() { container_alpn, container_allow_insecure, container_ech_config_list, - container_ech_force_query + container_ech_force_query, + container_pinned_ca256 ).forEach { it?.visibility = View.VISIBLE } listOf( container_public_key, @@ -306,7 +310,8 @@ class ServerActivity : BaseActivity() { container_alpn, container_allow_insecure, container_ech_config_list, - container_ech_force_query + container_ech_force_query, + container_pinned_ca256 ).forEach { it?.visibility = View.GONE } listOf( container_public_key, @@ -394,6 +399,7 @@ class ServerActivity : BaseActivity() { val index = Utils.arrayFind(echForceQuerys, it) index.let { sp_ech_force_query?.setSelection(if (it >= 0) it else 0) } } + et_pinned_ca256?.text = Utils.getEditable(config.pinnedCA256) } else if (config.security == REALITY) { et_public_key?.text = Utils.getEditable(config.publicKey.orEmpty()) et_short_id?.text = Utils.getEditable(config.shortId.orEmpty()) @@ -566,6 +572,7 @@ class ServerActivity : BaseActivity() { val mldsa65Verify = et_mldsa65_verify?.text?.toString() val echConfigList = et_ech_config_list?.text?.toString() val echForceQueryIndex = sp_ech_force_query?.selectedItemPosition ?: 0 + val pinnedCA256 = et_pinned_ca256?.text?.toString() val allowInsecure = if (allowInsecureField == null || allowinsecures[allowInsecureField].isBlank()) { @@ -585,6 +592,7 @@ class ServerActivity : BaseActivity() { config.mldsa65Verify = mldsa65Verify config.echConfigList = echConfigList config.echForceQuery = echForceQuerys[echForceQueryIndex] + config.pinnedCA256 = pinnedCA256 } private fun transportTypes(network: String?): Array { diff --git a/V2rayNG/app/src/main/res/layout/layout_tls.xml b/V2rayNG/app/src/main/res/layout/layout_tls.xml index c75a79f0..15de8788 100644 --- a/V2rayNG/app/src/main/res/layout/layout_tls.xml +++ b/V2rayNG/app/src/main/res/layout/layout_tls.xml @@ -158,6 +158,27 @@ android:entries="@array/ech_force_query_value" /> + + + + + + + + XHTTP Extra raw JSON, format: { XHTTPObject } EchConfigList EchForceQuery + Certificate fingerprint (SHA-256) فشل نسخ الملف، يرجى استخدام مدير الملفات diff --git a/V2rayNG/app/src/main/res/values-bn/strings.xml b/V2rayNG/app/src/main/res/values-bn/strings.xml index c1a4e5bd..b72b0f30 100644 --- a/V2rayNG/app/src/main/res/values-bn/strings.xml +++ b/V2rayNG/app/src/main/res/values-bn/strings.xml @@ -115,6 +115,7 @@ XHTTP Extra raw JSON, format: { XHTTPObject } EchConfigList EchForceQuery + Certificate fingerprint (SHA-256) ফাইল কপি ব্যর্থ, অনুগ্রহ করে ফাইল ম্যানেজার ব্যবহার করুন diff --git a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml index 125ed7e1..a804cacc 100644 --- a/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml +++ b/V2rayNG/app/src/main/res/values-bqi-rIR/strings.xml @@ -115,6 +115,7 @@ XHTTP Extra خام JSON، قالوو: { XHTTPObject } نومگه کانفیگ Ech پورس وو جۊ اجباری Ech + Certificate fingerprint (SHA-256) لف گیری فایل ٱنجوم نوابی، ز ی برنومه دؽوۉداری فایل هیاری بگرین diff --git a/V2rayNG/app/src/main/res/values-fa/strings.xml b/V2rayNG/app/src/main/res/values-fa/strings.xml index 78907e13..4f356a91 100644 --- a/V2rayNG/app/src/main/res/values-fa/strings.xml +++ b/V2rayNG/app/src/main/res/values-fa/strings.xml @@ -115,6 +115,7 @@ خام JSON XHTTP Extra، قالب: { XHTTPObject } لیست کانفیگ Ech EchForceQuery + Certificate fingerprint (SHA-256) کپی فایل انجام نشد، لطفا از برنامه مدیریت فایل استفاده کنید diff --git a/V2rayNG/app/src/main/res/values-ru/strings.xml b/V2rayNG/app/src/main/res/values-ru/strings.xml index 0c71cccc..47f8a270 100644 --- a/V2rayNG/app/src/main/res/values-ru/strings.xml +++ b/V2rayNG/app/src/main/res/values-ru/strings.xml @@ -115,6 +115,7 @@ Необработанный JSON XHTTP Extra, формат: { XHTTPObject } EchConfigList EchForceQuery + Certificate fingerprint (SHA-256) Невозможно скопировать файл, используйте файловый менеджер diff --git a/V2rayNG/app/src/main/res/values-vi/strings.xml b/V2rayNG/app/src/main/res/values-vi/strings.xml index 23288c23..0b62575f 100644 --- a/V2rayNG/app/src/main/res/values-vi/strings.xml +++ b/V2rayNG/app/src/main/res/values-vi/strings.xml @@ -115,6 +115,7 @@ XHTTP Extra raw JSON, format: { XHTTPObject } EchConfigList EchForceQuery + Certificate fingerprint (SHA-256) Không thể sao chép tệp tin, hãy dùng trình quản lý tệp! diff --git a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml index c9e2dbc5..18f2044b 100644 --- a/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rCN/strings.xml @@ -115,6 +115,7 @@ XHTTP Extra 原始 JSON,格式: { XHTTPObject } EchConfigList EchForceQuery + 证书指纹 (SHA-256) 失败, 请使用文件管理器 diff --git a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml index 09e67bef..ffc1ab00 100644 --- a/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml +++ b/V2rayNG/app/src/main/res/values-zh-rTW/strings.xml @@ -115,6 +115,7 @@ XHTTP Extra 原始 JSON,格式: { XHTTPObject } EchConfigList EchForceQuery + 證書指紋 (SHA-256) 失敗,請使用檔案總管 diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index 7e380bcd..561ad994 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -116,6 +116,7 @@ XHTTP Extra raw JSON, format: { XHTTPObject } EchConfigList EchForceQuery + Certificate fingerprint (SHA-256) File copy failed, please use File Manager