diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt index cf866b81e..b21487e95 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/AppConfig.kt @@ -44,6 +44,10 @@ object AppConfig { const val PREF_MUX_CONCURRENCY = "pref_mux_concurency" const val PREF_MUX_XUDP_CONCURRENCY = "pref_mux_xudp_concurency" const val PREF_MUX_XUDP_QUIC = "pref_mux_xudp_quic" + const val PREF_FRAGMENT_ENABLED = "pref_fragment_enabled" + const val PREF_FRAGMENT_PACKETS = "pref_fragment_packets" + const val PREF_FRAGMENT_LENGTH = "pref_fragment_length" + const val PREF_FRAGMENT_INTERVAL = "pref_fragment_interval" const val HTTP_PROTOCOL: String = "http://" const val HTTPS_PROTOCOL: String = "https://" diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt index b4cd3e164..38333919b 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/dto/ServerConfig.kt @@ -56,6 +56,10 @@ data class ServerConfig( return fullConfig?.getProxyOutbound() } + fun getFragmentOutbound(): V2rayConfig.OutboundBean? { + return fullConfig?.getFragmentOutbound() + } + fun getAllOutboundTags(): MutableList { if (configType != EConfigType.CUSTOM) { return mutableListOf(TAG_AGENT, TAG_DIRECT, TAG_BLOCKED) 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 f554e6cae..19a9e792a 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 @@ -61,13 +61,13 @@ data class V2rayConfig( val metadataOnly: Boolean? = null) } - data class OutboundBean(val tag: String = "proxy", + data class OutboundBean(var tag: String = "proxy", var protocol: String, var settings: OutSettingsBean? = null, var streamSettings: StreamSettingsBean? = null, val proxySettings: Any? = null, val sendThrough: String? = null, - val mux: MuxBean? = MuxBean(false)) { + var mux: MuxBean? = MuxBean(false)) { data class OutSettingsBean(var vnext: List? = null, var fragment: FragmentBean? = null, @@ -141,7 +141,7 @@ data class V2rayConfig( var realitySettings: TlsSettingsBean? = null, var grpcSettings: GrpcSettingsBean? = null, val dsSettings: Any? = null, - val sockopt: SockoptBean? = null + var sockopt: SockoptBean? = null ) { data class TcpSettingsBean(var header: HeaderBean = HeaderBean(), @@ -475,6 +475,15 @@ data class V2rayConfig( return null } + fun getFragmentOutbound(): OutboundBean? { + outbounds.forEach { outbound -> + if (outbound.protocol == "freedom" && outbound.tag == "fragment") { + return outbound + } + } + return null + } + fun toPrettyPrinting(): String { return GsonBuilder() .setPrettyPrinting() diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt index b9e1dd04f..83fa9eeea 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/ui/SettingsActivity.kt @@ -41,6 +41,10 @@ class SettingsActivity : BaseActivity() { private val muxXudpConcurrency by lazy { findPreference(AppConfig.PREF_MUX_XUDP_CONCURRENCY) } private val muxXudpQuic by lazy { findPreference(AppConfig.PREF_MUX_XUDP_QUIC) } + private val fragment by lazy { findPreference(AppConfig.PREF_FRAGMENT_ENABLED) } + private val fragmentPackets by lazy { findPreference(AppConfig.PREF_FRAGMENT_PACKETS) } + private val fragmentLength by lazy { findPreference(AppConfig.PREF_FRAGMENT_LENGTH) } + private val fragmentInterval by lazy { findPreference(AppConfig.PREF_FRAGMENT_INTERVAL) } // val autoRestart by lazy { findPreference(PREF_AUTO_RESTART) as CheckBoxPreference } private val remoteDns by lazy { findPreference(AppConfig.PREF_REMOTE_DNS) } @@ -168,6 +172,23 @@ class SettingsActivity : BaseActivity() { updateMuxXudpConcurrency(newValue as String) true } + + fragment?.setOnPreferenceChangeListener { _, newValue -> + updateFragment(newValue as Boolean) + true + } + fragmentPackets?.setOnPreferenceChangeListener { _, newValue -> + updateFragmentPackets(newValue as String) + true + } + fragmentLength?.setOnPreferenceChangeListener { _, newValue -> + updateFragmentLength(newValue as String) + true + } + fragmentInterval?.setOnPreferenceChangeListener { _, newValue -> + updateFragmentInterval(newValue as String) + true + } } override fun onStart() { @@ -184,6 +205,10 @@ class SettingsActivity : BaseActivity() { updateMux(defaultSharedPreferences.getBoolean(AppConfig.PREF_MUX_ENABLED, false)) muxConcurrency?.summary = defaultSharedPreferences.getString(AppConfig.PREF_MUX_CONCURRENCY, "8") muxXudpConcurrency?.summary = defaultSharedPreferences.getString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8") + updateFragment(defaultSharedPreferences.getBoolean(AppConfig.PREF_FRAGMENT_ENABLED, false)) + fragmentPackets?.summary = defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello") + fragmentLength?.summary = defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100") + fragmentInterval?.summary = defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20") autoUpdateInterval?.summary = defaultSharedPreferences.getString(AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL,AppConfig.SUBSCRIPTION_DEFAULT_UPDATE_INTERVAL) autoUpdateInterval?.isEnabled = defaultSharedPreferences.getBoolean(AppConfig.SUBSCRIPTION_AUTO_UPDATE, false) @@ -261,9 +286,9 @@ class SettingsActivity : BaseActivity() { private fun updateMux(enabled: Boolean) { val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()) - muxConcurrency?.isEnabled = enabled - muxXudpConcurrency?.isEnabled = enabled - muxXudpQuic?.isEnabled = enabled + muxConcurrency?.isVisible = enabled + muxXudpConcurrency?.isVisible = enabled + muxXudpQuic?.isVisible = enabled if (enabled) { updateMuxConcurrency(defaultSharedPreferences.getString(AppConfig.PREF_MUX_CONCURRENCY, "8")) updateMuxXudpConcurrency(defaultSharedPreferences.getString(AppConfig.PREF_MUX_XUDP_CONCURRENCY, "8")) @@ -287,6 +312,27 @@ class SettingsActivity : BaseActivity() { muxXudpQuic?.isEnabled = concurrency >= 0 } } + + private fun updateFragment(enabled: Boolean) { + val defaultSharedPreferences = PreferenceManager.getDefaultSharedPreferences(requireActivity()) + fragmentPackets?.isVisible = enabled + fragmentLength?.isVisible = enabled + fragmentInterval?.isVisible = enabled + if (enabled) { + updateFragmentPackets(defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_PACKETS, "tlshello")) + updateFragmentLength(defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_LENGTH, "50-100")) + updateFragmentInterval(defaultSharedPreferences.getString(AppConfig.PREF_FRAGMENT_INTERVAL, "10-20")) + } + } + private fun updateFragmentPackets(value: String?) { + fragmentPackets?.summary = value.toString() + } + private fun updateFragmentLength(value: String?) { + fragmentLength?.summary = value.toString() + } + private fun updateFragmentInterval(value: String?) { + fragmentInterval?.summary = value.toString() + } } fun onModeHelpClicked(view: View) { diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt index 84ab151e5..1549ce357 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/util/V2rayConfigUtil.kt @@ -38,7 +38,8 @@ object V2rayConfigUtil { return Result(true, customConfig) } val outbound = config.getProxyOutbound() ?: return Result(false, "") - val result = getV2rayNonCustomConfig(context, outbound) + val fragmentOutbound = config.getFragmentOutbound() ?: V2rayConfig.OutboundBean(protocol = "freedom") + val result = getV2rayNonCustomConfig(context, outbound, fragmentOutbound) //Log.d(ANG_PACKAGE, result.content) return result } catch (e: Exception) { @@ -50,7 +51,7 @@ object V2rayConfigUtil { /** * 生成v2ray的客户端配置文件 */ - private fun getV2rayNonCustomConfig(context: Context, outbound: V2rayConfig.OutboundBean): Result { + private fun getV2rayNonCustomConfig(context: Context, outbound: V2rayConfig.OutboundBean, fragmentOutbound: V2rayConfig.OutboundBean): Result { val result = Result(false, "") //取得默认配置 val assets = Utils.readTextFromAssets(context, "v2ray_config.json") @@ -66,9 +67,12 @@ object V2rayConfigUtil { inbounds(v2rayConfig) - updateOutboundWithGlobalSettings(outbound) + updateOutboundWithGlobalSettings(outbound, fragmentOutbound) v2rayConfig.outbounds[0] = outbound + if (fragmentOutbound.tag == "fragment") { + v2rayConfig.outbounds[1] = fragmentOutbound + } routing(v2rayConfig) @@ -401,7 +405,7 @@ object V2rayConfigUtil { return true } - private fun updateOutboundWithGlobalSettings(outbound: V2rayConfig.OutboundBean): Boolean { + private fun updateOutboundWithGlobalSettings(outbound: V2rayConfig.OutboundBean, fragmentOutbound: V2rayConfig.OutboundBean): Boolean { try { var muxEnabled = settingsStorage?.decodeBool(AppConfig.PREF_MUX_ENABLED, false) val protocol = outbound.protocol @@ -463,11 +467,29 @@ object V2rayConfigUtil { outbound.streamSettings?.tcpSettings?.header?.request?.headers?.Host = host!! } + if(settingsStorage?.decodeBool(AppConfig.PREF_FRAGMENT_ENABLED, false) == true) { + fragmentOutbound.tag = "fragment" + fragmentOutbound.mux = null + fragmentOutbound.settings = V2rayConfig.OutboundBean.OutSettingsBean( + fragment = V2rayConfig.OutboundBean.OutSettingsBean.FragmentBean( + packets = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_PACKETS) ?: "tlshello", + length = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_LENGTH) ?: "50-100", + interval = settingsStorage?.decodeString(AppConfig.PREF_FRAGMENT_INTERVAL) ?: "10-20")) + fragmentOutbound.streamSettings = V2rayConfig.OutboundBean.StreamSettingsBean( + sockopt = V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean( + TcpNoDelay = true, + mark = 255)) + } + if (fragmentOutbound.tag == "fragment") { + val sockopt = outbound.streamSettings?.sockopt ?: V2rayConfig.OutboundBean.StreamSettingsBean.SockoptBean() + sockopt.dialerProxy = "fragment" + sockopt.mark = 255 + outbound.streamSettings?.sockopt = sockopt + } } catch (e: Exception) { e.printStackTrace() return false } return true } - } diff --git a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt index 98230e611..d112dd361 100644 --- a/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt +++ b/V2rayNG/app/src/main/kotlin/com/v2ray/ang/viewmodel/SettingsViewModel.kt @@ -41,6 +41,9 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application AppConfig.PREF_V2RAY_ROUTING_BLOCKED, AppConfig.PREF_V2RAY_ROUTING_DIRECT, AppConfig.SUBSCRIPTION_AUTO_UPDATE_INTERVAL, + AppConfig.PREF_FRAGMENT_PACKETS, + AppConfig.PREF_FRAGMENT_LENGTH, + AppConfig.PREF_FRAGMENT_INTERVAL, AppConfig.PREF_MUX_XUDP_QUIC, -> { settingsStorage?.encode(key, sharedPreferences.getString(key, "")) } @@ -55,6 +58,7 @@ class SettingsViewModel(application: Application) : AndroidViewModel(application AppConfig.PREF_CONFIRM_REMOVE, AppConfig.PREF_START_SCAN_IMMEDIATE, AppConfig.SUBSCRIPTION_AUTO_UPDATE, + AppConfig.PREF_FRAGMENT_ENABLED, AppConfig.PREF_MUX_ENABLED, -> { settingsStorage?.encode(key, sharedPreferences.getBoolean(key, false)) } diff --git a/V2rayNG/app/src/main/res/values/arrays.xml b/V2rayNG/app/src/main/res/values/arrays.xml index 38a20734f..c64f0fb39 100644 --- a/V2rayNG/app/src/main/res/values/arrays.xml +++ b/V2rayNG/app/src/main/res/values/arrays.xml @@ -60,6 +60,10 @@ reality + + tlshello + + chrome diff --git a/V2rayNG/app/src/main/res/values/strings.xml b/V2rayNG/app/src/main/res/values/strings.xml index a518741ca..c14de35ca 100644 --- a/V2rayNG/app/src/main/res/values/strings.xml +++ b/V2rayNG/app/src/main/res/values/strings.xml @@ -240,6 +240,10 @@ Subscription imported Successfully Import subscription failed + Fragment Packets + Fragment Length (min-max) + Fragment Interval (min-max) + Enable Fragment QRcode diff --git a/V2rayNG/app/src/main/res/xml/pref_settings.xml b/V2rayNG/app/src/main/res/xml/pref_settings.xml index b4793db95..0eb159ab6 100644 --- a/V2rayNG/app/src/main/res/xml/pref_settings.xml +++ b/V2rayNG/app/src/main/res/xml/pref_settings.xml @@ -32,6 +32,28 @@ android:summary="%s" android:title="@string/title_pref_mux_xudp_quic" /> + + + + + + + +