refactor clash bridge
This commit is contained in:
18 files changed
+465
-202
No files matched your search
@@ -93,7 +93,9 @@ fun generateGolangBuildEnvironment(abi: String): Map<String, String> {
|
||||
"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"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -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)
|
||||
add_library(bridge SHARED main.cpp main.h init.cpp query.cpp patch.cpp)
|
||||
@@ -1,159 +0,0 @@
|
||||
#include <jni.h>
|
||||
#include <stddef.h>
|
||||
#include <string.h>
|
||||
#include <malloc.h>
|
||||
|
||||
#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, "<init>", "(JJ)V");
|
||||
cGeneralConstructor = (*env)->GetMethodID(env, cGeneral, "<init>", "(Ljava/lang/String;IIII)V");
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
@@ -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<void>(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();
|
||||
}
|
||||
@@ -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, "<init>",
|
||||
"(Ljava/lang/String;)V");
|
||||
cTrafficConstructor = env->GetMethodID(cTraffic, "<init>", "(JJ)V");
|
||||
cGeneralConstructor = env->GetMethodID(cGeneral, "<init>", "(Ljava/lang/String;IIII)V");
|
||||
cCompletableFutureConstructor = env->GetMethodID(cCompletableFuture, "<init>", "()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<jthrowable>(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<jbyte*>(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<jbyte*>(reinterpret_cast<const jbyte*>(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<void**>(&env), JNI_VERSION_1_6) != JNI_OK)
|
||||
return -1;
|
||||
|
||||
new Master(env);
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
@@ -0,0 +1,77 @@
|
||||
#include <jni.h>
|
||||
#include <string>
|
||||
#include <functional>
|
||||
|
||||
#include "libclash.h"
|
||||
|
||||
#define UNUSED(v) ((void)v)
|
||||
|
||||
class Master {
|
||||
public:
|
||||
class Context;
|
||||
|
||||
public:
|
||||
Master(JNIEnv *env);
|
||||
|
||||
public:
|
||||
template <class R>
|
||||
static R runWithContext(JNIEnv *env, const std::function<R (Context *)>& 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 <class R>
|
||||
R Master::runWithContext(JNIEnv *env, const std::function<R (Master::Context *)>& func) {
|
||||
Master::Context context(env);
|
||||
|
||||
return func(&context);
|
||||
}
|
||||
@@ -0,0 +1,2 @@
|
||||
#include "main.h"
|
||||
|
||||
@@ -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<jobject>(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<jobject>(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<jobject>(env, [&](Master::Context *context) -> jobject {
|
||||
traffic_t traffic;
|
||||
|
||||
querySpeed(&traffic);
|
||||
|
||||
return context->newTraffic(traffic.upload, traffic.download);
|
||||
});
|
||||
}
|
||||
@@ -2,6 +2,10 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#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;
|
||||
typedef const char *const_string_t;
|
||||
|
||||
#if __cplusplus
|
||||
};
|
||||
#endif
|
||||
@@ -0,0 +1,31 @@
|
||||
#include "ccall.h"
|
||||
|
||||
#include <pthread.h>
|
||||
#include <stdlib.h>
|
||||
|
||||
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();
|
||||
}
|
||||
@@ -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{}{}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,18 @@
|
||||
#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
|
||||
@@ -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;
|
||||
} dns_override_t;
|
||||
|
||||
#if __cplusplus
|
||||
};
|
||||
#endif
|
||||
@@ -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;
|
||||
} general_t;
|
||||
|
||||
#if __cplusplus
|
||||
};
|
||||
#endif
|
||||
@@ -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)
|
||||
|
||||
@@ -2,7 +2,15 @@
|
||||
|
||||
#include <stdint.h>
|
||||
|
||||
#if __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
typedef struct traffic_t {
|
||||
uint64_t upload;
|
||||
uint64_t download;
|
||||
} traffic_t;
|
||||
} traffic_t;
|
||||
|
||||
#if __cplusplus
|
||||
};
|
||||
#endif
|
||||
+28
-34
@@ -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
|
||||
}
|
||||
@@ -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 {
|
||||
|
||||
Reference in new issue
Block a user