- Update byedpi to v0.12

- Switch to original tun2socks
- Fix crash when socket opening failed
- Update Readme
- Remove redundant strings
This commit is contained in:
dovecoteescapee 2024-08-08 22:55:29 +03:00
parent 312dc4b264
commit 6d7b0bf63f
16 changed files with 238 additions and 96 deletions

View File

@ -2,8 +2,8 @@
[English](README.md) | **Русский**
<div align="center">
<img src=".github/images/logo.svg" height="200px" width="200px" />
<div style="text-align: center;">
<img src=".github/images/logo.svg" width="100%" height="200px">
</div>
---
@ -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/`

View File

@ -2,8 +2,8 @@
**English** | [Русский](README-ru.md)
<div align="center">
<img src=".github/images/logo.svg" height="200px" width="200px" />
<div style="text-align: center;">
<img src=".github/images/logo.svg" width="100%" height="200px">
</div>
---
@ -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/`

View File

@ -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>("getGomobileBind") {
group = "build"
description = "Get gomobile bind for compiling tun2socks"
}
tasks.register<BuildTun2Socks>("buildTun2Socks") {
group = "build"
description = "Build tun2socks"
description = "Build tun2socks for Android"
dependsOn("getGomobileBind")
}
tasks.withType(KotlinCompile::class).configureEach {

@ -1 +1 @@
Subproject commit b357c5b50ac07aa36aed843b803df06a307fe61a
Subproject commit e083dafcf534d85e7fce86b3d48ee5c7a63c604e

View File

@ -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)
log
)

@ -1 +1 @@
Subproject commit 6b484d598817cffd61344a812721fb00089f5095
Subproject commit dcf5ed727c996d0073a6cb95d5eec45a793d28a2

12
app/src/main/cpp/main.h Normal file
View File

@ -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);

View File

@ -1,14 +1,16 @@
#include <error.h>
#include <proxy.h>
#include <params.h>
#include <packets.h>
#include <unistd.h>
#include <string.h>
#include <netdb.h>
#include <jni.h>
#include <android/log.h>
#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 *) &params.dp,
&params.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;

10
app/src/main/cpp/utils.c Normal file
View File

@ -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;
}

3
app/src/main/cpp/utils.h Normal file
View File

@ -0,0 +1,3 @@
extern struct params default_params;
void reset_params(void);

View File

@ -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,

View File

@ -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(),

View File

@ -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
)

View File

@ -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 = ""

View File

@ -8,10 +8,6 @@
<string name="proxy_down">Proxy is down</string>
<string name="proxy_start">Start</string>
<string name="proxy_stop">Stop</string>
<string name="vpn_connecting">Connecting…</string>
<string name="vpn_disconnecting">Disconnecting…</string>
<string name="proxy_starting">Starting</string>
<string name="proxy_stopping">Stopping</string>
<string name="settings">Settings</string>
<string name="vpn_permission_denied">VPN permission denied</string>
<string name="settings_unavailable">Please stop the VPN service before changing settings</string>
@ -29,7 +25,6 @@
<string name="byedpi_buffer_size_setting">Buffer size</string>
<string name="byedpi_default_ttl_setting">Default TTL</string>
<string name="byedpi_no_domain_setting">No domain</string>
<string name="byedpi_desync_known_setting">Desync only HTTPS and TLS</string>
<string name="byedpi_desync_method_setting">Desync method</string>
<string name="byedpi_split_position_setting">Split position</string>
<string name="byedpi_split_at_host_setting">Split at host</string>

View File

@ -83,11 +83,6 @@
android:title="@string/byedpi_no_domain_setting"
android:defaultValue="false"/>
<CheckBoxPreference
android:key="byedpi_desync_known"
android:title="@string/byedpi_desync_known_setting"
android:defaultValue="false"/>
<DropDownPreference
android:key="byedpi_desync_method"
android:title="@string/byedpi_desync_method_setting"