diff --git a/.github/images/preview.png b/.github/images/preview.png deleted file mode 100644 index 64ca2fd..0000000 Binary files a/.github/images/preview.png and /dev/null differ diff --git a/.github/images/screenshot1.png b/.github/images/screenshot1.png deleted file mode 100644 index 59d9c2d..0000000 Binary files a/.github/images/screenshot1.png and /dev/null differ diff --git a/.github/images/screenshot2.png b/.github/images/screenshot2.png deleted file mode 100644 index 2455879..0000000 Binary files a/.github/images/screenshot2.png and /dev/null differ diff --git a/.github/images/screenshot3.png b/.github/images/screenshot3.png deleted file mode 100644 index a51a254..0000000 Binary files a/.github/images/screenshot3.png and /dev/null differ diff --git a/app/build.gradle.kts b/app/build.gradle.kts index bb0d160..330c540 100644 --- a/app/build.gradle.kts +++ b/app/build.gradle.kts @@ -11,8 +11,8 @@ android { applicationId = "io.github.dovecoteescapee.byedpi" minSdk = 21 targetSdk = 34 - versionCode = 8 - versionName = "1.1.0" + versionCode = 9 + versionName = "1.1.1" testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner" diff --git a/app/src/main/cpp/native-lib.c b/app/src/main/cpp/native-lib.c index eceb14c..e615a0c 100644 --- a/app/src/main/cpp/native-lib.c +++ b/app/src/main/cpp/native-lib.c @@ -89,7 +89,9 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket( jint tls_record_split_position, jboolean tls_record_split_at_sni, jint hosts_mode, - jstring hosts) { + jstring hosts, + jboolean tfo, + jint udp_fake_count) { struct sockaddr_ina s; const char *address = (*env)->GetStringUTFChars(env, ip, 0); @@ -105,6 +107,7 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket( params.max_open = max_connections; params.bfsize = buffer_size; params.resolve = !no_domain; + params.tfo = tfo; if (custom_ttl) { params.def_ttl = default_ttl; @@ -166,6 +169,7 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket( } dp->ttl = fake_ttl; + dp->udp_fake_count = udp_fake_count; dp->proto = IS_HTTP * desync_http | IS_HTTPS * desync_https | diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/core/ByeDpiProxy.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/core/ByeDpiProxy.kt index ec069c3..db78011 100644 --- a/app/src/main/java/io/github/dovecoteescapee/byedpi/core/ByeDpiProxy.kt +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/core/ByeDpiProxy.kt @@ -77,7 +77,9 @@ class ByeDpiProxy { tlsRecordSplitPosition = preferences.tlsRecordSplitPosition, tlsRecordSplitAtSni = preferences.tlsRecordSplitAtSni, hostsMode = preferences.hostsMode.ordinal, - hosts = preferences.hosts + hosts = preferences.hosts, + tcpFastOpen = preferences.tcpFastOpen, + udpFakeCount = preferences.udpFakeCount, ) } @@ -107,7 +109,9 @@ class ByeDpiProxy { tlsRecordSplitPosition: Int, tlsRecordSplitAtSni: Boolean, hostsMode: Int, - hosts: String? + hosts: String?, + tcpFastOpen: Boolean, + udpFakeCount: Int, ): Int private external fun jniStartProxy(fd: Int): Int diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/core/ByeDpiProxyPreferences.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/core/ByeDpiProxyPreferences.kt index 59c0c3e..3f2b17d 100644 --- a/app/src/main/java/io/github/dovecoteescapee/byedpi/core/ByeDpiProxyPreferences.kt +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/core/ByeDpiProxyPreferences.kt @@ -57,6 +57,8 @@ class ByeDpiProxyUIPreferences( tlsRecordSplitAtSni: Boolean? = null, hostsMode: HostsMode? = null, hosts: String? = null, + tcpFastOpen: Boolean? = null, + udpFakeCount: Int? = null, ) : ByeDpiProxyPreferences { val ip: String = ip ?: "127.0.0.1" val port: Int = port ?: 1080 @@ -69,7 +71,7 @@ class ByeDpiProxyUIPreferences( val desyncHttps: Boolean = desyncHttps ?: true val desyncUdp: Boolean = desyncUdp ?: false val desyncMethod: DesyncMethod = desyncMethod ?: DesyncMethod.Disorder - val splitPosition: Int = splitPosition ?: 3 + val splitPosition: Int = splitPosition ?: 2 val splitAtHost: Boolean = splitAtHost ?: false val fakeTtl: Int = fakeTtl ?: 8 val fakeSni: String = fakeSni ?: "www.iana.org" @@ -86,6 +88,8 @@ class ByeDpiProxyUIPreferences( val hosts: String? = if (this.hostsMode == HostsMode.Disable) null else hosts?.trim() + val tcpFastOpen: Boolean = tcpFastOpen ?: false + val udpFakeCount: Int = udpFakeCount ?: 0 constructor(preferences: SharedPreferences) : this( ip = preferences.getString("byedpi_proxy_ip", null), @@ -119,7 +123,9 @@ class ByeDpiProxyUIPreferences( HostsMode.Whitelist -> preferences.getString("byedpi_hosts_whitelist", null) else -> null } - } + }, + tcpFastOpen = preferences.getBoolean("byedpi_tcp_fast_open", false), + udpFakeCount = preferences.getString("byedpi_udp_fake_count", null)?.toIntOrNull(), ) enum class DesyncMethod { diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/fragments/ByeDpiUISettingsFragment.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/fragments/ByeDpiUISettingsFragment.kt index 3b9e88c..ae68339 100644 --- a/app/src/main/java/io/github/dovecoteescapee/byedpi/fragments/ByeDpiUISettingsFragment.kt +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/fragments/ByeDpiUISettingsFragment.kt @@ -79,6 +79,7 @@ class ByeDpiUISettingsFragment : PreferenceFragmentCompat() { val ttlFake = findPreferenceNotNull("byedpi_fake_ttl") val fakeSni = findPreferenceNotNull("byedpi_fake_sni") val oobData = findPreferenceNotNull("byedpi_oob_data") + val udpFakeCount = findPreferenceNotNull("byedpi_udp_fake_count") val hostMixedCase = findPreferenceNotNull("byedpi_host_mixed_case") val domainMixedCase = findPreferenceNotNull("byedpi_domain_mixed_case") val hostRemoveSpaces = @@ -105,11 +106,17 @@ class ByeDpiUISettingsFragment : PreferenceFragmentCompat() { } } + val desyncAllProtocols = + !desyncHttp.isChecked && !desyncHttps.isChecked && !desyncUdp.isChecked + + if (desyncAllProtocols || desyncUdp.isChecked) { + udpFakeCount.isVisible = true + } else { + udpFakeCount.isVisible = false + } + when (desyncMethod) { None -> { - desyncHttp.isVisible = false - desyncHttps.isVisible = false - desyncUdp.isVisible = false splitPosition.isVisible = false splitAtHost.isVisible = false ttlFake.isVisible = false @@ -121,15 +128,9 @@ class ByeDpiUISettingsFragment : PreferenceFragmentCompat() { } else -> { - desyncHttp.isVisible = true - desyncHttps.isVisible = true - desyncUdp.isVisible = true splitPosition.isVisible = true splitAtHost.isVisible = true - val desyncAllProtocols = - !desyncHttp.isChecked && !desyncHttps.isChecked && !desyncUdp.isChecked - if (desyncAllProtocols || desyncHttp.isChecked) { hostMixedCase.isVisible = true domainMixedCase.isVisible = true diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/fragments/MainSettingsFragment.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/fragments/MainSettingsFragment.kt index 150e04e..4aa5bf2 100644 --- a/app/src/main/java/io/github/dovecoteescapee/byedpi/fragments/MainSettingsFragment.kt +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/fragments/MainSettingsFragment.kt @@ -85,10 +85,18 @@ class MainSettingsFragment : PreferenceFragmentCompat() { val mode = findPreferenceNotNull("byedpi_mode") .value.let { Mode.fromString(it) } val dns = findPreferenceNotNull("dns_ip") + val ipv6 = findPreferenceNotNull("ipv6_enable") when (mode) { - Mode.VPN -> dns.isVisible = true - Mode.Proxy -> dns.isVisible = false + Mode.VPN -> { + dns.isVisible = true + ipv6.isVisible = true + } + + Mode.Proxy -> { + dns.isVisible = false + ipv6.isVisible = false + } } } } diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ByeDpiVpnService.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ByeDpiVpnService.kt index 1fedb18..ed7ff34 100644 --- a/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ByeDpiVpnService.kt +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ByeDpiVpnService.kt @@ -180,6 +180,7 @@ class ByeDpiVpnService : LifecycleVpnService() { val sharedPreferences = getPreferences() val port = sharedPreferences.getString("byedpi_proxy_port", null)?.toInt() ?: 1080 val dns = sharedPreferences.getStringNotNull("dns_ip", "1.1.1.1") + val ipv6 = sharedPreferences.getBoolean("ipv6_enable", false) val tun2socksConfig = """ | misc: @@ -200,13 +201,18 @@ class ByeDpiVpnService : LifecycleVpnService() { throw e } - val vpn = createBuilder(dns).establish() + val fd = createBuilder(dns, ipv6).establish() ?: throw IllegalStateException("VPN connection failed") - this.tunFd = vpn + this.tunFd = fd - Log.d(TAG, "Native tun2socks start") - TProxyService.TProxyStartService(configPath.absolutePath, vpn.fd) + TProxyService.TProxyStartService(configPath.absolutePath, fd.fd) + + try { + File(cacheDir, "config.tmp").delete() + } catch (e: SecurityException) { + Log.e(TAG, "Failed to delete config file", e) + } Log.i(TAG, "Tun2Socks started") } @@ -215,15 +221,10 @@ class ByeDpiVpnService : LifecycleVpnService() { Log.i(TAG, "Stopping tun2socks") TProxyService.TProxyStopService() - Log.d(TAG, "Native tun2socks stopped done") tunFd?.close() ?: Log.w(TAG, "VPN not running") tunFd = null - try { - File(cacheDir, "config.tmp").delete() - } catch (e: SecurityException) { - Log.e(TAG, "Failed to delete config file", e) - } + Log.i(TAG, "Tun2socks stopped") } @@ -268,7 +269,7 @@ class ByeDpiVpnService : LifecycleVpnService() { ByeDpiVpnService::class.java, ) - private fun createBuilder(dns: String): Builder { + private fun createBuilder(dns: String, ipv6: Boolean): Builder { Log.d(TAG, "DNS: $dns") val builder = Builder() builder.setSession("ByeDPI") @@ -282,8 +283,13 @@ class ByeDpiVpnService : LifecycleVpnService() { ) builder.addAddress("10.10.10.10", 32) - builder.addRoute("0.0.0.0", 0) - builder.addRoute("0:0:0:0:0:0:0:0", 0) + .addRoute("0.0.0.0", 0) + + if (ipv6) { + builder.addAddress("fd00:::1", 128) + .addRoute("::", 0) + } + if (dns.isNotBlank()) { builder.addDnsServer(dns) } diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/NotificationUtils.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/NotificationUtils.kt index 4ec01ae..af613e8 100644 --- a/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/NotificationUtils.kt +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/NotificationUtils.kt @@ -58,4 +58,4 @@ fun createConnectionNotification( PendingIntent.FLAG_IMMUTABLE, ) ) - .build() \ No newline at end of file + .build() diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/ValidateUtils.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/ValidateUtils.kt index a60929e..8ca4c2f 100644 --- a/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/ValidateUtils.kt +++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/utility/ValidateUtils.kt @@ -3,7 +3,6 @@ package io.github.dovecoteescapee.byedpi.utility import android.net.InetAddresses import android.os.Build import android.util.Log -import android.util.Patterns import android.widget.Toast import androidx.preference.EditTextPreference import androidx.preference.PreferenceFragmentCompat @@ -12,11 +11,14 @@ private const val TAG = "ValidateUtils" fun checkIp(ip: String): Boolean = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) { - InetAddresses.isNumericAddress(ip) + InetAddresses.isNumericAddress(ip) && InetAddresses.parseNumericAddress(ip).let { + !it.isAnyLocalAddress && !it.isLoopbackAddress + } } else { // This pattern doesn't not support IPv6 - @Suppress("DEPRECATION") - Patterns.IP_ADDRESS.matcher(ip).matches() + // @Suppress("DEPRECATION") + // Patterns.IP_ADDRESS.matcher(ip).matches() + true } fun PreferenceFragmentCompat.setEditTestPreferenceListenerPort(key: String) { diff --git a/app/src/main/res/drawable-hdpi/ic_notification.png b/app/src/main/res/drawable-hdpi/ic_notification.png deleted file mode 100644 index 7ce6f29..0000000 Binary files a/app/src/main/res/drawable-hdpi/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-mdpi/ic_notification.png b/app/src/main/res/drawable-mdpi/ic_notification.png deleted file mode 100644 index 3cbb23e..0000000 Binary files a/app/src/main/res/drawable-mdpi/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-xhdpi/ic_notification.png b/app/src/main/res/drawable-xhdpi/ic_notification.png deleted file mode 100644 index 36ef0ee..0000000 Binary files a/app/src/main/res/drawable-xhdpi/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxhdpi/ic_notification.png b/app/src/main/res/drawable-xxhdpi/ic_notification.png deleted file mode 100644 index bbb4a71..0000000 Binary files a/app/src/main/res/drawable-xxhdpi/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable-xxxhdpi/ic_notification.png b/app/src/main/res/drawable-xxxhdpi/ic_notification.png deleted file mode 100644 index 48db59d..0000000 Binary files a/app/src/main/res/drawable-xxxhdpi/ic_notification.png and /dev/null differ diff --git a/app/src/main/res/drawable/ic_notification.xml b/app/src/main/res/drawable/ic_notification.xml new file mode 100644 index 0000000..94b96bd --- /dev/null +++ b/app/src/main/res/drawable/ic_notification.xml @@ -0,0 +1,24 @@ + + + + + + + + diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml index b78c3f1..b2e4341 100644 --- a/app/src/main/res/values/strings.xml +++ b/app/src/main/res/values/strings.xml @@ -62,4 +62,7 @@ Hosts Hosts blacklist Hosts whitelist + TCP Fast Open + UDP fake count + IPv6 diff --git a/app/src/main/res/xml/byedpi_ui_settings.xml b/app/src/main/res/xml/byedpi_ui_settings.xml index ba06ce9..ed1d2ee 100644 --- a/app/src/main/res/xml/byedpi_ui_settings.xml +++ b/app/src/main/res/xml/byedpi_ui_settings.xml @@ -75,6 +75,11 @@ android:inputType="textMultiLine" app:useSimpleSummaryProvider="true" /> + + + + + + ByeDPI runs a local VPN service to bypass DPI (Deep Packet Inspection) and censorship. It runs a SOCKS5 proxy ByeDPI and redirects all traffic through it.

To bypass some blocks, you may need to change the settings. More about the various settings can be found in the ByeDPI documentation.

The application uses the VPN mode on Android to redirect traffic, but does not send anything to a remote server. It does not encrypt traffic and does not hide your IP address.

\ No newline at end of file diff --git a/fastlane/metadata/android/ru-RU/short_description.txt b/fastlane/metadata/android/ru-RU/short_description.txt new file mode 100644 index 0000000..41f23f0 --- /dev/null +++ b/fastlane/metadata/android/ru-RU/short_description.txt @@ -0,0 +1 @@ +обход цензуры на Android \ No newline at end of file