You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
184 lines
4.5 KiB
184 lines
4.5 KiB
#define LOG_TAG "Shadowsocks"
|
|
|
|
#include <android/log.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <unistd.h>
|
|
|
|
#include "android.h"
|
|
|
|
#define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0)
|
|
#define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0)
|
|
#define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0)
|
|
|
|
/*
|
|
* This is called by the VM when the shared library is first loaded.
|
|
*/
|
|
|
|
typedef union {
|
|
JNIEnv* env;
|
|
void* venv;
|
|
} UnionJNIEnvToVoid;
|
|
|
|
typedef unsigned short char16_t;
|
|
|
|
class String8 {
|
|
public:
|
|
String8() {
|
|
mString = 0;
|
|
}
|
|
|
|
~String8() {
|
|
if (mString) {
|
|
free(mString);
|
|
}
|
|
}
|
|
|
|
void set(const char16_t* o, size_t numChars) {
|
|
if (mString) {
|
|
free(mString);
|
|
}
|
|
mString = (char*) malloc(numChars + 1);
|
|
if (!mString) {
|
|
return;
|
|
}
|
|
for (size_t i = 0; i < numChars; i++) {
|
|
mString[i] = (char) o[i];
|
|
}
|
|
mString[numChars] = '\0';
|
|
}
|
|
|
|
const char* string() {
|
|
return mString;
|
|
}
|
|
private:
|
|
char* mString;
|
|
};
|
|
|
|
static UnionJNIEnvToVoid uenv;
|
|
static jmethodID newProtectedSocketMethod = NULL;
|
|
static jmethodID freeProtectedSocketMethod = NULL;
|
|
static jclass vpnClazz = NULL;
|
|
static jclass daemonClazz = NULL;
|
|
|
|
|
|
int main (int argc, char **argv);
|
|
|
|
jint Java_com_github_shadowsocks_daemon_exec(JNIEnv *env, jobject thiz, jobjectArray argv) {
|
|
|
|
int argc = argv ? env->GetArrayLength(argv) : 0;
|
|
char **daemon_argv = NULL;
|
|
String8 tmp_8;
|
|
|
|
if (argc > 0) {
|
|
daemon_argv = (char **)malloc((argc+1)*sizeof(char *));
|
|
for (int i = 0; i < argc; ++i) {
|
|
jstring arg = reinterpret_cast<jstring>(env->GetObjectArrayElement(argv, i));
|
|
const jchar *str = env->GetStringCritical(arg, 0);
|
|
tmp_8.set(str, env->GetStringLength(arg));
|
|
env->ReleaseStringCritical(arg, str);
|
|
daemon_argv[i] = strdup(tmp_8.string());
|
|
}
|
|
daemon_argv[argc] = NULL;
|
|
}
|
|
|
|
int ret = main(argc, daemon_argv);
|
|
|
|
for (int i = 0; i < argc; i++) free(daemon_argv[i]);
|
|
free(daemon_argv);
|
|
|
|
return ret;
|
|
}
|
|
|
|
/*
|
|
* Register several native methods for one class.
|
|
*/
|
|
static int registerNativeMethods(JNIEnv* env)
|
|
{
|
|
const char *daemonClassPathName = "com/github/shadowsocks/Daemon";
|
|
const char *vpnClassPathName = "com/github/shadowsocks/ShadowsocksVpnService";
|
|
|
|
vpnClazz = env->FindClass(vpnClassPathName);
|
|
|
|
if (vpnClazz == NULL)
|
|
{
|
|
LOGE("Native registration unable to find class '%s'", vpnClassPathName);
|
|
return JNI_FALSE;
|
|
}
|
|
newProtectedSocketMethod = env->GetStaticMethodID(vpnClazz, "newProtectedSocket", "()I");
|
|
if (newProtectedSocketMethod < 0)
|
|
{
|
|
LOGE("RegisterNatives failed for newProtectedSocketMethod");
|
|
return JNI_FALSE;
|
|
}
|
|
freeProtectedSocketMethod = env->GetStaticMethodID(vpnClazz, "freeProtectedSocket", "(I)V");
|
|
if (freeProtectedSocketMethod < 0)
|
|
{
|
|
LOGE("RegisterNatives failed for freeProtectedSocketMethod");
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
daemonClazz = env->FindClass(daemonClassPathName);
|
|
|
|
if (daemonClazz == NULL)
|
|
{
|
|
LOGE("Native registration unable to find class '%s'", daemonClassPathName);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
JNINativeMethod methods[] = {
|
|
{ "exec", "([Ljava/lang/String;)I",
|
|
(void*) Java_com_github_shadowsocks_daemon_exec }
|
|
};
|
|
|
|
if (env->RegisterNatives(daemonClazz, methods, 1) < 0) {
|
|
LOGE("RegisterNatives failed for '%s'", daemonClassPathName);
|
|
return JNI_FALSE;
|
|
}
|
|
|
|
return JNI_TRUE;
|
|
}
|
|
|
|
jint new_protected_socket()
|
|
{
|
|
if (newProtectedSocketMethod != NULL)
|
|
{
|
|
JNIEnv* env = uenv.env;
|
|
return env->CallStaticIntMethod(vpnClazz, newProtectedSocketMethod);
|
|
}
|
|
return -1;
|
|
}
|
|
|
|
void free_protected_socket(jint fd)
|
|
{
|
|
if (newProtectedSocketMethod != NULL)
|
|
{
|
|
JNIEnv* env = uenv.env;
|
|
env->CallStaticVoidMethod(vpnClazz, freeProtectedSocketMethod, fd);
|
|
}
|
|
}
|
|
|
|
jint JNI_OnLoad(JavaVM* vm, void* reserved)
|
|
{
|
|
uenv.venv = NULL;
|
|
jint result = -1;
|
|
JNIEnv* env = NULL;
|
|
|
|
LOGI("JNI_OnLoad");
|
|
|
|
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
|
|
LOGE("ERROR: GetEnv failed");
|
|
goto bail;
|
|
}
|
|
env = uenv.env;
|
|
|
|
if (registerNativeMethods(env) != JNI_TRUE) {
|
|
LOGE("ERROR: registerNatives failed");
|
|
goto bail;
|
|
}
|
|
|
|
result = JNI_VERSION_1_4;
|
|
|
|
bail:
|
|
return result;
|
|
}
|