refactor clash bridge

This commit is contained in:
Kr328 committed 2020-06-10 21:24:38 +08:00
1 parent f6263940f1
commit 123bf65c55
18 files changed
+465 -202

No files matched your search

+3 -1
View File
@@ -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"
)
}
+3 -2
View File
@@ -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)
-159
View File
@@ -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;
}
+30
View File
@@ -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();
}
+113
View File
@@ -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;
}
+77
View File
@@ -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);
}
+2
View File
@@ -0,0 +1,2 @@
#include "main.h"
+62
View File
@@ -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);
});
}
+9 -1
View File
@@ -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
+31
View File
@@ -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();
}
+57
View File
@@ -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{}{}
}
}
+18
View File
@@ -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
+9 -1
View File
@@ -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
+10 -1
View File
@@ -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
+1 -1
View File
@@ -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)
+9 -1
View File
@@ -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
View File
@@ -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
}
+3 -1
View File
@@ -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 {