refactor clash bridge
This commit is contained in:
17 files changed
+432
-266
No files matched your search
@@ -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)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
add_library(bridge SHARED main.cpp main.h init.cpp query.cpp patch.cpp tun.cpp defer.cpp event_queue.cpp)
|
||||
+84
-48
@@ -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<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(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<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(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<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(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<jobject>(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<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(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;
|
||||
});
|
||||
}
|
||||
@@ -0,0 +1,103 @@
|
||||
#include "event_queue.h"
|
||||
|
||||
static void *handleEventQueue(void *context) {
|
||||
auto *queue = reinterpret_cast<EventQueue*>(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;
|
||||
@@ -0,0 +1,51 @@
|
||||
#pragma once
|
||||
|
||||
#include <pthread.h>
|
||||
#include <vector>
|
||||
#include <map>
|
||||
#include <functional>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
|
||||
#include "event.h"
|
||||
|
||||
#define DEFAULT_EVENT_QUEUE_PROCESSES 8
|
||||
|
||||
class EventQueue {
|
||||
public:
|
||||
typedef std::function<void (event_type_t type, uint64_t token, std::string payload)> 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<event_t*> queue;
|
||||
std::map<event_type_t, std::map<uint64_t, Handler>> handlers;
|
||||
pthread_mutex_t lock;
|
||||
pthread_cond_t condition;
|
||||
uint64_t token;
|
||||
|
||||
public:
|
||||
static EventQueue *instance;
|
||||
};
|
||||
@@ -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;
|
||||
}
|
||||
@@ -3,6 +3,7 @@
|
||||
#include <functional>
|
||||
|
||||
#include "libclash.h"
|
||||
#include "event_queue.h"
|
||||
|
||||
#define UNUSED(v) ((void)v)
|
||||
|
||||
|
||||
+27
-46
@@ -1,38 +1,6 @@
|
||||
#include "main.h"
|
||||
|
||||
struct tunContext {
|
||||
jobject callback;
|
||||
};
|
||||
|
||||
static void tunContextOnNewSocket(void *context, void *argument) {
|
||||
auto *ctx = reinterpret_cast<tunContext*>(context);
|
||||
auto fd = reinterpret_cast<long>(argument);
|
||||
|
||||
Master::runWithAttached<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(env, [&](Master::Context *context) {
|
||||
context->tunCallbackNewSocket(ctx->callback, fd);
|
||||
});
|
||||
|
||||
return 0;
|
||||
});
|
||||
}
|
||||
|
||||
static void tunContextOnStop(void *context, void *argument) {
|
||||
UNUSED(argument);
|
||||
|
||||
auto *ctx = reinterpret_cast<tunContext*>(context);
|
||||
|
||||
Master::runWithAttached<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(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<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(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<int>([&](JNIEnv *env) -> int {
|
||||
Master::runWithContext<void>(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;
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
@@ -1,35 +0,0 @@
|
||||
#include "ccall.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
@@ -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{}{}
|
||||
}
|
||||
@@ -1,18 +0,0 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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
|
||||
@@ -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, "")
|
||||
}()
|
||||
}
|
||||
@@ -0,0 +1,28 @@
|
||||
#include "event.h"
|
||||
|
||||
#include <malloc.h>
|
||||
#include <string.h>
|
||||
|
||||
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);
|
||||
}
|
||||
@@ -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)))
|
||||
}
|
||||
@@ -0,0 +1,29 @@
|
||||
#pragma once
|
||||
|
||||
#include <stdint.h>
|
||||
#include <stddef.h>
|
||||
|
||||
#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
|
||||
+29
-36
@@ -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()
|
||||
}
|
||||
@@ -5,3 +5,4 @@ import "io"
|
||||
func CloseSilent(closer io.Closer) {
|
||||
_ = closer.Close()
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user