diff --git a/core/clash.gradle.kts b/core/clash.gradle.kts index 3604868..c89cf32 100644 --- a/core/clash.gradle.kts +++ b/core/clash.gradle.kts @@ -93,7 +93,9 @@ fun generateGolangBuildEnvironment(abi: String): Map { "LD" to compilerBase.resolve(linker).absolutePath, "GOOS" to "android", "GOARCH" to golangArch, - "CGO_ENABLED" to "1" + "CGO_ENABLED" to "1", + "LDFLAGS" to "-lpthread", + "CFLAGS" to "-O3" ) } diff --git a/core/src/main/cpp/CMakeLists.txt b/core/src/main/cpp/CMakeLists.txt index 31eb2b0..88849a0 100644 --- a/core/src/main/cpp/CMakeLists.txt +++ b/core/src/main/cpp/CMakeLists.txt @@ -1,10 +1,11 @@ cmake_minimum_required(VERSION 3.0) +project(clash_bridge) set(CMAKE_C_STANDARD 11) -set(CMAKE_CXX_STANDARD 17) +set(CMAKE_CXX_STANDARD 11) include_directories(${CLASH_OUTPUT}/${ANDROID_ABI} ${CLASH_SOURCE}) link_directories(${CLASH_OUTPUT}/${CMAKE_ANDROID_ARCH_ABI}) link_libraries(log clash) -add_library(bridge SHARED bridge.c) \ No newline at end of file +add_library(bridge SHARED main.cpp main.h init.cpp query.cpp patch.cpp) \ No newline at end of file diff --git a/core/src/main/cpp/bridge.c b/core/src/main/cpp/bridge.c deleted file mode 100644 index b035bcf..0000000 --- a/core/src/main/cpp/bridge.c +++ /dev/null @@ -1,159 +0,0 @@ -#include -#include -#include -#include - -#include "libclash.h" - -jclass cClashException; -jclass cTraffic; -jclass cGeneral; -jmethodID cTrafficConstructor; -jmethodID cGeneralConstructor; - -JNIEXPORT void JNICALL -Java_com_github_kr328_clash_core_bridge_Bridge_initialize(JNIEnv *env, jclass clazz, - jbyteArray database, - jstring home, - jstring version) { - buffer_t databaseBuffer = { - .buffer = (*env)->GetByteArrayElements(env, database, NULL), - .length = (*env)->GetArrayLength(env, database) - }; - const_string_t homeString = (*env)->GetStringUTFChars(env, home, NULL); - const_string_t versionString = (*env)->GetStringUTFChars(env, version, NULL); - - initialize(&databaseBuffer, homeString, versionString); - - (*env)->ReleaseByteArrayElements(env, database, databaseBuffer.buffer, databaseBuffer.length); - (*env)->ReleaseStringUTFChars(env, home, homeString); - (*env)->ReleaseStringUTFChars(env, version, versionString); -} - -JNIEXPORT void JNICALL -Java_com_github_kr328_clash_core_bridge_Bridge_reset(JNIEnv *env, jclass clazz) { - reset(); -} - -JNIEXPORT jobject JNICALL -Java_com_github_kr328_clash_core_bridge_Bridge_queryGeneral(JNIEnv *env, jclass clazz) { - general_t general; - - queryGeneral(&general); - - const char *mode = ""; - - switch (general.mode) { - case MODE_DIRECT: - mode = "Direct"; - break; - case MODE_GLOBAL: - mode = "Global"; - break; - case MODE_RULE: - mode = "Rule"; - break; - } - - return (*env)->NewObject(env, - cGeneral, - cGeneralConstructor, - (*env)->NewStringUTF(env, mode), - general.http_port, - general.socks_port, - general.redirect_port, - general.mixed_port); -} - -JNIEXPORT jobject JNICALL -Java_com_github_kr328_clash_core_bridge_Bridge_querySpeed(JNIEnv *env, jclass clazz) { - traffic_t traffic; - - querySpeed(&traffic); - - return (*env)->NewObject(env, cTraffic, cTrafficConstructor, (jlong)traffic.upload, (jlong)traffic.download); -} - -JNIEXPORT jobject JNICALL -Java_com_github_kr328_clash_core_bridge_Bridge_queryBandwidth(JNIEnv *env, jclass clazz) { - traffic_t traffic; - - queryBandwidth(&traffic); - - return (*env)->NewObject(env, cTraffic, cTrafficConstructor, (jlong)traffic.upload, (jlong)traffic.download); -} - -JNIEXPORT void JNICALL -Java_com_github_kr328_clash_core_bridge_Bridge_startTunDevice(JNIEnv *env, jclass clazz, - jint fd, - jint mtu, - jstring gateway, - jstring mirror, - jstring dns, - jint new_socket) { - - const_string_t gatewayString = (*env)->GetStringUTFChars(env, gateway, NULL); - const_string_t mirrorString = (*env)->GetStringUTFChars(env, mirror, NULL); - const_string_t dnsString = (*env)->GetStringUTFChars(env, dns, NULL); - - char *exception = startTunDevice(fd, mtu, gatewayString, mirrorString, dnsString, new_socket); - if ( exception != NULL ) - (*env)->ThrowNew(env, cClashException, exception); - free(exception); - - (*env)->ReleaseStringUTFChars(env, gateway, gatewayString); - (*env)->ReleaseStringUTFChars(env, mirror, mirrorString); - (*env)->ReleaseStringUTFChars(env, dns, dnsString); -} - -JNIEXPORT void JNICALL -Java_com_github_kr328_clash_core_bridge_Bridge_stopTunDevice(JNIEnv *env, jclass clazz) { - stopTunDevice(); -} - -JNIEXPORT void JNICALL -Java_com_github_kr328_clash_core_bridge_Bridge_setDnsOverride(JNIEnv *env, jclass clazz, - jboolean override_dns, - jstring append_nameservers) { - dns_override_t dns = { - .override_dns = override_dns, - .append_nameservers = (*env)->GetStringUTFChars(env, append_nameservers, NULL) - }; - - setDnsOverride(&dns); - - (*env)->ReleaseStringUTFChars(env, append_nameservers, dns.append_nameservers); -} - -JNIEXPORT void JNICALL -Java_com_github_kr328_clash_core_bridge_Bridge_setProxyMode(JNIEnv *env, jclass clazz, - jstring mode) { - const char *m = (*env)->GetStringUTFChars(env, mode, NULL); - - if (strcmp(m, "Direct") == 0) { - setProxyMode(MODE_DIRECT); - } else if (strcmp(m, "Global") == 0) { - setProxyMode(MODE_GLOBAL); - } else if (strcmp(m, "Rule") == 0) { - setProxyMode(MODE_RULE); - } - - (*env)->ReleaseStringUTFChars(env, mode, m); -} - -JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) { - (void) unused; - - JNIEnv *env = NULL; - - if ( (*vm)->GetEnv(vm, (void**) &env, JNI_VERSION_1_6) != JNI_OK ) - return -1; - - cClashException = (*env)->FindClass(env, "com/github/kr328/clash/core/bridge/ClashException"); - cTraffic = (*env)->FindClass(env, "com/github/kr328/clash/core/model/Traffic"); - cGeneral = (*env)->FindClass(env, "com/github/kr328/clash/core/model/General"); - cTrafficConstructor = (*env)->GetMethodID(env, cTraffic, "", "(JJ)V"); - cGeneralConstructor = (*env)->GetMethodID(env, cGeneral, "", "(Ljava/lang/String;IIII)V"); - - return JNI_VERSION_1_6; -} \ No newline at end of file diff --git a/core/src/main/cpp/init.cpp b/core/src/main/cpp/init.cpp new file mode 100644 index 0000000..83aabb1 --- /dev/null +++ b/core/src/main/cpp/init.cpp @@ -0,0 +1,30 @@ +#include "main.h" + +extern "C" +JNIEXPORT void JNICALL +Java_com_github_kr328_clash_core_bridge_Bridge_initialize(JNIEnv *env, jclass clazz, + jbyteArray database, jstring home, + jstring version) { + UNUSED(clazz); + + Master::runWithContext(env, [&](Master::Context *context) { + const_buffer_t databaseBuffer = context->createConstBufferFromByteArray(database); + const char *homeString = context->getString(home); + const char *versionString = context->getString(version); + + initialize(&databaseBuffer, homeString, versionString); + + context->releaseConstBufferFromByteArray(database, databaseBuffer); + context->releaseString(home, homeString); + context->releaseString(version, versionString); + }); +} + +extern "C" +JNIEXPORT void JNICALL +Java_com_github_kr328_clash_core_bridge_Bridge_reset(JNIEnv *env, jclass clazz) { + UNUSED(env); + UNUSED(clazz); + + reset(); +} \ No newline at end of file diff --git a/core/src/main/cpp/main.cpp b/core/src/main/cpp/main.cpp new file mode 100644 index 0000000..3a9711c --- /dev/null +++ b/core/src/main/cpp/main.cpp @@ -0,0 +1,113 @@ +#include "main.h" + +Master *Master::master = nullptr; + +Master::Master(JNIEnv *env) { + master = this; + + cClashException = env->FindClass("com/github/kr328/clash/core/bridge/ClashException"); + cTraffic = env->FindClass("com/github/kr328/clash/core/model/Traffic"); + cGeneral = env->FindClass("com/github/kr328/clash/core/model/General"); + cCompletableFuture = env->FindClass("java/util/concurrent/CompletableFuture"); + cClashExceptionConstructor = env->GetMethodID(cClashException, "", + "(Ljava/lang/String;)V"); + cTrafficConstructor = env->GetMethodID(cTraffic, "", "(JJ)V"); + cGeneralConstructor = env->GetMethodID(cGeneral, "", "(Ljava/lang/String;IIII)V"); + cCompletableFutureConstructor = env->GetMethodID(cCompletableFuture, "", "()V"); + mCompletableFutureComplete = env->GetMethodID(cCompletableFuture, "complete", + "(Ljava/lang/Object;)Z"); + mCompletableFutureCompleteExceptionally = env->GetMethodID(cCompletableFuture, + "completeExceptionally", + "(Ljava/lang/Throwable;)Z"); +} + +Master::Context::Context(JNIEnv *env) { + this->env = env; +} + +jthrowable Master::Context::newClashException(std::string const &reason) { + return reinterpret_cast(env->NewObject(master->cClashException, + master->cClashExceptionConstructor, + env->NewStringUTF(reason.c_str()))); +} + +void Master::Context::throwThrowable(jthrowable throwable) { + env->Throw(throwable); +} + +jobject Master::Context::newTraffic(long upload, long download) { + return env->NewObject(master->cTraffic, master->cTrafficConstructor, upload, download); +} + +jobject Master::Context::newGeneral(std::string const &mode, int http, int socks, int redirect, + int mixed) { + return env->NewObject(master->cGeneral, master->cGeneralConstructor, + env->NewStringUTF(mode.c_str()), http, socks, redirect, mixed); +} + +jobject Master::Context::newCompletableFuture() { + return env->NewObject(master->cCompletableFuture, master->cCompletableFutureConstructor); +} + +jobject Master::Context::newGlobalReference(jobject obj) { + return env->NewGlobalRef(obj); +} + +jobject Master::Context::removeGlobalReference(jobject obj) { + env->DeleteGlobalRef(obj); + + return obj; +} + +bool Master::Context::completeCompletableFuture(jobject completable, jobject object) { + return env->CallBooleanMethod(completable, master->mCompletableFutureComplete, object); +} + +bool +Master::Context::completeExceptionallyCompletableFuture(jobject completable, jthrowable throwable) { + return env->CallBooleanMethod(completable, master->mCompletableFutureCompleteExceptionally, + throwable); +} + +buffer_t Master::Context::createBufferFromByteArray(jbyteArray array) { + return { + .buffer = env->GetByteArrayElements(array, nullptr), + .length = env->GetArrayLength(array) + }; +} + +void Master::Context::releaseBufferFromByteArray(jbyteArray array, buffer_t &buffer) { + env->ReleaseByteArrayElements(array, reinterpret_cast(buffer.buffer), JNI_COMMIT); +} + +const_buffer_t Master::Context::createConstBufferFromByteArray(jbyteArray array) { + return { + .buffer = env->GetByteArrayElements(array, nullptr), + .length = env->GetArrayLength(array) + }; +} + +void Master::Context::releaseConstBufferFromByteArray(jbyteArray array, const_buffer_t &buffer) { + env->ReleaseByteArrayElements(array, const_cast(reinterpret_cast(buffer.buffer)), JNI_ABORT); +} + +const char *Master::Context::getString(jstring str) { + return env->GetStringUTFChars(str, nullptr); +} + +void Master::Context::releaseString(jstring str, const char *c) { + env->ReleaseStringUTFChars(str, c); +} + +JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) { + UNUSED(unused); + + JNIEnv *env = nullptr; + + if (vm->GetEnv(reinterpret_cast(&env), JNI_VERSION_1_6) != JNI_OK) + return -1; + + new Master(env); + + return JNI_VERSION_1_6; +} \ No newline at end of file diff --git a/core/src/main/cpp/main.h b/core/src/main/cpp/main.h new file mode 100644 index 0000000..bbc340f --- /dev/null +++ b/core/src/main/cpp/main.h @@ -0,0 +1,77 @@ +#include +#include +#include + +#include "libclash.h" + +#define UNUSED(v) ((void)v) + +class Master { +public: + class Context; + +public: + Master(JNIEnv *env); + +public: + template + static R runWithContext(JNIEnv *env, const std::function& func); + +private: + jclass cClashException; + jclass cTraffic; + jclass cGeneral; + jclass cCompletableFuture; + jmethodID cClashExceptionConstructor; + jmethodID cTrafficConstructor; + jmethodID cGeneralConstructor; + jmethodID cCompletableFutureConstructor; + jmethodID mCompletableFutureComplete; + jmethodID mCompletableFutureCompleteExceptionally; + +private: + static Master *master; + +private: + friend class Context; +}; + +class Master::Context { +public: + Context(JNIEnv *env); + +public: + jthrowable newClashException(std::string const &message); + jobject newTraffic(long upload, long download); + jobject newGeneral(std::string const &mode, int http, int socks, int redirect, int mixed); + jobject newCompletableFuture(); + +public: + void throwThrowable(jthrowable throwable); + +public: + jobject newGlobalReference(jobject obj); + jobject removeGlobalReference(jobject obj); + +public: + bool completeCompletableFuture(jobject completable, jobject object); + bool completeExceptionallyCompletableFuture(jobject completable, jthrowable throwable); + +public: + buffer_t createBufferFromByteArray(jbyteArray array); + void releaseBufferFromByteArray(jbyteArray array, buffer_t &buffer); + const_buffer_t createConstBufferFromByteArray(jbyteArray array); + void releaseConstBufferFromByteArray(jbyteArray array, const_buffer_t &buffer); + const char *getString(jstring str); + void releaseString(jstring str, const char *c); + +private: + JNIEnv *env; +}; + +template +R Master::runWithContext(JNIEnv *env, const std::function& func) { + Master::Context context(env); + + return func(&context); +} \ No newline at end of file diff --git a/core/src/main/cpp/patch.cpp b/core/src/main/cpp/patch.cpp new file mode 100644 index 0000000..ada2e3d --- /dev/null +++ b/core/src/main/cpp/patch.cpp @@ -0,0 +1,2 @@ +#include "main.h" + diff --git a/core/src/main/cpp/query.cpp b/core/src/main/cpp/query.cpp new file mode 100644 index 0000000..e65d8ac --- /dev/null +++ b/core/src/main/cpp/query.cpp @@ -0,0 +1,62 @@ +#include "main.h" + +extern "C" +JNIEXPORT jobject JNICALL +Java_com_github_kr328_clash_core_bridge_Bridge_queryGeneral(JNIEnv *env, jclass clazz) { + UNUSED(clazz); + + return Master::runWithContext(env, [&](Master::Context *context) -> jobject { + general_t general; + + queryGeneral(&general); + + const char *mode = nullptr; + + switch (general.mode) { + case MODE_DIRECT: + mode = "Direct"; + break; + case MODE_GLOBAL: + mode = "Global"; + break; + case MODE_RULE: + mode = "Rule"; + break; + case MODE_SCRIPT: + mode = "Script"; + break; + } + + return context->newGeneral(mode, + general.http_port, general.socks_port, + general.redirect_port, general.mixed_port); + }); +} + +extern "C" +JNIEXPORT jobject JNICALL +Java_com_github_kr328_clash_core_bridge_Bridge_queryBandwidth(JNIEnv *env, jclass clazz) { + UNUSED(clazz); + + return Master::runWithContext(env, [&](Master::Context *context) -> jobject { + traffic_t traffic; + + queryBandwidth(&traffic); + + return context->newTraffic(traffic.upload, traffic.download); + }); +} + +extern "C" +JNIEXPORT jobject JNICALL +Java_com_github_kr328_clash_core_bridge_Bridge_querySpeed(JNIEnv *env, jclass clazz) { + UNUSED(clazz); + + return Master::runWithContext(env, [&](Master::Context *context) -> jobject { + traffic_t traffic; + + querySpeed(&traffic); + + return context->newTraffic(traffic.upload, traffic.download); + }); +} \ No newline at end of file diff --git a/core/src/main/golang/buffer.h b/core/src/main/golang/buffer.h index c26f015..c12f34d 100644 --- a/core/src/main/golang/buffer.h +++ b/core/src/main/golang/buffer.h @@ -2,6 +2,10 @@ #include +#if __cplusplus +extern "C" { +#endif + typedef struct buffer_t { void *buffer; int length; @@ -12,4 +16,8 @@ typedef struct const_buffer_t { int length; } const_buffer_t; -typedef const char *const_string_t; \ No newline at end of file +typedef const char *const_string_t; + +#if __cplusplus +}; +#endif \ No newline at end of file diff --git a/core/src/main/golang/ccall.c b/core/src/main/golang/ccall.c new file mode 100644 index 0000000..34311e8 --- /dev/null +++ b/core/src/main/golang/ccall.c @@ -0,0 +1,31 @@ +#include "ccall.h" + +#include +#include + +extern void nextCcall(ccall_t *call, void **argument); +extern void finishCcall(); + +static void *run(void *arg) { + (void) arg; + + while ( 1 ) { + ccall_t call; + void *argument; + + nextCcall(&call, &argument); + + call.function(call.context, argument); + + finishCcall(); + } + + return NULL; +} + +void initialize_ccall() { + pthread_t tid; + + if ( pthread_create(&tid, NULL, &run, NULL) < 0 ) + abort(); +} diff --git a/core/src/main/golang/ccall.go b/core/src/main/golang/ccall.go new file mode 100644 index 0000000..fdd7093 --- /dev/null +++ b/core/src/main/golang/ccall.go @@ -0,0 +1,57 @@ +package main + +/* +#include "ccall.h" + +extern void initialize_ccall(); + */ +import "C" +import "unsafe" + +type nativeCcall C.ccall_t + +type ccallContext struct { + call nativeCcall + result chan struct{} + argument unsafe.Pointer +} + +var ccallQueue chan *ccallContext +var current *ccallContext + +func init() { + C.initialize_ccall() +} + +func callCcall(call nativeCcall, argument unsafe.Pointer) { + context := &ccallContext{ + call: call, + result: make(chan struct{}), + argument: argument, + } + + ccallQueue <- context + + <- context.result +} + +//export nextCcall +func nextCcall(ccall *nativeCcall, argument *unsafe.Pointer) { + ctx := <- ccallQueue + + current = ctx + + *ccall = ctx.call + *argument = ctx.argument +} + +//export finishCcall +func finishCcall() { + ctx := current + + current = nil + + if ctx != nil { + ctx.result <- struct{}{} + } +} \ No newline at end of file diff --git a/core/src/main/golang/ccall.h b/core/src/main/golang/ccall.h new file mode 100644 index 0000000..61817a8 --- /dev/null +++ b/core/src/main/golang/ccall.h @@ -0,0 +1,18 @@ +#pragma once + +#include + +#if __cplusplus +extern "C" { +#endif + +typedef void (*ccall_function_t)(void *context, void *argument); + +typedef struct ccall_t { + ccall_function_t function; + void *context; +} ccall_t; + +#if __cplusplus +}; +#endif \ No newline at end of file diff --git a/core/src/main/golang/config.h b/core/src/main/golang/config.h index 956450a..7f20ed7 100644 --- a/core/src/main/golang/config.h +++ b/core/src/main/golang/config.h @@ -1,6 +1,14 @@ #pragma once +#if __cplusplus +extern "C" { +#endif + typedef struct dns_override_t { int override_dns; const char *append_nameservers; -} dns_override_t; \ No newline at end of file +} dns_override_t; + +#if __cplusplus +}; +#endif \ No newline at end of file diff --git a/core/src/main/golang/general.h b/core/src/main/golang/general.h index ce65d1e..f237309 100644 --- a/core/src/main/golang/general.h +++ b/core/src/main/golang/general.h @@ -1,8 +1,13 @@ #pragma once +#if __cplusplus +extern "C" { +#endif + static const int MODE_DIRECT = 0; static const int MODE_GLOBAL = 1; static const int MODE_RULE = 2; +static const int MODE_SCRIPT = 3; typedef struct general_t { int mode; @@ -10,4 +15,8 @@ typedef struct general_t { int socks_port; int redirect_port; int mixed_port; -} general_t; \ No newline at end of file +} general_t; + +#if __cplusplus +}; +#endif \ No newline at end of file diff --git a/core/src/main/golang/main.go b/core/src/main/golang/main.go index f02852d..eaedd5c 100644 --- a/core/src/main/golang/main.go +++ b/core/src/main/golang/main.go @@ -13,7 +13,7 @@ import "C" func main() {} //export initialize -func initialize(database *C.buffer_t, home, version C.const_string_t) { +func initialize(database *C.const_buffer_t, home, version C.const_string_t) { databaseData := C.GoBytes(database.buffer, database.length) homeData := C.GoString(home) versionData := C.GoString(version) diff --git a/core/src/main/golang/traffic.h b/core/src/main/golang/traffic.h index 440fc4e..1a09383 100644 --- a/core/src/main/golang/traffic.h +++ b/core/src/main/golang/traffic.h @@ -2,7 +2,15 @@ #include +#if __cplusplus +extern "C" { +#endif + typedef struct traffic_t { uint64_t upload; uint64_t download; -} traffic_t; \ No newline at end of file +} traffic_t; + +#if __cplusplus +}; +#endif \ No newline at end of file diff --git a/core/src/main/golang/tun.go b/core/src/main/golang/tun.go index 1e3b55a..5e5d784 100644 --- a/core/src/main/golang/tun.go +++ b/core/src/main/golang/tun.go @@ -3,21 +3,20 @@ package main //#include "buffer.h" import "C" import ( - "encoding/binary" "github.com/Dreamacro/clash/component/dialer" "github.com/kr328/cfa/tun" - "io" "net" - "os" "sync" "syscall" + "unsafe" ) var lock sync.Mutex -var oldNewSocketPipe *os.File = nil //export startTunDevice -func startTunDevice(fd, mtu int, gateway, mirror, dns C.const_string_t, newSocket int) *C.char { +func startTunDevice(fd, mtu int, gateway, mirror, dns C.const_string_t, onNewSocket nativeCcall, onStop nativeCcall) *C.char { + stopTunDevice() + lock.Lock() defer lock.Unlock() @@ -25,38 +24,39 @@ func startTunDevice(fd, mtu int, gateway, mirror, dns C.const_string_t, newSocke m := C.GoString(mirror) d := C.GoString(dns) - err := tun.StartTunDevice(fd, mtu, g, m, d) - if err != nil { - return C.CString(err.Error()) - } + l := &sync.Mutex{} + c := false + closed := &c - if pipe := oldNewSocketPipe ; pipe != nil { - _ = pipe.Close() - } - - _ = syscall.SetNonblock(newSocket, true) - pipe := os.NewFile(uintptr(newSocket), "socket") - oldNewSocketPipe = pipe - - dialer.DialerHook = func(dialer *net.Dialer) error { - dialer.Control = func(network, address string, c syscall.RawConn) error { + dialer.DialerHook = func(d *net.Dialer) error { + d.Control = func(network, address string, c syscall.RawConn) error { return c.Control(func(fd uintptr) { - lock.Lock() - defer lock.Unlock() + l.Lock() + defer l.Unlock() - if pipe := oldNewSocketPipe ; pipe != nil { - var fdBytes [4]byte - - binary.BigEndian.PutUint32(fdBytes[:], uint32(fd)) - - _, _ = pipe.Write(fdBytes[:]) - _, _ = io.ReadFull(pipe, fdBytes[:]) + if *closed { + return } + + //noinspection GoVetUnsafePointer + callCcall(onNewSocket, unsafe.Pointer(fd)) }) } return nil } + err := tun.StartTunDevice(fd, mtu, g, m, d, func() { + l.Lock() + defer l.Unlock() + + *closed = true + + callCcall(onStop, nil) + }) + if err != nil { + return C.CString(err.Error()) + } + return nil } @@ -66,10 +66,4 @@ func stopTunDevice() { defer lock.Unlock() tun.StopTunDevice() - - if pipe := oldNewSocketPipe ; pipe != nil { - _ = pipe.Close() - } - - oldNewSocketPipe = nil } \ No newline at end of file diff --git a/core/src/main/golang/tun/tun.go b/core/src/main/golang/tun/tun.go index ed1ca00..89167c9 100644 --- a/core/src/main/golang/tun/tun.go +++ b/core/src/main/golang/tun/tun.go @@ -24,7 +24,7 @@ const ( var adapter *tun2socket.Tun2Socket var mutex sync.Mutex -func StartTunDevice(fd, mtu int, gateway, mirror, dnsAddress string) error { +func StartTunDevice(fd, mtu int, gateway, mirror, dnsAddress string, onStop func()) error { mutex.Lock() defer mutex.Unlock() @@ -60,6 +60,8 @@ func StartTunDevice(fd, mtu int, gateway, mirror, dnsAddress string) error { adapter.SetLogger(&ClashLogger{}) adapter.SetClosedHandler(func() { StopTunDevice() + + onStop() }) adapter.SetAllocator(func(length int) []byte { if length <= maxUdpPacketSize {