@@ -219,6 +219,11 @@ object V2rayConfigManager {
219219 resolveOutboundDomainsToHosts(v2rayConfig)
220220 }
221221
222+ // apply fragment
223+ for (outbound in v2rayConfig.outbounds) {
224+ updateOutboundFragment(outbound)
225+ }
226+
222227 result.status = true
223228 result.content = JsonUtil .toJsonPretty(v2rayConfig) ? : " "
224229 result.guid = guid
@@ -278,6 +283,11 @@ object V2rayConfigManager {
278283 resolveOutboundDomainsToHosts(v2rayConfig)
279284 }
280285
286+ // apply fragment
287+ for (outbound in v2rayConfig.outbounds) {
288+ updateOutboundFragment(outbound)
289+ }
290+
281291 return v2rayConfig
282292 }
283293
@@ -710,8 +720,6 @@ object V2rayConfigManager {
710720 } else {
711721 v2rayConfig.outbounds.add(outbound)
712722 }
713-
714- updateOutboundFragment(v2rayConfig)
715723 return true
716724 }
717725
@@ -955,19 +963,22 @@ object V2rayConfigManager {
955963 *
956964 * Configures packet fragmentation for TLS and REALITY protocols if enabled.
957965 *
958- * @param v2rayConfig The V2ray configuration object to be modified
966+ * @param outbound The outbound object to be modified
959967 * @return true if fragment configuration was successful, false otherwise
960968 */
961- private fun updateOutboundFragment (v2rayConfig : V2rayConfig ): Boolean {
969+ private fun updateOutboundFragment (outbound : OutboundBean ): Boolean {
962970 try {
963971 if (MmkvManager .decodeSettingsBool(AppConfig .PREF_FRAGMENT_ENABLED , false ) == false ) {
964972 return true
965973 }
966- if (v2rayConfig.outbounds[ 0 ] .streamSettings?.security != AppConfig .TLS
967- && v2rayConfig.outbounds[ 0 ] .streamSettings?.security != AppConfig .REALITY
974+ if (outbound .streamSettings?.security != AppConfig .TLS
975+ && outbound .streamSettings?.security != AppConfig .REALITY
968976 ) {
969977 return true
970978 }
979+ if (outbound.streamSettings?.sockopt?.dialerProxy.isNotNullEmpty()) {
980+ return true
981+ }
971982
972983 val fragmentOutbound =
973984 OutboundBean (
@@ -978,45 +989,56 @@ object V2rayConfigManager {
978989
979990 var packets =
980991 MmkvManager .decodeSettingsString(AppConfig .PREF_FRAGMENT_PACKETS ) ? : " tlshello"
981- if (v2rayConfig.outbounds[ 0 ] .streamSettings?.security == AppConfig .REALITY
992+ if (outbound .streamSettings?.security == AppConfig .REALITY
982993 && packets == " tlshello"
983994 ) {
984995 packets = " 1-3"
985- } else if (v2rayConfig.outbounds[ 0 ] .streamSettings?.security == AppConfig .TLS
996+ } else if (outbound .streamSettings?.security == AppConfig .TLS
986997 && packets != " tlshello"
987998 ) {
988999 packets = " tlshello"
9891000 }
9901001
991- fragmentOutbound.settings = OutSettingsBean (
992- fragment = OutSettingsBean .FragmentBean (
1002+ val fragmentMask = StreamSettingsBean .FinalMaskBean .MaskBean (
1003+ type = " fragment" ,
1004+ settings = StreamSettingsBean .FinalMaskBean .MaskBean .MaskSettingsBean (
9931005 packets = packets,
9941006 length = MmkvManager .decodeSettingsString(AppConfig .PREF_FRAGMENT_LENGTH )
9951007 ? : " 50-100" ,
996- interval = MmkvManager .decodeSettingsString(AppConfig .PREF_FRAGMENT_INTERVAL )
1008+ delay = MmkvManager .decodeSettingsString(AppConfig .PREF_FRAGMENT_INTERVAL )
9971009 ? : " 10-20"
998- ),
999- noises = listOf (
1000- OutSettingsBean .NoiseBean (
1001- type = " rand" ,
1002- packet = " 10-20" ,
1003- delay = " 10-16" ,
1004- )
1005- ),
1010+ )
10061011 )
1007- fragmentOutbound.streamSettings = StreamSettingsBean (
1008- sockopt = StreamSettingsBean .SockoptBean (
1009- TcpNoDelay = true ,
1010- mark = 255
1012+ val noiseMask = StreamSettingsBean .FinalMaskBean .MaskBean (
1013+ type = " noise" ,
1014+ settings = StreamSettingsBean .FinalMaskBean .MaskBean .MaskSettingsBean (
1015+ noise = listOf (
1016+ StreamSettingsBean .FinalMaskBean .MaskBean .MaskSettingsBean .NoiseMaskBean (
1017+ rand = " 10-20" ,
1018+ delay = " 10-16" ,
1019+ )
1020+ )
10111021 )
10121022 )
1013- v2rayConfig.outbounds.add(fragmentOutbound)
10141023
1015- // proxy chain
1016- v2rayConfig.outbounds[0 ].streamSettings?.sockopt =
1017- StreamSettingsBean .SockoptBean (
1018- dialerProxy = AppConfig .TAG_FRAGMENT
1019- )
1024+ val finalMaskObj = JsonUtil .parseString(JsonUtil .toJson(outbound.streamSettings?.finalmask))
1025+ ? : com.google.gson.JsonObject ()
1026+
1027+ // finalmask.tcp / finalmask.udp are arrays; prepend mask at index 0.
1028+ fun prependMask (scope : String , mask : StreamSettingsBean .FinalMaskBean .MaskBean ) {
1029+ val newArray = JsonArray ()
1030+ newArray.add(JsonUtil .parseString(JsonUtil .toJson(mask)))
1031+
1032+ val current = finalMaskObj.get(scope)
1033+ if (current != null && current.isJsonArray) {
1034+ current.asJsonArray.forEach { newArray.add(it) }
1035+ }
1036+ finalMaskObj.add(scope, newArray)
1037+ }
1038+
1039+ prependMask(" tcp" , fragmentMask)
1040+ prependMask(" udp" , noiseMask)
1041+ outbound.streamSettings?.finalmask = finalMaskObj
10201042 } catch (e: Exception ) {
10211043 Log .e(AppConfig .TAG , " Failed to update outbound fragment" , e)
10221044 return false
0 commit comments