diff --git a/.gitignore b/.gitignore
index aedf400..0b776bf 100644
--- a/.gitignore
+++ b/.gitignore
@@ -14,3 +14,4 @@
.externalNativeBuild
.cxx
local.properties
+/app/src/main/jniLibs
diff --git a/.gitmodules b/.gitmodules
index b87085c..f712c59 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -1,6 +1,6 @@
-[submodule "app/libs/tun2socks"]
- path = app/libs/tun2socks
- url = https://github.com/dovecoteescapee/tun2socks.git
-[submodule "app/src/main/cpp/byedpi"]
+[submodule "hev-socks5-tunnel"]
+ path = app/src/main/jni/hev-socks5-tunnel
+ url = https://github.com/heiher/hev-socks5-tunnel.git
+[submodule "byedpi"]
path = app/src/main/cpp/byedpi
url = https://github.com/hufrea/byedpi.git
diff --git a/.idea/vcs.xml b/.idea/vcs.xml
index 31f9673..d76c0bb 100644
--- a/.idea/vcs.xml
+++ b/.idea/vcs.xml
@@ -2,7 +2,11 @@
-
+
+
+
+
+
\ No newline at end of file
diff --git a/app/build.gradle.kts b/app/build.gradle.kts
index 005d47b..354635f 100644
--- a/app/build.gradle.kts
+++ b/app/build.gradle.kts
@@ -1,5 +1,3 @@
-import com.android.build.gradle.internal.tasks.factory.dependsOn
-
plugins {
id("com.android.application")
id("org.jetbrains.kotlin.android")
@@ -13,10 +11,17 @@ android {
applicationId = "io.github.dovecoteescapee.byedpi"
minSdk = 21
targetSdk = 34
- versionCode = 7
- versionName = "1.0.2"
+ versionCode = 8
+ versionName = "1.1.0-beta"
testInstrumentationRunner = "androidx.test.runner.AndroidJUnitRunner"
+
+ ndk {
+ abiFilters.add("armeabi-v7a")
+ abiFilters.add("arm64-v8a")
+ abiFilters.add("x86")
+ abiFilters.add("x86_64")
+ }
}
buildFeatures {
@@ -61,8 +66,6 @@ android {
}
dependencies {
- implementation(files("libs/tun2socks.aar"))
-
implementation("androidx.fragment:fragment-ktx:1.8.2")
implementation("androidx.core:core-ktx:1.13.1")
implementation("androidx.appcompat:appcompat:1.7.0")
@@ -77,75 +80,25 @@ dependencies {
androidTestImplementation("androidx.test.espresso:espresso-core:3.6.1")
}
-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("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"
- )
- }
- }
-}
-
-val getGomobileBind = tasks.register("getGomobileBind") {
+tasks.register("runNdkBuild") {
group = "build"
- description = "Get gomobile bind for compiling tun2socks"
+
+ val ndkDir = android.ndkDirectory
+ executable = if (System.getProperty("os.name").startsWith("Windows", ignoreCase = true)) {
+ "$ndkDir\\ndk-build.cmd"
+ } else {
+ "$ndkDir/ndk-build"
+ }
+ setArgs(listOf(
+ "NDK_PROJECT_PATH=build/intermediates/ndkBuild",
+ "NDK_LIBS_OUT=src/main/jniLibs",
+ "APP_BUILD_SCRIPT=src/main/jni/Android.mk",
+ "NDK_APPLICATION_MK=src/main/jni/Application.mk"
+ ))
+
+ println("Command: $commandLine")
}
-val buildTun2Socks = tasks.register("buildTun2Socks") {
- group = "build"
- description = "Build tun2socks for Android"
- dependsOn(getGomobileBind)
-}
-
-tasks.preBuild.dependsOn(buildTun2Socks)
+tasks.preBuild {
+ dependsOn("runNdkBuild")
+}
\ No newline at end of file
diff --git a/app/libs/tun2socks b/app/libs/tun2socks
deleted file mode 160000
index e083daf..0000000
--- a/app/libs/tun2socks
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit e083dafcf534d85e7fce86b3d48ee5c7a63c604e
diff --git a/app/src/main/cpp/CMakeLists.txt b/app/src/main/cpp/CMakeLists.txt
index 2e1d16f..dee3304 100644
--- a/app/src/main/cpp/CMakeLists.txt
+++ b/app/src/main/cpp/CMakeLists.txt
@@ -1,53 +1,14 @@
-# For more information about using CMake with Android Studio, read the
-# documentation: https://d.android.com/studio/projects/add-native-code.html.
-# For more examples on how to use CMake, see https://github.com/android/ndk-samples.
-
-# Sets the minimum CMake version required for this project.
cmake_minimum_required(VERSION 3.22.1)
-# Declares the project name. The project name can be accessed via ${ PROJECT_NAME},
-# Since this is the top level CMakeLists.txt, the project name is also accessible
-# with ${CMAKE_PROJECT_NAME} (both CMake variables are in-sync within the top level
-# build script scope).
-project("byedpi")
+project(byedpi_native)
-# Creates and names a library, sets it as either STATIC
-# or SHARED, and provides the relative paths to its source code.
-# You can define multiple libraries, and CMake builds them for you.
-# Gradle automatically packages shared libraries with your APK.
-#
-# In this top level CMakeLists.txt, ${CMAKE_PROJECT_NAME} is used to define
-# the target library name; in the sub-module's CMakeLists.txt, ${PROJECT_NAME}
-# is preferred for the same purpose.
-#
-# In order to load a library into your app from Java/Kotlin, you must call
-# System.loadLibrary() and pass the name of the library defined here;
-# for GameActivity/NativeActivity derived applications, the same library name must be
-# used in the AndroidManifest.xml file.
-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
- )
+file(GLOB BYE_DPI_SRC byedpi/*.c)
+list(REMOVE_ITEM BYE_DPI_SRC ${CMAKE_CURRENT_SOURCE_DIR}/byedpi/win_service.c)
-include_directories("byedpi")
+add_library(byedpi SHARED ${BYE_DPI_SRC} native-lib.c utils.c)
+target_include_directories(byedpi PRIVATE byedpi)
-set(CMAKE_C_FLAGS "-std=c99 -O2 -D_XOPEN_SOURCE=500")
+target_compile_options(byedpi PRIVATE -std=c99 -O2 -D_XOPEN_SOURCE=500)
+target_compile_definitions(byedpi PRIVATE ANDROID_APP)
-add_compile_definitions(ANDROID_APP)
-
-# Specifies libraries CMake should link to your target library. You
-# can link libraries from various origins, such as libraries defined in this
-# build script, prebuilt third-party libraries, or Android system libraries.
-target_link_libraries(${CMAKE_PROJECT_NAME}
- # List libraries link to the target library
- android
- log
- )
\ No newline at end of file
+target_link_libraries(byedpi PRIVATE android log)
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 45e6714..f5fdb7c 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
@@ -8,9 +8,6 @@ import android.os.Build
import android.os.ParcelFileDescriptor
import android.util.Log
import androidx.lifecycle.lifecycleScope
-import engine.Engine
-import engine.Key
-import io.github.dovecoteescapee.byedpi.BuildConfig
import io.github.dovecoteescapee.byedpi.R
import io.github.dovecoteescapee.byedpi.activities.MainActivity
import io.github.dovecoteescapee.byedpi.core.ByeDpiProxy
@@ -23,11 +20,12 @@ import kotlinx.coroutines.launch
import kotlinx.coroutines.sync.Mutex
import kotlinx.coroutines.sync.withLock
import kotlinx.coroutines.withContext
+import java.io.File
class ByeDpiVpnService : LifecycleVpnService() {
- private val proxy = ByeDpiProxy()
+ private val byeDpiProxy = ByeDpiProxy()
private var proxyJob: Job? = null
- private var vpn: ParcelFileDescriptor? = null
+ private var tunFd: ParcelFileDescriptor? = null
private val mutex = Mutex()
private var stopping: Boolean = false
@@ -138,7 +136,7 @@ class ByeDpiVpnService : LifecycleVpnService() {
val preferences = getByeDpiPreferences()
proxyJob = lifecycleScope.launch(Dispatchers.IO) {
- val code = proxy.startProxy(preferences)
+ val code = byeDpiProxy.startProxy(preferences)
withContext(Dispatchers.Main) {
if (code != 0) {
@@ -164,7 +162,7 @@ class ByeDpiVpnService : LifecycleVpnService() {
return
}
- proxy.stopProxy()
+ byeDpiProxy.stopProxy()
proxyJob?.join() ?: throw IllegalStateException("ProxyJob field null")
proxyJob = null
@@ -174,7 +172,7 @@ class ByeDpiVpnService : LifecycleVpnService() {
private fun startTun2Socks() {
Log.i(TAG, "Starting tun2socks")
- if (vpn != null) {
+ if (tunFd != null) {
throw IllegalStateException("VPN field not null")
}
@@ -182,22 +180,49 @@ class ByeDpiVpnService : LifecycleVpnService() {
val port = sharedPreferences.getString("byedpi_proxy_port", null)?.toInt() ?: 1080
val dns = sharedPreferences.getStringNotNull("dns_ip", "1.1.1.1")
+ val tun2socksConfig = """
+ | misc:
+ | task-stack-size: 81920
+ | socks5:
+ | mtu: 8500
+ | address: 127.0.0.1
+ | port: $port
+ | udp: udp
+ """.trimMargin("| ")
+
+ val configPath = try {
+ File.createTempFile("config", "tmp", cacheDir).apply {
+ writeText(tun2socksConfig)
+ }
+ } catch (e: Exception) {
+ Log.e(TAG, "Failed to create config file", e)
+ throw e
+ }
+
val vpn = createBuilder(dns).establish()
?: throw IllegalStateException("VPN connection failed")
- this.vpn = vpn
-// val fd = vpn.detachFd()
- Engine.insert(createKey(vpn.fd, port))
- Engine.start()
+ this.tunFd = vpn
+
+ Log.d(TAG, "Native tun2socks start")
+ TProxyService.TProxyStartService(configPath.absolutePath, vpn.fd)
Log.i(TAG, "Tun2Socks started")
}
private fun stopTun2Socks() {
Log.i(TAG, "Stopping tun2socks")
-// Engine.stop() // sometimes crashes with fdsan
- vpn?.close() ?: Log.w(TAG, "VPN not running") // Is engine close sockets?
- vpn = null
+
+ 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")
}
@@ -212,6 +237,7 @@ class ByeDpiVpnService : LifecycleVpnService() {
setStatus(
when (newStatus) {
ServiceStatus.Connected -> AppStatus.Running
+
ServiceStatus.Disconnected,
ServiceStatus.Failed -> {
proxyJob = null
@@ -261,23 +287,8 @@ class ByeDpiVpnService : LifecycleVpnService() {
builder.setMetered(false)
}
- builder.addDisallowedApplication("io.github.dovecoteescapee.byedpi")
+ builder.addDisallowedApplication(applicationContext.packageName)
return builder
}
-
- private fun createKey(fd: Int, port: Int): Key = Key().apply {
- mark = 0
- mtu = 0
- device = "fd://${fd}"
-
- setInterface("")
- logLevel = if (BuildConfig.DEBUG) "debug" else "info"
- proxy = "socks5://127.0.0.1:$port"
-
- restAPI = ""
- tcpSendBufferSize = ""
- tcpReceiveBufferSize = ""
- tcpModerateReceiveBuffer = false
- }
}
diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/services/LifecycleVpnService.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/LifecycleVpnService.kt
index d6dc85d..49010f5 100644
--- a/app/src/main/java/io/github/dovecoteescapee/byedpi/services/LifecycleVpnService.kt
+++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/LifecycleVpnService.kt
@@ -9,7 +9,7 @@ import androidx.lifecycle.LifecycleOwner
import androidx.lifecycle.ServiceLifecycleDispatcher
/**
- * Based on @link [androidx.lifecycle.LifecycleService]
+ * Based on [androidx.lifecycle.LifecycleService]
*/
open class LifecycleVpnService : VpnService(), LifecycleOwner {
@Suppress("LeakingThis")
@@ -28,10 +28,10 @@ open class LifecycleVpnService : VpnService(), LifecycleOwner {
}
@Deprecated("Deprecated in Java")
- @Suppress("DEPRECATION")
@CallSuper
override fun onStart(intent: Intent?, startId: Int) {
dispatcher.onServicePreSuperOnStart()
+ @Suppress("DEPRECATION")
super.onStart(intent, startId)
}
diff --git a/app/src/main/java/io/github/dovecoteescapee/byedpi/services/TProxyService.kt b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/TProxyService.kt
new file mode 100644
index 0000000..17775a6
--- /dev/null
+++ b/app/src/main/java/io/github/dovecoteescapee/byedpi/services/TProxyService.kt
@@ -0,0 +1,17 @@
+package io.github.dovecoteescapee.byedpi.services
+
+object TProxyService {
+ init {
+ System.loadLibrary("hev-socks5-tunnel")
+ }
+
+ @JvmStatic
+ external fun TProxyStartService(configPath: String, fd: Int)
+
+ @JvmStatic
+ external fun TProxyStopService()
+
+ @JvmStatic
+ @Suppress("unused")
+ external fun TProxyGetStats(): LongArray
+}
diff --git a/app/src/main/jni/Android.mk b/app/src/main/jni/Android.mk
new file mode 100644
index 0000000..548efc9
--- /dev/null
+++ b/app/src/main/jni/Android.mk
@@ -0,0 +1,16 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+include $(call all-subdir-makefiles)
diff --git a/app/src/main/jni/Application.mk b/app/src/main/jni/Application.mk
new file mode 100644
index 0000000..4960cc5
--- /dev/null
+++ b/app/src/main/jni/Application.mk
@@ -0,0 +1,21 @@
+# Copyright (C) 2023 The Android Open Source Project
+#
+# Licensed under the Apache License, Version 2.0 (the "License");
+# you may not use this file except in compliance with the License.
+# You may obtain a copy of the License at
+#
+# http://www.apache.org/licenses/LICENSE-2.0
+#
+# Unless required by applicable law or agreed to in writing, software
+# distributed under the License is distributed on an "AS IS" BASIS,
+# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+# See the License for the specific language governing permissions and
+# limitations under the License.
+#
+
+APP_OPTIM := release
+APP_PLATFORM := android-21
+APP_ABI := armeabi-v7a arm64-v8a x86 x86_64
+APP_CFLAGS := -O3 -DPKGNAME=io/github/dovecoteescapee/byedpi/services
+APP_CPPFLAGS := -O3 -std=c++11
+NDK_TOOLCHAIN_VERSION := clang
diff --git a/app/src/main/jni/hev-socks5-tunnel b/app/src/main/jni/hev-socks5-tunnel
new file mode 160000
index 0000000..677bb45
--- /dev/null
+++ b/app/src/main/jni/hev-socks5-tunnel
@@ -0,0 +1 @@
+Subproject commit 677bb4530cfc867cd44d88d298960c8d8d9fbfad