diff --git a/README-ru.md b/README-ru.md index 87505f4..6a78bfb 100644 --- a/README-ru.md +++ b/README-ru.md @@ -2,8 +2,8 @@ [English](README.md) | **Русский** -
- +
+
--- @@ -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"/> - -