Improvements and fixes (v 1.1.1):
- Added TCP Fast Open and UDP fake count to UI editor - Added IPv6 support - Changed default split position to 2 - Improved DNS domain validation - Adjusted QuickTile icon - Added russian description for fastlane
BIN
.github/images/preview.png
vendored
Before Width: | Height: | Size: 912 KiB |
BIN
.github/images/screenshot1.png
vendored
Before Width: | Height: | Size: 31 KiB |
BIN
.github/images/screenshot2.png
vendored
Before Width: | Height: | Size: 67 KiB |
BIN
.github/images/screenshot3.png
vendored
Before Width: | Height: | Size: 82 KiB |
@ -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"
|
||||
|
||||
|
@ -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 |
|
||||
|
@ -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
|
||||
|
@ -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 {
|
||||
|
@ -79,6 +79,7 @@ class ByeDpiUISettingsFragment : PreferenceFragmentCompat() {
|
||||
val ttlFake = findPreferenceNotNull<EditTextPreference>("byedpi_fake_ttl")
|
||||
val fakeSni = findPreferenceNotNull<EditTextPreference>("byedpi_fake_sni")
|
||||
val oobData = findPreferenceNotNull<EditTextPreference>("byedpi_oob_data")
|
||||
val udpFakeCount = findPreferenceNotNull<EditTextPreference>("byedpi_udp_fake_count")
|
||||
val hostMixedCase = findPreferenceNotNull<CheckBoxPreference>("byedpi_host_mixed_case")
|
||||
val domainMixedCase = findPreferenceNotNull<CheckBoxPreference>("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
|
||||
|
@ -85,10 +85,18 @@ class MainSettingsFragment : PreferenceFragmentCompat() {
|
||||
val mode = findPreferenceNotNull<ListPreference>("byedpi_mode")
|
||||
.value.let { Mode.fromString(it) }
|
||||
val dns = findPreferenceNotNull<EditTextPreference>("dns_ip")
|
||||
val ipv6 = findPreferenceNotNull<SwitchPreference>("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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -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)
|
||||
}
|
||||
|
@ -58,4 +58,4 @@ fun createConnectionNotification(
|
||||
PendingIntent.FLAG_IMMUTABLE,
|
||||
)
|
||||
)
|
||||
.build()
|
||||
.build()
|
||||
|
@ -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) {
|
||||
|
Before Width: | Height: | Size: 820 B |
Before Width: | Height: | Size: 486 B |
Before Width: | Height: | Size: 1.1 KiB |
Before Width: | Height: | Size: 1.9 KiB |
Before Width: | Height: | Size: 2.7 KiB |
24
app/src/main/res/drawable/ic_notification.xml
Normal file
@ -0,0 +1,24 @@
|
||||
<vector xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:width="24dp"
|
||||
android:height="24dp"
|
||||
android:viewportWidth="24"
|
||||
android:viewportHeight="24">
|
||||
<path
|
||||
android:pathData="M14.74,16.866C17.473,15.885 17.893,13.832 17.869,12.884C17.835,11.487 16.792,10.113 16.751,9.354C16.714,8.676 16.967,8.174 18.106,8.338C18.002,8.039 17.763,7.536 17.479,7.178C17.112,6.714 16.629,6.356 15.858,6.357C14.554,6.359 14.124,7.537 13.826,8.296C13.528,9.056 13.837,9.36 13.026,10.509C13.149,11.895 13.097,11.909 12.882,12.771C12.362,14.851 10.511,17.307 9.567,18.641C8.623,19.975 7.843,20.663 8.685,21.329C9.526,21.997 9.927,21.032 10.234,20.477C10.542,19.923 10.871,19.205 11.014,19.246C11.158,19.287 11.199,19.153 10.819,20.303C10.44,21.452 10.532,21.955 11.353,22.017C12.174,22.078 12.328,21.575 12.389,20.713C12.451,19.852 12.564,19.379 12.677,19.379C12.79,19.379 12.821,19.236 12.903,20.323C12.985,21.411 13.016,22.15 14.063,21.781C15.109,21.412 14.791,20.816 14.535,20.242C14.278,19.667 14.207,19.092 14.319,19.041C14.432,18.99 14.566,19.257 14.976,19.893C15.387,20.529 15.613,21.289 16.485,20.704C17.697,19.89 16.043,18.477 15.304,17.543C13.991,18.015 13.324,18.015 13.313,17.543C13.303,17.071 13.875,17.176 14.74,16.866ZM16.057,7.078C16.282,7.078 16.465,7.261 16.465,7.486C16.465,7.71 16.282,7.893 16.057,7.893C15.833,7.893 15.65,7.71 15.65,7.486C15.65,7.261 15.833,7.078 16.057,7.078Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M12.323,10.724C12.328,12.132 12.098,13.609 10.721,15.484C9.624,15.608 8.689,15.672 8.468,15.227C8.209,14.706 8.769,14.613 9.399,14.153C9.399,14.153 10.449,13.531 10.239,13.298C10.029,13.064 8.995,13.788 8.497,13.974C7.999,14.161 7.027,14.632 6.506,13.71C6.272,13.298 6.568,13.088 7.478,12.901C8.388,12.715 8.932,12.458 8.777,12.256C8.621,12.054 7.206,12.566 6.086,12.575C4.966,12.582 4.616,12.085 4.616,11.68C4.616,11.276 4.966,11.338 5.946,11.276C6.926,11.214 7.552,11.307 7.532,10.926C7.517,10.623 6.101,10.856 5.246,10.724C4.39,10.592 3.13,10.359 3.13,9.448C3.13,9.06 3.355,8.943 4.047,9.168C4.74,9.394 6.606,9.728 6.606,9.308C6.606,8.888 4.18,9.121 2.849,7.877C1.519,6.633 2.391,5.972 2.655,5.972C3.06,5.972 3.375,6.62 5.548,7.317C6.59,7.651 9.2,7.899 9.958,8.266C10.908,8.725 12.323,10.724 12.323,10.724Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M15.828,5.51C16.435,4.323 16.575,4.112 16.837,3.281C17.099,2.449 17.249,1.729 17.696,1.762C18.302,1.806 18.524,2.519 18.273,3.392C17.982,4.402 17.146,5.458 16.94,5.776C16.605,5.468 15.828,5.51 15.828,5.51Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M17.31,5.903C18.106,5.041 18.208,4.344 18.605,4.337C19.001,4.33 19.22,4.813 19.142,5.238C19.065,5.664 18.542,6.428 18.194,6.796C17.774,6.357 17.536,6.085 17.31,5.903Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M18.47,7.222C18.552,7.004 19.051,6.733 19.283,6.946C19.573,7.212 19.602,8.025 19.036,8.586C18.852,7.943 18.61,7.396 18.47,7.222Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
<path
|
||||
android:pathData="M18.378,11.31C18.712,11.059 19.35,10.323 19.239,9.844C19.128,9.366 19.046,9.308 18.378,9.22C17.71,9.133 17.497,9.061 17.531,9.298C17.566,9.535 18.363,11.121 18.378,11.31Z"
|
||||
android:fillColor="#ffffff"/>
|
||||
</vector>
|
@ -62,4 +62,7 @@
|
||||
<string name="byedpi_hosts_mode_setting">Hosts</string>
|
||||
<string name="byedpi_hosts_blacklist_setting">Hosts blacklist</string>
|
||||
<string name="byedpi_hosts_whitelist_setting">Hosts whitelist</string>
|
||||
<string name="byedpi_tcp_fast_open_setting">TCP Fast Open</string>
|
||||
<string name="byedpi_udp_fake_count">UDP fake count</string>
|
||||
<string name="ipv6_setting">IPv6</string>
|
||||
</resources>
|
||||
|
@ -75,6 +75,11 @@
|
||||
android:inputType="textMultiLine"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="byedpi_tcp_fast_open"
|
||||
android:title="@string/byedpi_tcp_fast_open_setting"
|
||||
android:defaultValue="false" />
|
||||
|
||||
<com.takisoft.preferencex.EditTextPreference
|
||||
android:key="byedpi_default_ttl"
|
||||
android:title="@string/byedpi_default_ttl_setting"
|
||||
@ -109,7 +114,7 @@
|
||||
android:key="byedpi_split_position"
|
||||
android:title="@string/byedpi_split_position_setting"
|
||||
android:inputType="numberSigned"
|
||||
android:defaultValue="3"
|
||||
android:defaultValue="2"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<CheckBoxPreference
|
||||
@ -136,6 +141,13 @@
|
||||
android:defaultValue="a"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<com.takisoft.preferencex.EditTextPreference
|
||||
android:key="byedpi_udp_fake_count"
|
||||
android:title="@string/byedpi_udp_fake_count"
|
||||
android:inputType="number"
|
||||
android:defaultValue="0"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<CheckBoxPreference
|
||||
android:key="byedpi_host_mixed_case"
|
||||
android:title="@string/byedpi_host_mixed_case_setting"
|
||||
|
@ -29,6 +29,11 @@
|
||||
android:defaultValue="1.1.1.1"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<SwitchPreference
|
||||
android:key="ipv6_enable"
|
||||
android:title="@string/ipv6_setting"
|
||||
android:defaultValue="false" />
|
||||
|
||||
</androidx.preference.PreferenceCategory>
|
||||
|
||||
<androidx.preference.PreferenceCategory
|
||||
|
1
fastlane/metadata/android/ru-RU/full_description.txt
Normal file
@ -0,0 +1 @@
|
||||
<p><i>ByeDPI</i> runs a local VPN service to bypass DPI (Deep Packet Inspection) and censorship. It runs a SOCKS5 proxy <a href='https://github.com/hufrea/byedpi' target='_blank' rel='nofollow noopener'>ByeDPI</a> and redirects all traffic through it.</p><p>To bypass some blocks, you may need to change the settings. More about the various settings can be found in the <a href='https://github.com/hufrea/byedpi#readme-ov-file' target='_blank' rel='nofollow noopener'>ByeDPI documentation</a>.</p><p>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.</p>
|
1
fastlane/metadata/android/ru-RU/short_description.txt
Normal file
@ -0,0 +1 @@
|
||||
обход цензуры на Android
|