From 8eca5fe8af0e1768a315bfa890e1b22d878740de Mon Sep 17 00:00:00 2001 From: DHR60 Date: Fri, 6 Feb 2026 11:27:02 +0000 Subject: [PATCH] Add finalmask support (#5242) --- .../java/com/v2ray/ang/dto/V2rayConfig.kt | 31 +++++++------- .../java/com/v2ray/ang/fmt/Hysteria2Fmt.kt | 15 +++---- .../v2ray/ang/handler/V2rayConfigManager.kt | 40 ++++++++++++++----- 3 files changed, 53 insertions(+), 33 deletions(-) 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 ad1685a0..0878ae45 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 @@ -162,7 +162,7 @@ data class V2rayConfig( var realitySettings: TlsSettingsBean? = null, var grpcSettings: GrpcSettingsBean? = null, var hysteriaSettings: HysteriaSettingsBean? = null, - var udpmasks: List? = null, + var finalmask: FinalMaskBean? = null, val dsSettings: Any? = null, var sockopt: SockoptBean? = null ) { @@ -202,15 +202,8 @@ data class V2rayConfig( var downlinkCapacity: Int = 100, var congestion: Boolean = false, var readBufferSize: Int = 1, - var writeBufferSize: Int = 1, - var header: HeaderBean = HeaderBean(), - var seed: String? = null - ) { - data class HeaderBean( - var type: String = "none", - var domain: String? = null - ) - } + var writeBufferSize: Int = 1 + ) data class WsSettingsBean( var path: String? = null, @@ -310,13 +303,19 @@ data class V2rayConfig( ) } - data class UdpMasksBean( - var type: String, - var settings: UdpMasksSettingsBean? = null + data class FinalMaskBean( + var tcp: List? = null, + var udp: List? = null ) { - data class UdpMasksSettingsBean( - var password: String? = null - ) + data class MaskBean( + var type: String, + var settings: MaskSettingsBean? = null + ) { + data class MaskSettingsBean( + var password: String? = null, + var domain: String? = null + ) + } } } diff --git a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt index 1b461b0c..b136e3b0 100644 --- a/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt +++ b/V2rayNG/app/src/main/java/com/v2ray/ang/fmt/Hysteria2Fmt.kt @@ -5,8 +5,7 @@ import com.v2ray.ang.enums.EConfigType import com.v2ray.ang.enums.NetworkType import com.v2ray.ang.dto.ProfileItem import com.v2ray.ang.dto.V2rayConfig.OutboundBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean -import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.UdpMasksBean.UdpMasksSettingsBean +import com.v2ray.ang.dto.V2rayConfig.OutboundBean.StreamSettingsBean.FinalMaskBean import com.v2ray.ang.extension.idnHost import com.v2ray.ang.extension.isNotNullEmpty import com.v2ray.ang.extension.nullIfBlank @@ -109,11 +108,13 @@ object Hysteria2Fmt : FmtBase() { } if (profileItem.obfsPassword.isNotNullEmpty()) { - outboundBean.streamSettings?.udpmasks = mutableListOf( - StreamSettingsBean.UdpMasksBean( - type = "salamander", - settings = UdpMasksSettingsBean( - password = profileItem.obfsPassword + outboundBean.streamSettings?.finalmask = FinalMaskBean( + udp = listOf( + FinalMaskBean.MaskBean( + type = "salamander", + settings = FinalMaskBean.MaskBean.MaskSettingsBean( + password = profileItem.obfsPassword + ) ) ) ) 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 117453cf..7978e3c2 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 @@ -1183,19 +1183,39 @@ object V2rayConfigManager { } NetworkType.KCP.type -> { - val kcpsetting = StreamSettingsBean.KcpSettingsBean() - kcpsetting.header.type = headerType ?: "none" + streamSettings.kcpSettings = StreamSettingsBean.KcpSettingsBean() + val udpMaskList = mutableListOf() + if (!headerType.isNullOrEmpty() && headerType != "none") { + val kcpHeaderType = when { + headerType == "wechat-video" -> "header-wechat" + else -> "header-$headerType" + } + udpMaskList.add(StreamSettingsBean.FinalMaskBean.MaskBean( + type = kcpHeaderType, + settings = if (headerType == "dns" && !host.isNullOrEmpty()) { + StreamSettingsBean.FinalMaskBean.MaskBean.MaskSettingsBean( + domain = host + ) + } else { + null + } + )) + } if (seed.isNullOrEmpty()) { - kcpsetting.seed = null + udpMaskList.add(StreamSettingsBean.FinalMaskBean.MaskBean( + type = "mkcp-original" + )) } else { - kcpsetting.seed = seed + udpMaskList.add(StreamSettingsBean.FinalMaskBean.MaskBean( + type = "mkcp-aes128gcm", + settings = StreamSettingsBean.FinalMaskBean.MaskBean.MaskSettingsBean( + password = seed + ) + )) } - if (host.isNullOrEmpty()) { - kcpsetting.header.domain = null - } else { - kcpsetting.header.domain = host - } - streamSettings.kcpSettings = kcpsetting + streamSettings.finalmask = StreamSettingsBean.FinalMaskBean( + udp = udpMaskList.toList() + ) } NetworkType.WS.type -> {