n2n/android/edge_jni.c
2018-06-12 02:59:13 +08:00

406 lines
13 KiB
C

//
// Created by switchwang(https://github.com/switch-st) on 2018-04-15.
//
#include <jni.h>
#include <pthread.h>
#include "edge_android.h"
static int GetEdgeCmd(JNIEnv *env, jobject jcmd, n2n_edge_cmd_t* cmd);
static void* EdgeRoutine(void* cmd);
static n2n_edge_cmd_t cmd;
static pthread_t tid = -1;
static JavaVM* jvm = NULL;
static jobject jobj_service = NULL;
static jclass jcls_status = NULL;
JNIEXPORT jboolean JNICALL Java_wang_switchy_hin2n_service_N2NService_startEdge(
JNIEnv *env,
jobject this,
jobject jcmd) {
#ifndef NDEBUG
__android_log_write(LOG_DEBUG, "edge_jni", "in start");
#endif /* #ifndef NDEBUG */
if (GetEdgeCmd(env, jcmd, &cmd) != 0) {
goto ERROR;
}
int val = fcntl(cmd.vpn_fd, F_GETFL);
if (val == -1) {
goto ERROR;
}
if ((val & O_NONBLOCK) == O_NONBLOCK) {
val &= ~O_NONBLOCK;
val = fcntl(cmd.vpn_fd, F_SETFL, val);
if (val == -1) {
goto ERROR;
}
}
if (tid != -1) {
stop_edge();
pthread_kill(tid, SIGINT);
pthread_join(tid, NULL);
tid = -1;
if (jcls_status) {
(*env)->DeleteGlobalRef(env, jcls_status);
jcls_status = NULL;
}
if (jobj_service) {
(*env)->DeleteGlobalRef(env, jobj_service);
jobj_service = NULL;
}
jvm = NULL;
pthread_mutex_destroy(&status.mutex);
}
if ((*env)->GetJavaVM(env, &jvm) != JNI_OK) {
goto ERROR;
}
jobj_service = (*env)->NewGlobalRef(env, this);
jclass cls = (*env)->FindClass(env, "wang/switchy/hin2n/model/EdgeStatus");
if (!cls) {
goto ERROR;
}
jcls_status = (*env)->NewGlobalRef(env, cls);
pthread_mutex_init(&status.mutex, NULL);
int ret = pthread_create(&tid, NULL, EdgeRoutine, &cmd);
if (ret != 0) {
tid = -1;
goto ERROR;
}
return JNI_TRUE;
ERROR:
if (jcls_status) {
(*env)->DeleteGlobalRef(env, jcls_status);
jcls_status = NULL;
}
if (jobj_service) {
(*env)->DeleteGlobalRef(env, jobj_service);
jobj_service = NULL;
}
jvm = NULL;
pthread_mutex_destroy(&status.mutex);
free(cmd.enc_key);
free(cmd.enc_key_file);
cmd.enc_key = NULL;
cmd.enc_key_file = NULL;
return JNI_FALSE;
}
JNIEXPORT void JNICALL Java_wang_switchy_hin2n_service_N2NService_stopEdge(
JNIEnv *env,
jobject this) {
#ifndef NDEBUG
__android_log_write(LOG_DEBUG, "edge_jni", "in stop");
#endif /* #ifndef NDEBUG */
stop_edge();
pthread_join(tid, NULL);
tid = -1;
if (jcls_status) {
(*env)->DeleteGlobalRef(env, jcls_status);
jcls_status = NULL;
}
if (jobj_service) {
(*env)->DeleteGlobalRef(env, jobj_service);
jobj_service = NULL;
}
jvm = NULL;
pthread_mutex_destroy(&status.mutex);
}
JNIEXPORT jobject JNICALL Java_wang_switchy_hin2n_service_N2NService_getEdgeStatus(
JNIEnv *env,
jobject this) {
jboolean is_running = JNI_FALSE;
if (tid != -1) {
is_running = pthread_kill(tid, 0) ? JNI_FALSE : JNI_TRUE;
pthread_mutex_lock(&status.mutex);
is_running = is_running && status.is_running;
pthread_mutex_unlock(&status.mutex);
}
jclass cls = (*env)->FindClass(env, "wang/switchy/hin2n/model/EdgeStatus");
jobject jStatus = (*env)->NewObject(env, cls, (*env)->GetMethodID(env, cls, "<init>", "()V"));
if (!jStatus) {
return NULL;
}
(*env)->SetBooleanField(env, jStatus, (*env)->GetFieldID(env, cls, "isRunning", "Z"), is_running);
return jStatus;
}
/////////////////////////////////////////////////////////////////////////
#ifndef JNI_CHECKNULL
#define JNI_CHECKNULL(p) do { if (!(p)) return 1;}while(0)
#endif /* JNI_CHECKNULL */
int GetEdgeCmd(JNIEnv *env, jobject jcmd, n2n_edge_cmd_t* cmd)
{
jclass cls;
int i, j;
INIT_EDGE_CMD(*cmd);
cls = (*env)->GetObjectClass(env, jcmd);
JNI_CHECKNULL(cls);
// ipAddr
{
jstring jsIpAddr = (*env)->GetObjectField(env, jcmd, (*env)->GetFieldID(env, cls, "ipAddr", "Ljava/lang/String;"));
JNI_CHECKNULL(jsIpAddr);
const char* ipAddr = (*env)->GetStringUTFChars(env, jsIpAddr, NULL);
if (!ipAddr || strlen(ipAddr) == 0) {
(*env)->ReleaseStringUTFChars(env, jsIpAddr, ipAddr);
return 1;
}
strncpy(cmd->ip_addr, ipAddr, EDGE_CMD_IPSTR_SIZE);
(*env)->ReleaseStringUTFChars(env, jsIpAddr, ipAddr);
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "ipAddr = %s", cmd->ip_addr);
#endif /* #ifndef NDEBUG */
}
// ipNetmask
{
jstring jsIpNetmask = (*env)->GetObjectField(env, jcmd, (*env)->GetFieldID(env, cls, "ipNetmask", "Ljava/lang/String;"));
JNI_CHECKNULL(jsIpNetmask);
const char* ipNetmask = (*env)->GetStringUTFChars(env, jsIpNetmask, NULL);
if (!ipNetmask || strlen(ipNetmask) == 0) {
(*env)->ReleaseStringUTFChars(env, jsIpNetmask, ipNetmask);
return 1;
}
strncpy(cmd->ip_netmask, ipNetmask, EDGE_CMD_IPSTR_SIZE);
(*env)->ReleaseStringUTFChars(env, jsIpNetmask, ipNetmask);
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "ipNetmask = %s", cmd->ip_netmask);
#endif /* #ifndef NDEBUG */
}
// supernodes
{
jarray jaSupernodes = (*env)->GetObjectField(env, jcmd, (*env)->GetFieldID(env, cls, "supernodes", "[Ljava/lang/String;"));
JNI_CHECKNULL(jaSupernodes);
int len = (*env)->GetArrayLength(env, jaSupernodes);
if (len <= 0) {
return 1;
}
for (i = 0, j = 0; i < len && i < EDGE_CMD_SUPERNODES_NUM; ++i) {
const jobject jsNode = (*env)->GetObjectArrayElement(env, jaSupernodes, i);
if (!jsNode) {
continue;
}
const char* node = (*env)->GetStringUTFChars(env, jsNode, NULL);
if (!node || strlen(node) == 0) {
(*env)->ReleaseStringUTFChars(env, jsNode, node);
continue;
}
strncpy(cmd->supernodes[j], node, EDGE_CMD_SN_HOST_SIZE);
(*env)->ReleaseStringUTFChars(env, jsNode, node);
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "supernodes = %s", cmd->supernodes[j]);
#endif /* #ifndef NDEBUG */
j++;
}
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "j = %d", j);
#endif /* #ifndef NDEBUG */
if (j == 0) {
return 1;
}
}
// community
{
jstring jsCommunity = (*env)->GetObjectField(env, jcmd, (*env)->GetFieldID(env, cls, "community", "Ljava/lang/String;"));
JNI_CHECKNULL(jsCommunity);
const char* community = (*env)->GetStringUTFChars(env, jsCommunity, NULL);
if (!community || strlen(community) == 0) {
(*env)->ReleaseStringUTFChars(env, jsCommunity, community);
return 1;
}
strncpy(cmd->community, community, N2N_COMMUNITY_SIZE);
(*env)->ReleaseStringUTFChars(env, jsCommunity, community);
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "community = %s", cmd->community);
#endif /* #ifndef NDEBUG */
}
// encKey
{
jstring jsEncKey = (*env)->GetObjectField(env, jcmd, (*env)->GetFieldID(env, cls, "encKey", "Ljava/lang/String;"));
if (jsEncKey) {
const char* encKey = (*env)->GetStringUTFChars(env, jsEncKey, NULL);
if (encKey && strlen(encKey) != 0) {
cmd->enc_key = strdup(encKey);
}
(*env)->ReleaseStringUTFChars(env, jsEncKey, encKey);
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "encKey = %s", cmd->enc_key);
#endif /* #ifndef NDEBUG */
}
}
// encKeyFile
{
jstring jsEncKeyFile = (*env)->GetObjectField(env, jcmd, (*env)->GetFieldID(env, cls, "encKeyFile", "Ljava/lang/String;"));
if (jsEncKeyFile) {
const char* encKeyFile = (*env)->GetStringUTFChars(env, jsEncKeyFile, NULL);
if (encKeyFile && strlen(encKeyFile) != 0) {
cmd->enc_key_file = strdup(encKeyFile);
}
(*env)->ReleaseStringUTFChars(env, jsEncKeyFile, encKeyFile);
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "encKeyFile = %s", cmd->enc_key_file);
#endif /* #ifndef NDEBUG */
}
}
// macAddr
{
jstring jsMacAddr = (*env)->GetObjectField(env, jcmd, (*env)->GetFieldID(env, cls, "macAddr", "Ljava/lang/String;"));
JNI_CHECKNULL(jsMacAddr);
const char* macAddr = (*env)->GetStringUTFChars(env, jsMacAddr, NULL);
if (macAddr && strlen(macAddr) != 0) {
strncpy(cmd->mac_addr, macAddr, EDGE_CMD_MACNAMSIZ);
}
(*env)->ReleaseStringUTFChars(env, jsMacAddr, macAddr);
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "macAddr = %s", cmd->mac_addr);
#endif /* #ifndef NDEBUG */
}
// mtu
{
jint jiMtu = (*env)->GetIntField(env, jcmd, (*env)->GetFieldID(env, cls, "mtu", "I"));
if (jiMtu <= 0) {
return 1;
}
cmd->mtu = jiMtu;
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "mtu = %d", cmd->mtu);
#endif /* #ifndef NDEBUG */
}
// reResoveSupernodeIP
{
jboolean jbReResoveSupernodeIP= (*env)->GetBooleanField(env, jcmd, (*env)->GetFieldID(env, cls, "reResoveSupernodeIP", "Z"));
cmd->re_resolve_supernode_ip = jbReResoveSupernodeIP ? 1 : 0;
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "reResoveSupernodeIP = %d", cmd->re_resolve_supernode_ip);
#endif /* #ifndef NDEBUG */
}
// localPort
{
jint jiLocalPort = (*env)->GetIntField(env, jcmd, (*env)->GetFieldID(env, cls, "localPort", "I"));
if (jiLocalPort < 0) {
return 1;
}
cmd->local_port = jiLocalPort;
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "localPort = %d", cmd->local_port);
#endif /* #ifndef NDEBUG */
}
// allowRouting
{
jboolean jbAllowRouting= (*env)->GetBooleanField(env, jcmd, (*env)->GetFieldID(env, cls, "allowRouting", "Z"));
cmd->allow_routing = jbAllowRouting ? 1 : 0;
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "allowRouting = %d", cmd->allow_routing);
#endif /* #ifndef NDEBUG */
}
// dropMuticast
{
jboolean jbDropMuticast= (*env)->GetBooleanField(env, jcmd, (*env)->GetFieldID(env, cls, "dropMuticast", "Z"));
cmd->drop_multicast = jbDropMuticast ? 1 : 0;
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "dropMuticast = %d", cmd->drop_multicast);
#endif /* #ifndef NDEBUG */
}
// traceLevel
{
jint jiTraceLevel = (*env)->GetIntField(env, jcmd, (*env)->GetFieldID(env, cls, "traceLevel", "I"));
cmd->trace_vlevel = jiTraceLevel;
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "traceLevel = %d", cmd->trace_vlevel);
#endif /* #ifndef NDEBUG */
}
// vpnFd
{
jint jiVpnFd = (*env)->GetIntField(env, jcmd, (*env)->GetFieldID(env, cls, "vpnFd", "I"));
if (jiVpnFd < 0) {
return 1;
}
cmd->vpn_fd = jiVpnFd;
#ifndef NDEBUG
__android_log_print(LOG_DEBUG, "edge_jni", "vpnFd = %d", cmd->vpn_fd);
#endif /* #ifndef NDEBUG */
}
return 0;
}
void* EdgeRoutine(void* cmd)
{
int flag = 0;
if (jvm) {
JNIEnv* env = NULL;
if ((*jvm)->AttachCurrentThread(jvm, &env, NULL) == JNI_OK) {
flag = 1;
}
}
n2n_edge_cmd_t* c = cmd;
int ret = start_edge(c);
free(c->enc_key);
free(c->enc_key_file);
c->enc_key = NULL;
c->enc_key_file = NULL;
if (flag && jvm) {
(*jvm)->DetachCurrentThread(jvm);
}
return (void*)ret;
}
void report_edge_status(void)
{
if (!jvm || !jobj_service || !jcls_status || tid == -1) {
return;
}
jboolean is_running = JNI_FALSE;
pthread_mutex_lock(&status.mutex);
is_running = status.is_running;
pthread_mutex_unlock(&status.mutex);
JNIEnv* env = NULL;
if ((*jvm)->GetEnv(jvm, &env, JNI_VERSION_1_1) != JNI_OK || !env) {
return;
}
jmethodID mid = (*env)->GetMethodID(env, jcls_status, "<init>", "()V");
if (!mid) {
return;
}
jobject jStatus = (*env)->NewObject(env, jcls_status, mid);
if (!jStatus) {
return;
}
jfieldID fid = (*env)->GetFieldID(env, jcls_status, "isRunning", "Z");
if (!fid) {
return;
}
(*env)->SetBooleanField(env, jStatus, fid, is_running);
jclass cls = (*env)->GetObjectClass(env, jobj_service);
if (!cls) {
return;
}
mid = (*env)->GetMethodID(env, cls, "reportEdgeStatus", "(Lwang/switchy/hin2n/model/EdgeStatus;)V");
if (!mid) {
return;
}
(*env)->CallVoidMethod(env, jobj_service, mid, jStatus);
(*env)->DeleteLocalRef(env, jStatus);
}