diff --git a/core/clash.gradle.kts b/core/clash.gradle.kts index 724ae87..257379a 100644 --- a/core/clash.gradle.kts +++ b/core/clash.gradle.kts @@ -145,7 +145,7 @@ task("compileClashCore") { mkdirs() }.resolve("libclash.so") - "go build --buildmode=c-shared -o \"$out\"".exec(pwd = golangSource, env = env) + "go build --buildmode=c-shared -trimpath -o \"$out\"".exec(pwd = golangSource, env = env) } } } diff --git a/core/src/main/cpp/CMakeLists.txt b/core/src/main/cpp/CMakeLists.txt index a2f2e58..dd75a94 100644 --- a/core/src/main/cpp/CMakeLists.txt +++ b/core/src/main/cpp/CMakeLists.txt @@ -8,4 +8,4 @@ include_directories(${CLASH_OUTPUT}/${ANDROID_ABI} ${CLASH_SOURCE}) link_directories(${CLASH_OUTPUT}/${CMAKE_ANDROID_ARCH_ABI}) link_libraries(log clash) -add_library(bridge SHARED main.cpp main.h init.cpp query.cpp patch.cpp tun.cpp defer.cpp) \ No newline at end of file +add_library(bridge SHARED main.cpp main.h init.cpp query.cpp patch.cpp tun.cpp defer.cpp event_queue.cpp) \ No newline at end of file diff --git a/core/src/main/cpp/defer.cpp b/core/src/main/cpp/defer.cpp index d40310a..c114cf6 100644 --- a/core/src/main/cpp/defer.cpp +++ b/core/src/main/cpp/defer.cpp @@ -56,25 +56,34 @@ Java_com_github_kr328_clash_core_bridge_Bridge_downloadProfile__ILjava_lang_Stri const char *b = context->getString(base); const char *o = context->getString(output); - auto *ctx = new deferContext(); + jobject completableFuture = context->newGlobalReference(context->newCompletableFuture()); - ctx->completableFuture = context->newGlobalReference(context->newCompletableFuture()); + uint64_t token = EventQueue::getInstance()->obtainToken(); - ccall_t onSuccess = { - .context = ctx, - .function = &downloadContextSuccess - }; - ccall_t onFailure = { - .context = ctx, - .function = &downloadContextFailure - }; + EventQueue::getInstance()->registerHandler(COMPLETE, token, [completableFuture](event_type_t, uint64_t token, std::string const &payload) { + Master::runWithAttached([&](JNIEnv *env) -> int { + Master::runWithContext(env, [&](Master::Context *context) { + if ( payload.empty() ) { + context->completeCompletableFuture(completableFuture, nullptr); + } else { + context->completeExceptionallyCompletableFuture(completableFuture, context->newClashException(payload)); + } - downloadProfileFromFd(fd, b, o, onSuccess, onFailure); + context->removeGlobalReference(completableFuture); + }); + + return 0; + }); + + EventQueue::getInstance()->unregisterHandler(COMPLETE, token); + }); + + downloadProfileFromFd(fd, b, o, token); context->releaseString(base, b); context->releaseString(output, o); - return ctx->completableFuture; + return completableFuture; }); } @@ -89,26 +98,35 @@ Java_com_github_kr328_clash_core_bridge_Bridge_downloadProfile__Ljava_lang_Strin const char *b = context->getString(base); const char *o = context->getString(output); - auto *ctx = new deferContext(); + jobject completableFuture = context->newGlobalReference(context->newCompletableFuture()); - ctx->completableFuture = context->newGlobalReference(context->newCompletableFuture()); + uint64_t token = EventQueue::getInstance()->obtainToken(); - ccall_t onSuccess = { - .context = ctx, - .function = &downloadContextSuccess - }; - ccall_t onFailure = { - .context = ctx, - .function = &downloadContextFailure - }; + EventQueue::getInstance()->registerHandler(COMPLETE, token, [completableFuture](event_type_t, uint64_t token, std::string const &payload) { + Master::runWithAttached([&](JNIEnv *env) -> int { + Master::runWithContext(env, [&](Master::Context *context) { + if ( payload.empty() ) { + context->completeCompletableFuture(completableFuture, nullptr); + } else { + context->completeExceptionallyCompletableFuture(completableFuture, context->newClashException(payload)); + } - downloadProfileFromUrl(u, b, o, onSuccess, onFailure); + context->removeGlobalReference(completableFuture); + }); + + return 0; + }); + + EventQueue::getInstance()->unregisterHandler(COMPLETE, token); + }); + + downloadProfileFromUrl(u, b, o, token); context->releaseString(url, u); context->releaseString(base, b); context->releaseString(output, o); - return ctx->completableFuture; + return completableFuture; }); } @@ -122,25 +140,34 @@ Java_com_github_kr328_clash_core_bridge_Bridge_loadProfile(JNIEnv *env, jclass c const char *p = context->getString(path); const char *b = context->getString(base); - auto *ctx = new deferContext(); + jobject completableFuture = context->newGlobalReference(context->newCompletableFuture()); - ctx->completableFuture = context->newGlobalReference(context->newCompletableFuture()); + uint64_t token = EventQueue::getInstance()->obtainToken(); - ccall_t onSuccess = { - .context = ctx, - .function = &downloadContextSuccess - }; - ccall_t onFailure = { - .context = ctx, - .function = &downloadContextFailure - }; + EventQueue::getInstance()->registerHandler(COMPLETE, token, [completableFuture](event_type_t, uint64_t token, std::string const &payload) { + Master::runWithAttached([&](JNIEnv *env) -> int { + Master::runWithContext(env, [&](Master::Context *context) { + if ( payload.empty() ) { + context->completeCompletableFuture(completableFuture, nullptr); + } else { + context->completeExceptionallyCompletableFuture(completableFuture, context->newClashException(payload)); + } - loadProfile(p, b, onSuccess, onFailure); + context->removeGlobalReference(completableFuture); + }); + + return 0; + }); + + EventQueue::getInstance()->unregisterHandler(COMPLETE, token); + }); + + loadProfile(p, b, token); context->releaseString(path, p); context->releaseString(base, b); - return ctx->completableFuture; + return completableFuture; }); } @@ -153,23 +180,32 @@ Java_com_github_kr328_clash_core_bridge_Bridge_performHealthCheck(JNIEnv *env, j return Master::runWithContext(env, [&](Master::Context *context) -> jobject { const char *g = context->getString(group); - auto *ctx = new deferContext(); + jobject completableFuture = context->newGlobalReference(context->newCompletableFuture()); - ctx->completableFuture = context->newGlobalReference(context->newCompletableFuture()); + uint64_t token = EventQueue::getInstance()->obtainToken(); - ccall_t onSuccess = { - .context = ctx, - .function = &downloadContextSuccess - }; - ccall_t onFailure = { - .context = ctx, - .function = &downloadContextFailure - }; + EventQueue::getInstance()->registerHandler(COMPLETE, token, [completableFuture](event_type_t, uint64_t token, std::string const &payload) { + Master::runWithAttached([&](JNIEnv *env) -> int { + Master::runWithContext(env, [&](Master::Context *context) { + if ( payload.empty() ) { + context->completeCompletableFuture(completableFuture, nullptr); + } else { + context->completeExceptionallyCompletableFuture(completableFuture, context->newClashException(payload)); + } - performHealthCheck(g, onSuccess, onFailure); + context->removeGlobalReference(completableFuture); + }); + + return 0; + }); + + EventQueue::getInstance()->unregisterHandler(COMPLETE, token); + }); + + performHealthCheck(g, token); context->releaseString(group, g); - return ctx->completableFuture; + return completableFuture; }); } \ No newline at end of file diff --git a/core/src/main/cpp/event_queue.cpp b/core/src/main/cpp/event_queue.cpp new file mode 100644 index 0000000..1986de4 --- /dev/null +++ b/core/src/main/cpp/event_queue.cpp @@ -0,0 +1,103 @@ +#include "event_queue.h" + +static void *handleEventQueue(void *context) { + auto *queue = reinterpret_cast(context); + + while ( !queue->isClosed() ) { + event_t *e = queue->dequeueEvent(); + + EventQueue::Handler h = queue->findHandler(e->type, e->token); + + h(e->type, e->token, e->payload); + + answer_event(e->id); + + free(e); + } + + return nullptr; +} + +EventQueue::EventQueue(): lock(), condition(), closed(false), token(0) { + instance = this; + + pthread_mutex_init(&lock, nullptr); + pthread_cond_init(&condition, nullptr); + + for ( int i = 0 ; i < DEFAULT_EVENT_QUEUE_PROCESSES ; i++ ) { + pthread_t tid = 0; + + if ( pthread_create(&tid, nullptr, &handleEventQueue, this) < 0 ) + abort(); + } +} + +void EventQueue::enqueueEvent(event_t *event) { + pthread_mutex_lock(&lock); + + queue.push_back(event); + + pthread_cond_signal(&condition); + + pthread_mutex_unlock(&lock); +} + +event_t *EventQueue::dequeueEvent() { + pthread_mutex_lock(&lock); + + while ( queue.empty() ) + pthread_cond_wait(&condition, &lock); + + auto *result = queue.back(); + + queue.pop_back(); + + pthread_mutex_unlock(&lock); + + return result; +} + +void EventQueue::registerHandler(event_type_t type, uint64_t token, const EventQueue::Handler& handler) { + pthread_mutex_lock(&lock); + + handlers[type][token] = handler; + + pthread_mutex_unlock(&lock); +} + +void EventQueue::unregisterHandler(event_type_t type, uint64_t token) { + pthread_mutex_lock(&lock); + + handlers[type].erase(token); + + pthread_mutex_unlock(&lock); +} + +EventQueue::Handler EventQueue::findHandler(event_type_t type, uint64_t token) { + pthread_mutex_lock(&lock); + + Handler result = handlers[type][token]; + + pthread_mutex_unlock(&lock); + + if (result == nullptr) + return [](event_type_t type, uint64_t token, const std::string& payload){}; + + return result; +} + +EventQueue *EventQueue::getInstance() { + return instance; +} + +uint64_t EventQueue::obtainToken() { + pthread_mutex_lock(&lock); + + uint64_t r = token++; + + pthread_mutex_unlock(&lock); + + return r; +} + +EventQueue *EventQueue::instance; \ No newline at end of file diff --git a/core/src/main/cpp/event_queue.h b/core/src/main/cpp/event_queue.h new file mode 100644 index 0000000..c0c605b --- /dev/null +++ b/core/src/main/cpp/event_queue.h @@ -0,0 +1,51 @@ +#pragma once + +#include +#include +#include +#include +#include +#include + +#include "event.h" + +#define DEFAULT_EVENT_QUEUE_PROCESSES 8 + +class EventQueue { +public: + typedef std::function Handler; + +public: + EventQueue(); + +public: + void enqueueEvent(event_t *event); + event_t *dequeueEvent(); + +public: + void registerHandler(event_type_t type, uint64_t token, const Handler& handler); + void unregisterHandler(event_type_t type, uint64_t token); + Handler findHandler(event_type_t type, uint64_t token); + +public: + uint64_t obtainToken(); + +public: + static EventQueue *getInstance(); + +public: + inline bool isClosed() { + return closed; + } + +private: + bool closed; + std::vector queue; + std::map> handlers; + pthread_mutex_t lock; + pthread_cond_t condition; + uint64_t token; + +public: + static EventQueue *instance; +}; \ No newline at end of file diff --git a/core/src/main/cpp/main.cpp b/core/src/main/cpp/main.cpp index 0c8d83f..c90333e 100644 --- a/core/src/main/cpp/main.cpp +++ b/core/src/main/cpp/main.cpp @@ -224,6 +224,10 @@ jobject Master::Context::createProxyGroup(char const *name, proxy_type_t type, return env->NewObject(master->cProxyGroup, master->cProxyGroupConstructor, env->NewStringUTF(name), ts, env->NewStringUTF(current), proxies); } +static void enqueue_event(event_t *e) { + EventQueue::getInstance()->enqueueEvent(e); +} + JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) { UNUSED(unused); @@ -233,6 +237,9 @@ JNIEXPORT jint JNICALL JNI_OnLoad(JavaVM *vm, void *unused) { return -1; new Master(vm, env); + new EventQueue(); + + set_event_handler(&enqueue_event); 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 index de7a73c..d668180 100644 --- a/core/src/main/cpp/main.h +++ b/core/src/main/cpp/main.h @@ -3,6 +3,7 @@ #include #include "libclash.h" +#include "event_queue.h" #define UNUSED(v) ((void)v) diff --git a/core/src/main/cpp/tun.cpp b/core/src/main/cpp/tun.cpp index f5652f6..271b171 100644 --- a/core/src/main/cpp/tun.cpp +++ b/core/src/main/cpp/tun.cpp @@ -1,38 +1,6 @@ #include "main.h" -struct tunContext { - jobject callback; -}; - -static void tunContextOnNewSocket(void *context, void *argument) { - auto *ctx = reinterpret_cast(context); - auto fd = reinterpret_cast(argument); - - Master::runWithAttached([&](JNIEnv *env) -> int { - Master::runWithContext(env, [&](Master::Context *context) { - context->tunCallbackNewSocket(ctx->callback, fd); - }); - - return 0; - }); -} - -static void tunContextOnStop(void *context, void *argument) { - UNUSED(argument); - - auto *ctx = reinterpret_cast(context); - - Master::runWithAttached([&](JNIEnv *env) -> int { - Master::runWithContext(env, [&](Master::Context *context) { - context->tunCallbackStop(ctx->callback); - context->removeGlobalReference(ctx->callback); - }); - - return 0; - }); - - delete ctx; -} +#include "event_queue.h" extern "C" JNIEXPORT void JNICALL @@ -48,20 +16,34 @@ Java_com_github_kr328_clash_core_bridge_Bridge_startTunDevice(JNIEnv *env, jclas const char *dnsString = context->getString(dns); jobject callbackGlobal = context->newGlobalReference(callback); - auto *callbackContext = new tunContext(); - callbackContext->callback = callbackGlobal; + EventQueue::getInstance()->registerHandler(NEW_SOCKET, 0, [callbackGlobal](event_type_t, u_int64_t, const std::string& payload) { + Master::runWithAttached([&](JNIEnv *env) -> int { + Master::runWithContext(env, [&](Master::Context *context) { + context->tunCallbackNewSocket(callbackGlobal, std::stoi(payload)); + }); - ccall_t onNewSocket = { - .function = &tunContextOnNewSocket, - .context = callbackContext - }; - ccall_t onStop = { - .function = &tunContextOnStop, - .context = callbackContext - }; + return 0; + }); + }); - char *exception = startTunDevice(fd, mtu, gatewayString, mirrorString, dnsString, onNewSocket, onStop); + EventQueue::getInstance()->registerHandler(TUN_STOP, 0, [callbackGlobal](event_type_t, u_int64_t, const std::string&) { + Master::runWithAttached([&](JNIEnv *env) -> int { + Master::runWithContext(env, [&](Master::Context *context) { + context->tunCallbackStop(callbackGlobal); + context->removeGlobalReference(callbackGlobal); + }); + + return 0; + }); + + auto queue = EventQueue::getInstance(); + + queue->unregisterHandler(NEW_SOCKET, 0); + queue->unregisterHandler(TUN_STOP, 0); + }); + + char *exception = startTunDevice(fd, mtu, gatewayString, mirrorString, dnsString); context->releaseString(gateway, gatewayString); context->releaseString(mirror, mirrorString); @@ -69,9 +51,8 @@ Java_com_github_kr328_clash_core_bridge_Bridge_startTunDevice(JNIEnv *env, jclas if ( exception != nullptr ) { context->throwThrowable(context->newClashException(exception)); - context->removeGlobalReference(callbackContext->callback); + context->removeGlobalReference(callbackGlobal); free(exception); - delete callbackContext; } }); } diff --git a/core/src/main/golang/ccall.c b/core/src/main/golang/ccall.c deleted file mode 100644 index 03663ba..0000000 --- a/core/src/main/golang/ccall.c +++ /dev/null @@ -1,35 +0,0 @@ -#include "ccall.h" - -#include -#include -#include - -extern void nextCcall(ccall_t *call, void **argument, uint64_t *index); -extern void finishCcall(); - -static void *run(void *arg) { - (void) arg; - - while ( 1 ) { - ccall_t call; - void *argument; - uint64_t index; - - nextCcall(&call, &argument, &index); - - call.function(call.context, argument); - - finishCcall(index); - } - - return NULL; -} - -void initialize_ccall(int pool_size) { - for ( int i = 0 ; i < pool_size ; i++ ) { - pthread_t tid = 0; - - 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 deleted file mode 100644 index 3cd39a2..0000000 --- a/core/src/main/golang/ccall.go +++ /dev/null @@ -1,66 +0,0 @@ -package main - -/* -#include "ccall.h" - -extern void initialize_ccall(int pool_size); - */ -import "C" -import ( - "runtime" - "sync" - "unsafe" -) - -type nativeCcall C.ccall_t - -type ccallContext struct { - call nativeCcall - result chan struct{} - argument unsafe.Pointer -} - -var ccallQueue = make(chan *ccallContext) -var ccallMap sync.Map -var currentIndex uint64 -var indexLock sync.Mutex - -func init() { - C.initialize_ccall(C.int(runtime.NumCPU())) -} - -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, index *uint64) { - ctx := <- ccallQueue - - indexLock.Lock() - currentIndex++ - *index = currentIndex - *ccall = ctx.call - *argument = ctx.argument - indexLock.Unlock() - - ccallMap.Store(*index, ctx) -} - -//export finishCcall -func finishCcall(index uint64) { - ctx, ok := ccallMap.Load(index) - if !ok { - return - } - - ctx.(*ccallContext).result <- struct{}{} -} \ No newline at end of file diff --git a/core/src/main/golang/ccall.h b/core/src/main/golang/ccall.h deleted file mode 100644 index 61817a8..0000000 --- a/core/src/main/golang/ccall.h +++ /dev/null @@ -1,18 +0,0 @@ -#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/defer.go b/core/src/main/golang/defer.go index c0cf3e9..25a2e82 100644 --- a/core/src/main/golang/defer.go +++ b/core/src/main/golang/defer.go @@ -8,29 +8,29 @@ import ( "github.com/Dreamacro/clash/tunnel" "github.com/kr328/cfa/config" "sync" - "unsafe" ) //#include "buffer.h" +//#include "event.h" import "C" //export downloadProfileFromFd -func downloadProfileFromFd(fd int, base C.const_string_t, output C.const_string_t, onSuccess nativeCcall, onFailure nativeCcall) { +func downloadProfileFromFd(fd int, base C.const_string_t, output C.const_string_t, callbackId uint64) { b := C.GoString(base) o := C.GoString(output) go func() { err := config.DownloadFd(fd, o, b) if err != nil { - callCcall(onFailure, unsafe.Pointer(C.CString(err.Error()))) + sendEvent(C.COMPLETE, callbackId, err.Error()) } else { - callCcall(onSuccess, nil) + sendEvent(C.COMPLETE, callbackId, "") } }() } //export downloadProfileFromUrl -func downloadProfileFromUrl(url C.const_string_t, base C.const_string_t, output C.const_string_t, onSuccess nativeCcall, onFailure nativeCcall) { +func downloadProfileFromUrl(url C.const_string_t, base C.const_string_t, output C.const_string_t, callbackId uint64) { u := C.GoString(url) b := C.GoString(base) o := C.GoString(output) @@ -38,36 +38,36 @@ func downloadProfileFromUrl(url C.const_string_t, base C.const_string_t, output go func() { err := config.DownloadUrl(u, o, b) if err != nil { - callCcall(onFailure, unsafe.Pointer(C.CString(err.Error()))) + sendEvent(C.COMPLETE, callbackId, err.Error()) } else { - callCcall(onSuccess, nil) + sendEvent(C.COMPLETE, callbackId, "") } }() } //export loadProfile -func loadProfile(path C.const_string_t, base C.const_string_t, onSuccess nativeCcall, onFailure nativeCcall) { +func loadProfile(path C.const_string_t, base C.const_string_t, callbackId uint64) { p := C.GoString(path) b := C.GoString(base) go func() { err := config.LoadFromFile(p, b) if err != nil { - callCcall(onFailure, unsafe.Pointer(C.CString(err.Error()))) + sendEvent(C.COMPLETE, callbackId, err.Error()) } else { - callCcall(onSuccess, nil) + sendEvent(C.COMPLETE, callbackId, "") } }() } //export performHealthCheck -func performHealthCheck(group C.const_string_t, onSuccess nativeCcall, onFailure nativeCcall) { +func performHealthCheck(group C.const_string_t, callbackId uint64) { g := C.GoString(group) go func() { p := tunnel.Proxies()[g] if p == nil { - callCcall(onFailure, unsafe.Pointer(C.CString("No such proxy group"))) + sendEvent(C.COMPLETE, callbackId, "No such proxy group") log.Warnln("Perform health check failure: %s not found", g) @@ -76,7 +76,7 @@ func performHealthCheck(group C.const_string_t, onSuccess nativeCcall, onFailure pw, ok := p.(*outbound.Proxy) if !ok { - callCcall(onFailure, unsafe.Pointer(C.CString("Invalid group"))) + sendEvent(C.COMPLETE, callbackId, "Invalid group") log.Warnln("Perform health check failure: %s not valid group", g) @@ -85,7 +85,7 @@ func performHealthCheck(group C.const_string_t, onSuccess nativeCcall, onFailure adapter, ok := pw.ProxyAdapter.(outboundgroup.ProxyGroup) if !ok { - callCcall(onFailure, unsafe.Pointer(C.CString("Invalid group"))) + sendEvent(C.COMPLETE, callbackId, "Invalid group") log.Warnln("Perform health check failure: %s not valid group", g) @@ -107,6 +107,6 @@ func performHealthCheck(group C.const_string_t, onSuccess nativeCcall, onFailure wg.Wait() - callCcall(onSuccess, nil) + sendEvent(C.COMPLETE, callbackId, "") }() } \ No newline at end of file diff --git a/core/src/main/golang/event.c b/core/src/main/golang/event.c new file mode 100644 index 0000000..975c31e --- /dev/null +++ b/core/src/main/golang/event.c @@ -0,0 +1,28 @@ +#include "event.h" + +#include +#include + +extern void answerEvent(uint64_t id); + +static event_handler_t event_handler; + +void set_event_handler(event_handler_t handler) { + event_handler = handler; +} + +void send_event(event_t *event, const void *payload, size_t payload_length) { + event_handler_t h = event_handler; + if ( h != NULL ) { + memcpy(event->payload, payload, payload_length); + h(event); + } + else { + answer_event(event->id); + free(event); + } +} + +void answer_event(uint64_t id) { + answerEvent(id); +} \ No newline at end of file diff --git a/core/src/main/golang/event.go b/core/src/main/golang/event.go new file mode 100644 index 0000000..884bfc1 --- /dev/null +++ b/core/src/main/golang/event.go @@ -0,0 +1,55 @@ +package main + +//#include "event.h" +import "C" +import ( + "sync" + "unsafe" +) + +type EventWaiter struct { + result chan struct{} +} + +var idLock = sync.Mutex{} +var currentId = uint64(0) +var ids = map[uint64]*EventWaiter{} + +//export answerEvent +func answerEvent(id uint64) { + idLock.Lock() + defer idLock.Unlock() + + waiter, ok := ids[id] + if ok { + close(waiter.result) + delete(ids, id) + } +} + +func sendEvent(t C.event_type_t, token uint64, payload string) *EventWaiter { + idLock.Lock() + defer idLock.Unlock() + + currentId++ + + id := currentId + r := &EventWaiter{make(chan struct{})} + + ids[id] = r + + p := append([]byte(payload), 0) + e := allocCEvent(len(p)) + + e.id = C.uint64_t(id) + e._type = t + e.token = C.uint64_t(token) + + C.send_event(e, unsafe.Pointer(&p[0]), C.size_t(len(p))) + + return r +} + +func allocCEvent(payloadLength int) *C.event_t { + return (*C.event_t)(C.malloc(C.sizeof_event_t + C.size_t(payloadLength))) +} \ No newline at end of file diff --git a/core/src/main/golang/event.h b/core/src/main/golang/event.h new file mode 100644 index 0000000..55a2f3d --- /dev/null +++ b/core/src/main/golang/event.h @@ -0,0 +1,29 @@ +#pragma once + +#include +#include + +#if __cplusplus +extern "C" { +#endif + +typedef enum { + NEW_SOCKET, TUN_STOP, COMPLETE +} event_type_t; + +typedef struct event_t { + uint64_t id; + uint64_t token; + event_type_t type; + char payload[]; +} event_t; + +typedef void (*event_handler_t)(event_t *event); + +void set_event_handler(event_handler_t handler); +void send_event(event_t *event, const void *payload, size_t payload_length); +void answer_event(uint64_t id); + +#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 5e5d784..5a741e1 100644 --- a/core/src/main/golang/tun.go +++ b/core/src/main/golang/tun.go @@ -1,57 +1,50 @@ package main -//#include "buffer.h" -import "C" import ( "github.com/Dreamacro/clash/component/dialer" "github.com/kr328/cfa/tun" "net" + "strconv" "sync" "syscall" - "unsafe" ) -var lock sync.Mutex +//#include "buffer.h" +//#include "event.h" +import "C" + +var tunLock sync.Mutex + +func init() { + c := func(_, _ string, conn syscall.RawConn) error { + return conn.Control(func(fd uintptr) { + <- sendEvent(C.NEW_SOCKET, 0, strconv.Itoa(int(fd))).result + }) + } + + dialer.DialerHook = func(d *net.Dialer) error { + d.Control = c + return nil + } + dialer.ListenConfigHook = func(l *net.ListenConfig) error { + l.Control = c + return nil + } +} //export startTunDevice -func startTunDevice(fd, mtu int, gateway, mirror, dns C.const_string_t, onNewSocket nativeCcall, onStop nativeCcall) *C.char { +func startTunDevice(fd, mtu int, gateway, mirror, dns C.const_string_t) *C.char { stopTunDevice() - lock.Lock() - defer lock.Unlock() + tunLock.Lock() + defer tunLock.Unlock() g := C.GoString(gateway) m := C.GoString(mirror) d := C.GoString(dns) - l := &sync.Mutex{} - c := false - closed := &c - - dialer.DialerHook = func(d *net.Dialer) error { - d.Control = func(network, address string, c syscall.RawConn) error { - return c.Control(func(fd uintptr) { - l.Lock() - defer l.Unlock() - - 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) + sendEvent(C.TUN_STOP, 0, "") }) if err != nil { return C.CString(err.Error()) @@ -62,8 +55,8 @@ func startTunDevice(fd, mtu int, gateway, mirror, dns C.const_string_t, onNewSoc //export stopTunDevice func stopTunDevice() { - lock.Lock() - defer lock.Unlock() + tunLock.Lock() + defer tunLock.Unlock() tun.StopTunDevice() } \ No newline at end of file diff --git a/core/src/main/golang/utils/close.go b/core/src/main/golang/utils/close.go index 1e025ef..1843773 100644 --- a/core/src/main/golang/utils/close.go +++ b/core/src/main/golang/utils/close.go @@ -5,3 +5,4 @@ import "io" func CloseSilent(closer io.Closer) { _ = closer.Close() } +