diff --git a/app/src/main/cpp/byedpi b/app/src/main/cpp/byedpi index 12adfe2..6b484d5 160000 --- a/app/src/main/cpp/byedpi +++ b/app/src/main/cpp/byedpi @@ -1 +1 @@ -Subproject commit 12adfe285f603bfa38c19e9057d897b2b4e40941 +Subproject commit 6b484d598817cffd61344a812721fb00089f5095 diff --git a/app/src/main/cpp/native-lib.c b/app/src/main/cpp/native-lib.c index 9371211..6e3cbaf 100644 --- a/app/src/main/cpp/native-lib.c +++ b/app/src/main/cpp/native-lib.c @@ -5,7 +5,6 @@ #include #include #include -#include #include #include @@ -17,24 +16,15 @@ const enum demode DESYNC_METHODS[] = { DESYNC_FAKE }; +extern int NOT_EXIT; extern struct packet fake_tls, fake_http; extern int get_default_ttl(); extern int get_addr(const char *str, struct sockaddr_ina *addr); JNIEXPORT jint JNICALL -Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniEventFd(JNIEnv *env, jobject thiz) { - int fd = eventfd(0, EFD_NONBLOCK); - if (fd < 0) { - return -1; - } - return fd; -} - -JNIEXPORT jint JNICALL -Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniStartProxy( +Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniCreateSocket( JNIEnv *env, jobject thiz, - jint event_fd, jstring ip, jint port, jint max_connections, @@ -87,22 +77,33 @@ Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniStartProxy( } } - int res = listener(event_fd, s); - - if (close(event_fd) < 0) { - uniperror("close"); + int fd = listen_socket(&s); + if (fd < 0) { + uniperror("listen_socket"); + return get_e(); } - return res < 0 ? get_e() : 0; + LOG(LOG_S, "listen_socket, fd: %d", fd); + return fd; +} + +JNIEXPORT jint JNICALL +Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniStartProxy(JNIEnv *env, jobject thiz, + jint fd) { + LOG(LOG_S, "start_proxy, fd: %d", fd); + NOT_EXIT = 1; + if (event_loop(fd) < 0) { + return get_e(); + } + return 0; } JNIEXPORT jint JNICALL Java_io_github_dovecoteescapee_byedpi_core_ByeDpiProxy_jniStopProxy(JNIEnv *env, jobject thiz, - jint event_fd) { - if (eventfd_write(event_fd, 1) < 0) { - uniperror("eventfd_write"); - LOG(LOG_S, "event_fd: %d", event_fd); + jint fd) { + LOG(LOG_S, "stop_proxy, fd: %d", fd); + if (shutdown(fd, SHUT_RDWR) < 0) { + return get_e(); } - return 0; } \ No newline at end of file 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 8ee663f..372eeb5 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 @@ -1,5 +1,7 @@ package io.github.dovecoteescapee.byedpi.core +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import java.io.IOException class ByeDpiProxy { @@ -9,42 +11,61 @@ class ByeDpiProxy { } } - private val fd = jniEventFd() + private val mutex = Mutex() + private var fd = -1 - init { - if (fd < 0) { - throw IOException("Failed to create eventfd") + suspend fun startProxy(preferences: ByeDpiProxyPreferences): Int = + jniStartProxy(createSocket(preferences)) + + suspend fun stopProxy(): Int { + mutex.withLock { + if (fd < 0) { + throw IllegalStateException("Proxy is not running") + } + + val result = jniStopProxy(fd) + if (result == 0) { + fd = -1 + } + return result } } - fun startProxy(preferences: ByeDpiProxyPreferences): Int = - jniStartProxy( - eventFd = fd, - ip = preferences.ip, - port = preferences.port, - maxConnections = preferences.maxConnections, - bufferSize = preferences.bufferSize, - defaultTtl = preferences.defaultTtl, - noDomain = preferences.noDomain, - desyncKnown = preferences.desyncKnown, - desyncMethod = preferences.desyncMethod.ordinal, - splitPosition = preferences.splitPosition, - splitAtHost = preferences.splitAtHost, - fakeTtl = preferences.fakeTtl, - hostMixedCase = preferences.hostMixedCase, - domainMixedCase = preferences.domainMixedCase, - hostRemoveSpaces = preferences.hostRemoveSpaces, - tlsRecordSplit = preferences.tlsRecordSplit, - tlsRecordSplitPosition = preferences.tlsRecordSplitPosition, - tlsRecordSplitAtSni = preferences.tlsRecordSplitAtSni, - ) + private suspend fun createSocket(preferences: ByeDpiProxyPreferences): Int = + mutex.withLock { + if (fd >= 0) { + throw IllegalStateException("Proxy is already running") + } - fun stopProxy(): Int = jniStopProxy(fd) + val fd = jniCreateSocket( + ip = preferences.ip, + port = preferences.port, + maxConnections = preferences.maxConnections, + bufferSize = preferences.bufferSize, + defaultTtl = preferences.defaultTtl, + noDomain = preferences.noDomain, + desyncKnown = preferences.desyncKnown, + desyncMethod = preferences.desyncMethod.ordinal, + splitPosition = preferences.splitPosition, + splitAtHost = preferences.splitAtHost, + fakeTtl = preferences.fakeTtl, + hostMixedCase = preferences.hostMixedCase, + domainMixedCase = preferences.domainMixedCase, + hostRemoveSpaces = preferences.hostRemoveSpaces, + tlsRecordSplit = preferences.tlsRecordSplit, + tlsRecordSplitPosition = preferences.tlsRecordSplitPosition, + tlsRecordSplitAtSni = preferences.tlsRecordSplitAtSni, + ) - private external fun jniEventFd(): Int + if (fd < 0) { + throw IOException("Failed to create socket") + } - private external fun jniStartProxy( - eventFd: Int, + this.fd = fd + fd + } + + private external fun jniCreateSocket( ip: String, port: Int, maxConnections: Int, @@ -64,5 +85,7 @@ class ByeDpiProxy { tlsRecordSplitAtSni: Boolean, ): Int - private external fun jniStopProxy(eventFd: Int): Int + private external fun jniStartProxy(fd: Int): Int + + private external fun jniStopProxy(fd: Int): Int } \ No newline at end of file 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 b92391d..5de1e64 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 @@ -41,25 +41,23 @@ class ByeDpiProxyPreferences( constructor(preferences: SharedPreferences) : this( ip = preferences.getString("byedpi_proxy_ip", null), - port = preferences.getString("byedpi_proxy_port", null)?.toInt(), - maxConnections = preferences.getString("byedpi_max_connections", null)?.toInt(), - bufferSize = preferences.getString("byedpi_buffer_size", null)?.toInt(), - defaultTtl = preferences.getString("byedpi_default_ttl", null)?.toInt(), + port = preferences.getString("byedpi_proxy_port", null)?.toIntOrNull(), + maxConnections = preferences.getString("byedpi_max_connections", null)?.toIntOrNull(), + 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)?.toInt(), + splitPosition = preferences.getString("byedpi_split_position", null)?.toIntOrNull(), splitAtHost = preferences.getBoolean("byedpi_split_at_host", false), - fakeTtl = preferences.getString("byedpi_fake_ttl", null)?.toInt(), + fakeTtl = preferences.getString("byedpi_fake_ttl", null)?.toIntOrNull(), hostMixedCase = preferences.getBoolean("byedpi_host_mixed_case", false), domainMixedCase = preferences.getBoolean("byedpi_domain_mixed_case", false), hostRemoveSpaces = preferences.getBoolean("byedpi_host_remove_spaces", false), tlsRecordSplit = preferences.getBoolean("byedpi_tlsrec_enabled", false), - tlsRecordSplitPosition = preferences.getString("byedpi_tlsrec_position", null)?.toInt(), + tlsRecordSplitPosition = preferences.getString("byedpi_tlsrec_position", null)?.toIntOrNull(), tlsRecordSplitAtSni = preferences.getBoolean("byedpi_tlsrec_at_sni", false), - - ) enum class DesyncMethod { 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 da9942d..492ea3b 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 @@ -24,11 +24,14 @@ import io.github.dovecoteescapee.byedpi.utility.registerNotificationChannel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext class ByeDpiProxyService : LifecycleService() { - private var proxy: ByeDpiProxy? = null + private var proxy = ByeDpiProxy() private var proxyJob: Job? = null + private val mutex = Mutex() companion object { private val TAG: String = ByeDpiProxyService::class.java.simpleName @@ -77,7 +80,9 @@ class ByeDpiProxyService : LifecycleService() { } try { - startProxy() + mutex.withLock { + startProxy() + } updateStatus(ServiceStatus.CONNECTED) startForeground() } catch (e: Exception) { @@ -103,7 +108,9 @@ class ByeDpiProxyService : LifecycleService() { private suspend fun stop() { Log.i(TAG, "Stopping VPN") - stopProxy() + mutex.withLock { + stopProxy() + } updateStatus(ServiceStatus.DISCONNECTED) stopSelf() } @@ -111,7 +118,7 @@ class ByeDpiProxyService : LifecycleService() { private suspend fun startProxy() { Log.i(TAG, "Starting proxy") - if (proxy != null || proxyJob != null) { + if (proxyJob != null) { Log.w(TAG, "Proxy fields not null") throw IllegalStateException("Proxy fields not null") } @@ -120,7 +127,7 @@ class ByeDpiProxyService : LifecycleService() { val preferences = getByeDpiPreferences() proxyJob = lifecycleScope.launch(Dispatchers.IO) { - val code = proxy?.startProxy(preferences) + val code = proxy.startProxy(preferences) withContext(Dispatchers.Main) { if (code != 0) { @@ -141,13 +148,12 @@ class ByeDpiProxyService : LifecycleService() { if (status == ServiceStatus.DISCONNECTED) { Log.w(TAG, "Proxy already disconnected") return - } else { - proxy?.stopProxy() - proxyJob?.join() - proxy = null - proxyJob = null } + proxy.stopProxy() + proxyJob?.join() + proxyJob = null + Log.i(TAG, "Proxy stopped") } 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 87e06f1..d694f98 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 @@ -29,17 +29,15 @@ import io.github.dovecoteescapee.byedpi.utility.registerNotificationChannel import kotlinx.coroutines.Dispatchers import kotlinx.coroutines.Job import kotlinx.coroutines.launch -import kotlinx.coroutines.sync.Semaphore -import kotlinx.coroutines.sync.withPermit +import kotlinx.coroutines.sync.Mutex +import kotlinx.coroutines.sync.withLock import kotlinx.coroutines.withContext class ByeDpiVpnService : LifecycleVpnService() { - private var proxy: ByeDpiProxy? = null + private val proxy = ByeDpiProxy() private var proxyJob: Job? = null private var vpn: ParcelFileDescriptor? = null - private val semaphore = Semaphore(1) - - @Volatile + private val mutex = Mutex() private var stopping: Boolean = false companion object { @@ -94,7 +92,7 @@ class ByeDpiVpnService : LifecycleVpnService() { } try { - semaphore.withPermit { + mutex.withLock { startProxy() startTun2Socks() } @@ -123,8 +121,7 @@ class ByeDpiVpnService : LifecycleVpnService() { private suspend fun stop() { Log.i(TAG, "Stopping") - // Wait end of starting - semaphore.withPermit { + mutex.withLock { stopping = true try { stopTun2Socks() @@ -143,16 +140,15 @@ class ByeDpiVpnService : LifecycleVpnService() { private suspend fun startProxy() { Log.i(TAG, "Starting proxy") - if (proxy != null || proxyJob != null) { + if (proxyJob != null) { Log.w(TAG, "Proxy fields not null") throw IllegalStateException("Proxy fields not null") } - proxy = ByeDpiProxy() val preferences = getByeDpiPreferences() proxyJob = lifecycleScope.launch(Dispatchers.IO) { - val code = proxy?.startProxy(preferences) + val code = proxy.startProxy(preferences) withContext(Dispatchers.Main) { if (code != 0) { @@ -176,13 +172,12 @@ class ByeDpiVpnService : LifecycleVpnService() { if (status == ServiceStatus.DISCONNECTED) { Log.w(TAG, "Proxy already disconnected") return - } else { - proxy?.stopProxy() ?: throw IllegalStateException("Proxy field null") - proxyJob?.join() ?: throw IllegalStateException("ProxyJob field null") - proxy = null - proxyJob = null } + proxy.stopProxy() + proxyJob?.join() ?: throw IllegalStateException("ProxyJob field null") + proxyJob = null + Log.i(TAG, "Proxy stopped") }