---
@@ -55,8 +55,7 @@ DPI (Deep Packet Inspection) - это технология для анализа
## Зависимости
- [ByeDPI](https://github.com/hufrea/byedpi)
-- [Tun2Socks](https://github.com/dovecoteescapee/tun2socks)*
- *форк с добавлением раздельного тунелирования TCP и UDP
+- [Tun2Socks](https://github.com/xjasonlyu/tun2socks)
## Сборка
@@ -70,8 +69,17 @@ DPI (Deep Packet Inspection) - это технология для анализа
Сборка приложения:
-1. Клонируйте репозиторий с подмодулями:
- `git clone --recurse-submodules`
-2. Запустите скрипт сборки из корня репозитория:
- `./gradlew assembleRelease`
-3. APK будет лежать в `app/build/outputs/apk/release/`
+1. Установите gomobile:
+ ```bash
+ go install golang.org/x/mobile/cmd/gomobile@latest
+ gomobile init
+ ```
+2. Клонируйте репозиторий с подмодулями:
+ ```bash
+ git clone --recurse-submodules
+ ```
+3. Запустите скрипт сборки из корня репозитория:
+ ```bash
+ ./gradlew assembleRelease`
+ ```
+4. APK будет лежать в `app/build/outputs/apk/release/`
diff --git a/README.md b/README.md
index 31c7cd1..05b7ee5 100644
--- a/README.md
+++ b/README.md
@@ -2,8 +2,8 @@
**English** | [Русский](README-ru.md)
-
-
+
+
---
@@ -55,8 +55,7 @@ DPI (Deep Packet Inspection) is a technology for analyzing and filtering traffic
## Dependencies
- [ByeDPI](https://github.com/hufrea/byedpi)
-- [Tun2Socks](https://github.com/dovecoteescapee/tun2socks)*
- *fork with the addition of separate tunneling of TCP and UDP
+- [Tun2Socks](https://github.com/xjasonlyu/tun2socks)
## Building
@@ -70,8 +69,17 @@ For building the application, you need:
To build the application:
-1. Clone the repository with submodules:
- `git clone --recurse-submodules`
-2. Run the build script from the root of the repository:
- `./gradlew assembleRelease`
-3. The APK will be in `app/build/outputs/apk/release/`
+1. Install gomobile:
+ ```bash
+ go install golang.org/x/mobile/cmd/gomobile@latest
+ gomobile init
+ ```
+2. Clone the repository with submodules:
+ ```bash
+ git clone --recurse-submodules
+ ```
+3. Run the build script from the root of the repository:
+ ```bash
+ ./gradlew assembleRelease
+ ```
+4. The APK will be in `app/build/outputs/apk/release/`
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 36e44f5..6495890 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -69,26 +69,75 @@ dependencies {
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
}
-abstract class BuildTun2Socks : DefaultTask() {
- @TaskAction
- fun buildTun2Socks() {
- val projectDir = project.projectDir
- val tun2socksDir = projectDir.resolve("libs/tun2socks")
- val tun2socksOutput = projectDir.resolve("libs/tun2socks.aar")
+abstract class BaseTun2SocksTask : DefaultTask() {
+ @get:InputDirectory
+ val tun2socksDir: File
+ get() = project.file("libs/tun2socks")
+ @get:OutputFile
+ val tun2socksOutput: File
+ get() = project.file("libs/tun2socks.aar")
+
+ @Internal
+ protected fun isUpToDate(): Boolean {
if (tun2socksOutput.exists()) {
+ val lastModified = tun2socksOutput.lastModified()
+ return !tun2socksDir.walkTopDown().any {
+ it.isFile && it.lastModified() > lastModified
+ }
+ }
+ return false
+ }
+}
+
+abstract class GetGomobileBind : BaseTun2SocksTask() {
+ @TaskAction
+ fun getBind() {
+ if (isUpToDate()) {
+ logger.lifecycle("No changes detected, skipping getBind.")
return
}
+
project.exec {
workingDir = tun2socksDir
- commandLine("gomobile", "bind", "-o", tun2socksOutput, "-trimpath", "./engine")
+
+ commandLine("go", "get", "golang.org/x/mobile/bind")
}
}
}
+abstract class BuildTun2Socks : BaseTun2SocksTask() {
+ @TaskAction
+ fun buildTun2Socks() {
+ if (isUpToDate()) {
+ logger.lifecycle("No changes detected, skipping buildTun2Socks.")
+ return
+ }
+
+ project.exec {
+ workingDir = tun2socksDir
+
+ commandLine(
+ "gomobile", "bind",
+ "-target", "android",
+ "-androidapi", "21",
+ "-o", tun2socksOutput,
+ "-trimpath",
+ "./engine"
+ )
+ }
+ }
+}
+
+tasks.register
("getGomobileBind") {
+ group = "build"
+ description = "Get gomobile bind for compiling tun2socks"
+}
+
tasks.register("buildTun2Socks") {
group = "build"
- description = "Build tun2socks"
+ description = "Build tun2socks for Android"
+ dependsOn("getGomobileBind")
}
tasks.withType(KotlinCompile::class).configureEach {
diff --git a/app/libs/tun2socks b/app/libs/tun2socks
index b357c5b..e083daf 160000
--- a/app/libs/tun2socks
+++ b/app/libs/tun2socks
@@ -1 +1 @@
-Subproject commit b357c5b50ac07aa36aed843b803df06a307fe61a
+Subproject commit e083dafcf534d85e7fce86b3d48ee5c7a63c604e
diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt
index d034952..2e1d16f 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -28,10 +28,13 @@ add_library(${CMAKE_PROJECT_NAME} SHARED
# List C/C++ source files with relative paths to this CMakeLists.txt.
byedpi/conev.c
byedpi/desync.c
+ byedpi/extend.c
byedpi/packets.c
byedpi/proxy.c
byedpi/main.c
+ byedpi/mpool.c
native-lib.c
+ utils.c
)
include_directories("byedpi")
@@ -46,4 +49,5 @@ add_compile_definitions(ANDROID_APP)
target_link_libraries(${CMAKE_PROJECT_NAME}
# List libraries link to the target library
android
- log)
\ No newline at end of file
+ log
+ )
\ No newline at end of file
diff --git a/app/src/main/cpp/byedpi b/app/src/main/cpp/byedpi
index 6b484d5..dcf5ed7 160000
--- a/app/src/main/cpp/byedpi
+++ b/app/src/main/cpp/byedpi
@@ -1 +1 @@
-Subproject commit 6b484d598817cffd61344a812721fb00089f5095
+Subproject commit dcf5ed727c996d0073a6cb95d5eec45a793d28a2
diff --git a/app/src/main/cpp/main.h b/app/src/main/cpp/main.h
new file mode 100644
index 0000000..13297db
--- /dev/null
+++ b/app/src/main/cpp/main.h
@@ -0,0 +1,12 @@
+extern char *oob_char;
+extern int NOT_EXIT;
+
+struct sockaddr_ina;
+
+int get_default_ttl();
+
+int get_addr(const char *str, struct sockaddr_ina *addr);
+
+void *add(void **root, int *n, size_t ss);
+
+void clear_params(void);
diff --git a/app/src/main/cpp/native-lib.c b/app/src/main/cpp/native-lib.c
index a903e75..6a2ac39 100644
--- a/app/src/main/cpp/native-lib.c
+++ b/app/src/main/cpp/native-lib.c
@@ -1,14 +1,16 @@
-#include
-#include
-#include
-#include
-#include
#include
#include
#include
#include
+#include "byedpi/error.h"
+#include "byedpi/proxy.h"
+#include "byedpi/params.h"
+#include "byedpi/packets.h"
+#include "main.h"
+#include "utils.h"
+
const enum demode DESYNC_METHODS[] = {
DESYNC_NONE,
DESYNC_SPLIT,
@@ -17,14 +19,9 @@ const enum demode DESYNC_METHODS[] = {
DESYNC_OOB,
};
-extern int NOT_EXIT;
-
-extern int get_default_ttl();
-
-extern int get_addr(const char *str, struct sockaddr_ina *addr);
-
JNIEXPORT jint JNI_OnLoad(JavaVM *vm, __attribute__((unused)) void *reserved) {
oob_data.data = NULL;
+ default_params = params;
return JNI_VERSION_1_6;
}
@@ -37,8 +34,8 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
jint max_connections,
jint buffer_size,
jint default_ttl,
+ jboolean custom_ttl,
jboolean no_domain,
- jboolean desync_known,
jint desync_method,
jint split_position,
jboolean split_at_host,
@@ -52,52 +49,90 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
jint tls_record_split_position,
jboolean tls_record_split_at_sni) {
- struct sockaddr_ina s = {
- .in.sin_family = AF_INET,
- .in.sin_addr.s_addr = inet_addr("0.0.0.0"),
- };
+ struct sockaddr_ina s;
const char *address = (*env)->GetStringUTFChars(env, ip, 0);
- if (get_addr(address, &s) < 0) {
+ int res = get_addr(address, &s);
+ (*env)->ReleaseStringUTFChars(env, ip, address);
+ if (res < 0) {
+ uniperror("get_addr");
return -1;
}
- (*env)->ReleaseStringUTFChars(env, ip, address);
s.in.sin_port = htons(port);
params.max_open = max_connections;
params.bfsize = buffer_size;
- params.def_ttl = default_ttl;
params.resolve = !no_domain;
- params.de_known = desync_known;
- params.attack = DESYNC_METHODS[desync_method];
- params.split = split_position;
- params.split_host = split_at_host;
- params.ttl = fake_ttl;
- params.mod_http =
- MH_HMIX * host_mixed_case |
- MH_DMIX * domain_mixed_case |
- MH_SPACE * host_remove_spaces;
- params.tlsrec = tls_record_split;
- params.tlsrec_pos = tls_record_split_position;
- params.tlsrec_sni = tls_record_split_at_sni;
- if (!params.def_ttl && params.attack != DESYNC_NONE) {
+ if (custom_ttl) {
+ params.def_ttl = default_ttl;
+ params.custom_ttl = 1;
+ }
+
+ if (!params.def_ttl) {
if ((params.def_ttl = get_default_ttl()) < 1) {
+ uniperror("get_default_ttl");
+ reset_params();
return -1;
}
}
- int fd = listen_socket(&s);
- if (fd < 0) {
- uniperror("listen_socket");
- return get_e();
+ struct desync_params *dp = add(
+ (void *) ¶ms.dp,
+ ¶ms.dp_count,
+ sizeof(struct desync_params)
+ );
+ if (!dp) {
+ uniperror("add");
+ reset_params();
+ return -1;
}
- if (params.attack == DESYNC_FAKE) {
+ dp->ttl = fake_ttl;
+ dp->mod_http =
+ MH_HMIX * host_mixed_case |
+ MH_DMIX * domain_mixed_case |
+ MH_SPACE * host_remove_spaces;
+
+ struct part *part = add(
+ (void *) &dp->parts,
+ &dp->parts_n,
+ sizeof(struct part)
+ );
+ if (!part) {
+ uniperror("add");
+ reset_params();
+ return -1;
+ }
+
+ enum demode mode = DESYNC_METHODS[desync_method];
+
+ part->flag = split_at_host ? OFFSET_SNI : 0;
+ part->pos = split_position;
+ part->m = mode;
+
+ if (tls_record_split) {
+ struct part *tlsrec_part = add(
+ (void *) &dp->tlsrec,
+ &dp->tlsrec_n,
+ sizeof(struct part)
+ );
+
+ if (!tlsrec_part) {
+ uniperror("add");
+ reset_params();
+ return -1;
+ }
+
+ tlsrec_part->flag = tls_record_split_at_sni ? OFFSET_SNI : 0;
+ tlsrec_part->pos = tls_record_split_position;
+ }
+
+ if (mode == DESYNC_FAKE) {
const char *sni = (*env)->GetStringUTFChars(env, fake_sni, 0);
LOG(LOG_S, "fake_sni: %s", sni);
- int res = change_tls_sni(sni, fake_tls.data, fake_tls.size);
+ res = change_tls_sni(sni, fake_tls.data, fake_tls.size);
(*env)->ReleaseStringUTFChars(env, fake_sni, sni);
if (res) {
fprintf(stderr, "error chsni\n");
@@ -105,14 +140,11 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
}
}
- if (params.attack == DESYNC_OOB) {
+ if (mode == DESYNC_OOB) {
const char *oob = (*env)->GetStringUTFChars(env, custom_oob_data, 0);
const size_t oob_len = strlen(oob);
- LOG(LOG_L, "custom_oob_data: %s", oob);
+
oob_data.size = oob_len;
- LOG(LOG_L, "before free");
- free(oob_data.data);
- LOG(LOG_L, "after free");
oob_data.data = malloc(oob_len);
if (oob_data.data == NULL) {
uniperror("malloc");
@@ -122,7 +154,20 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket(
(*env)->ReleaseStringUTFChars(env, custom_oob_data, oob);
}
+ params.mempool = mem_pool(0);
+ if (!params.mempool) {
+ uniperror("mem_pool");
+ clear_params();
+ return -1;
+ }
+
+ int fd = listen_socket(&s);
+ if (fd < 0) {
+ uniperror("listen_socket");
+ return -1;
+ }
LOG(LOG_S, "listen_socket, fd: %d", fd);
+
return fd;
}
@@ -134,6 +179,7 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniStartProxy(
LOG(LOG_S, "start_proxy, fd: %d", fd);
NOT_EXIT = 1;
if (event_loop(fd) < 0) {
+ uniperror("event_loop");
return get_e();
}
return 0;
@@ -145,7 +191,12 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniStopProxy(
__attribute__((unused)) jobject thiz,
jint fd) {
LOG(LOG_S, "stop_proxy, fd: %d", fd);
- if (shutdown(fd, SHUT_RDWR) < 0) {
+
+ int res = shutdown(fd, SHUT_RDWR);
+ reset_params();
+
+ if (res < 0) {
+ uniperror("shutdown");
return get_e();
}
return 0;
diff --git a/app/src/main/cpp/utils.c b/app/src/main/cpp/utils.c
new file mode 100644
index 0000000..f4c902f
--- /dev/null
+++ b/app/src/main/cpp/utils.c
@@ -0,0 +1,10 @@
+#include "utils.h"
+#include "byedpi/params.h"
+#include "main.h"
+
+struct params default_params;
+
+void reset_params(void) {
+ clear_params();
+ params = default_params;
+}
diff --git a/app/src/main/cpp/utils.h b/app/src/main/cpp/utils.h
new file mode 100644
index 0000000..9846b94
--- /dev/null
+++ b/app/src/main/cpp/utils.h
@@ -0,0 +1,3 @@
+extern struct params default_params;
+
+void reset_params(void);
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 5626423..f923a70 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
@@ -2,7 +2,6 @@ package io.github.dovecoteescapee.byedpi.core
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
-import java.io.IOException
class ByeDpiProxy {
companion object {
@@ -14,8 +13,13 @@ class ByeDpiProxy {
private val mutex = Mutex()
private var fd = -1
- suspend fun startProxy(preferences: ByeDpiProxyPreferences): Int =
- jniStartProxy(createSocket(preferences))
+ suspend fun startProxy(preferences: ByeDpiProxyPreferences): Int {
+ val fd = createSocket(preferences)
+ if (fd < 0) {
+ return -1 // TODO: should be error code
+ }
+ return jniStartProxy(fd)
+ }
suspend fun stopProxy(): Int {
mutex.withLock {
@@ -43,8 +47,8 @@ class ByeDpiProxy {
maxConnections = preferences.maxConnections,
bufferSize = preferences.bufferSize,
defaultTtl = preferences.defaultTtl,
+ customTtl = preferences.customTtl,
noDomain = preferences.noDomain,
- desyncKnown = preferences.desyncKnown,
desyncMethod = preferences.desyncMethod.ordinal,
splitPosition = preferences.splitPosition,
splitAtHost = preferences.splitAtHost,
@@ -60,7 +64,7 @@ class ByeDpiProxy {
)
if (fd < 0) {
- throw IOException("Failed to create socket")
+ return -1
}
this.fd = fd
@@ -73,8 +77,8 @@ class ByeDpiProxy {
maxConnections: Int,
bufferSize: Int,
defaultTtl: Int,
+ customTtl: Boolean,
noDomain: Boolean,
- desyncKnown: Boolean,
desyncMethod: Int,
splitPosition: Int,
splitAtHost: Boolean,
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 ae489e7..d1aa781 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
@@ -9,7 +9,6 @@ class ByeDpiProxyPreferences(
bufferSize: Int? = null,
defaultTtl: Int? = null,
noDomain: Boolean? = null,
- desyncKnown: Boolean? = null,
desyncMethod: DesyncMethod? = null,
splitPosition: Int? = null,
splitAtHost: Boolean? = null,
@@ -28,8 +27,8 @@ class ByeDpiProxyPreferences(
val maxConnections: Int = maxConnections ?: 512
val bufferSize: Int = bufferSize ?: 16384
val defaultTtl: Int = defaultTtl ?: 0
+ val customTtl: Boolean = defaultTtl != null
val noDomain: Boolean = noDomain ?: false
- val desyncKnown: Boolean = desyncKnown ?: false
val desyncMethod: DesyncMethod = desyncMethod ?: DesyncMethod.Disorder
val splitPosition: Int = splitPosition ?: 3
val splitAtHost: Boolean = splitAtHost ?: false
@@ -50,7 +49,6 @@ class ByeDpiProxyPreferences(
bufferSize = preferences.getString("byedpi_buffer_size", null)?.toIntOrNull(),
defaultTtl = preferences.getString("byedpi_default_ttl", null)?.toIntOrNull(),
noDomain = preferences.getBoolean("byedpi_no_domain", false),
- desyncKnown = preferences.getBoolean("byedpi_desync_known", false),
desyncMethod = preferences.getString("byedpi_desync_method", null)
?.let { DesyncMethod.fromName(it) },
splitPosition = preferences.getString("byedpi_split_position", null)?.toIntOrNull(),
diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ByeDpiProxyService.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ByeDpiProxyService.kt
index e3b4799..892ec5c 100644
--- a/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ByeDpiProxyService.kt
+++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/ByeDpiProxyService.kt
@@ -170,7 +170,10 @@ class ByeDpiProxyService : LifecycleService() {
when (newStatus) {
ServiceStatus.Connected -> AppStatus.Running
ServiceStatus.Disconnected,
- ServiceStatus.Failed -> AppStatus.Halted
+ ServiceStatus.Failed -> {
+ proxyJob = null
+ AppStatus.Halted
+ }
},
Mode.Proxy
)
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 3832f98..bea931b 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
@@ -224,7 +224,10 @@ class ByeDpiVpnService : LifecycleVpnService() {
when (newStatus) {
ServiceStatus.Connected -> AppStatus.Running
ServiceStatus.Disconnected,
- ServiceStatus.Failed -> AppStatus.Halted
+ ServiceStatus.Failed -> {
+ proxyJob = null
+ AppStatus.Halted
+ }
},
Mode.VPN
)
@@ -281,8 +284,7 @@ class ByeDpiVpnService : LifecycleVpnService() {
setInterface("")
logLevel = if (BuildConfig.DEBUG) "debug" else "info"
- udpProxy = "direct://"
- tcpProxy = "socks5://127.0.0.1:$port"
+ proxy = "socks5://127.0.0.1:$port"
restAPI = ""
tcpSendBufferSize = ""
diff --git a/app/src/main/res/values/strings.xml b/app/src/main/res/values/strings.xml
index bc18012..0db88a1 100644
--- a/app/src/main/res/values/strings.xml
+++ b/app/src/main/res/values/strings.xml
@@ -8,10 +8,6 @@
Proxy is down
Start
Stop
- Connecting…
- Disconnecting…
- Starting
- Stopping
Settings
VPN permission denied
Please stop the VPN service before changing settings
@@ -29,7 +25,6 @@
Buffer size
Default TTL
No domain
- Desync only HTTPS and TLS
Desync method
Split position
Split at host
diff --git a/app/src/main/res/xml/settings.xml b/app/src/main/res/xml/settings.xml
index 4d83f28..7ea528f 100644
--- a/app/src/main/res/xml/settings.xml
+++ b/app/src/main/res/xml/settings.xml
@@ -83,11 +83,6 @@
android:title="@string/byedpi_no_domain_setting"
android:defaultValue="false"/>
-
-