2016-01-18 21:22:54 +00:00
|
|
|
/*
|
|
|
|
This file is part of NetGuard.
|
|
|
|
|
|
|
|
NetGuard is free software: you can redistribute it and/or modify
|
|
|
|
it under the terms of the GNU General Public License as published by
|
|
|
|
the Free Software Foundation, either version 3 of the License, or
|
|
|
|
(at your option) any later version.
|
|
|
|
|
|
|
|
NetGuard is distributed in the hope that it will be useful,
|
|
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
GNU General Public License for more details.
|
|
|
|
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
|
|
along with NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
|
|
|
|
|
|
|
Copyright 2015-2016 by Marcel Bokhorst (M66B)
|
|
|
|
*/
|
|
|
|
|
2016-01-17 16:41:54 +00:00
|
|
|
#include "netguard.h"
|
|
|
|
|
2016-01-12 20:15:25 +00:00
|
|
|
// TODO TCP fragmentation
|
2016-01-16 08:07:04 +00:00
|
|
|
// It is assumed that no packets will get lost and that packets arrive in order
|
2016-02-11 07:20:45 +00:00
|
|
|
// https://android.googlesource.com/platform/frameworks/base.git/+/master/services/core/jni/com_android_server_connectivity_Vpn.cpp
|
2016-01-19 19:58:51 +00:00
|
|
|
|
2016-01-12 20:15:25 +00:00
|
|
|
// Global variables
|
2016-01-12 12:15:21 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
JavaVM *jvm = NULL;
|
|
|
|
pthread_t thread_id = 0;
|
|
|
|
pthread_mutex_t lock;
|
|
|
|
jboolean stopping = 0;
|
|
|
|
jboolean signaled = 0;
|
|
|
|
int loglevel = ANDROID_LOG_WARN;
|
2016-01-11 22:06:35 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
extern int max_tun_msg;
|
|
|
|
extern FILE *pcap_file;
|
2016-02-05 11:03:44 +00:00
|
|
|
|
2016-01-13 08:23:21 +00:00
|
|
|
// JNI
|
2016-01-11 22:06:35 +00:00
|
|
|
|
2016-01-22 09:37:57 +00:00
|
|
|
jclass clsPacket;
|
2016-02-07 16:44:23 +00:00
|
|
|
jclass clsAllowed;
|
2016-01-30 08:51:41 +00:00
|
|
|
jclass clsRR;
|
2016-01-22 09:37:57 +00:00
|
|
|
|
|
|
|
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
|
|
|
log_android(ANDROID_LOG_INFO, "JNI load");
|
|
|
|
|
|
|
|
JNIEnv *env;
|
|
|
|
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK) {
|
|
|
|
log_android(ANDROID_LOG_INFO, "JNI load GetEnv failed");
|
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
|
|
|
const char *packet = "eu/faircode/netguard/Packet";
|
|
|
|
clsPacket = jniGlobalRef(env, jniFindClass(env, packet));
|
2016-02-07 16:44:23 +00:00
|
|
|
|
|
|
|
const char *allowed = "eu/faircode/netguard/Allowed";
|
|
|
|
clsAllowed = jniGlobalRef(env, jniFindClass(env, allowed));
|
|
|
|
|
2016-01-30 08:51:41 +00:00
|
|
|
const char *rr = "eu/faircode/netguard/ResourceRecord";
|
|
|
|
clsRR = jniGlobalRef(env, jniFindClass(env, rr));
|
2016-01-22 09:37:57 +00:00
|
|
|
|
2016-02-11 07:48:14 +00:00
|
|
|
// Raise file number limit to maximum
|
|
|
|
struct rlimit rlim;
|
|
|
|
if (getrlimit(RLIMIT_NOFILE, &rlim))
|
|
|
|
log_android(ANDROID_LOG_WARN, "getrlimit error %d: %s", errno, strerror(errno));
|
|
|
|
else {
|
|
|
|
rlim_t soft = rlim.rlim_cur;
|
|
|
|
rlim.rlim_cur = rlim.rlim_max;
|
|
|
|
if (setrlimit(RLIMIT_NOFILE, &rlim))
|
|
|
|
log_android(ANDROID_LOG_WARN, "setrlimit error %d: %s", errno, strerror(errno));
|
|
|
|
else
|
|
|
|
log_android(ANDROID_LOG_WARN, "raised file limit from %d to %d", soft, rlim.rlim_cur);
|
|
|
|
}
|
|
|
|
|
2016-01-22 09:37:57 +00:00
|
|
|
return JNI_VERSION_1_6;
|
|
|
|
}
|
|
|
|
|
|
|
|
void JNI_OnUnload(JavaVM *vm, void *reserved) {
|
|
|
|
log_android(ANDROID_LOG_INFO, "JNI unload");
|
|
|
|
|
|
|
|
JNIEnv *env;
|
|
|
|
if ((*vm)->GetEnv(vm, (void **) &env, JNI_VERSION_1_6) != JNI_OK)
|
|
|
|
log_android(ANDROID_LOG_INFO, "JNI load GetEnv failed");
|
|
|
|
else {
|
|
|
|
(*env)->DeleteGlobalRef(env, clsPacket);
|
2016-01-30 08:51:41 +00:00
|
|
|
(*env)->DeleteGlobalRef(env, clsRR);
|
2016-01-22 09:37:57 +00:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2016-02-06 11:25:20 +00:00
|
|
|
// JNI SinkholeService
|
|
|
|
|
2016-01-14 14:02:32 +00:00
|
|
|
JNIEXPORT void JNICALL
|
2016-02-10 18:28:40 +00:00
|
|
|
Java_eu_faircode_netguard_SinkholeService_jni_1init(JNIEnv *env, jobject instance) {
|
2016-01-22 12:06:50 +00:00
|
|
|
loglevel = ANDROID_LOG_WARN;
|
2016-02-10 18:28:40 +00:00
|
|
|
|
|
|
|
struct arguments args;
|
|
|
|
args.env = env;
|
|
|
|
args.instance = instance;
|
|
|
|
init_icmp(&args);
|
|
|
|
init_udp(&args);
|
|
|
|
init_tcp(&args);
|
|
|
|
|
2016-01-22 12:06:50 +00:00
|
|
|
pcap_file = NULL;
|
2016-01-24 21:46:25 +00:00
|
|
|
|
|
|
|
if (pthread_mutex_init(&lock, NULL))
|
|
|
|
log_android(ANDROID_LOG_ERROR, "pthread_mutex_init failed");
|
2016-01-17 09:42:21 +00:00
|
|
|
}
|
|
|
|
|
2016-01-09 11:10:11 +00:00
|
|
|
JNIEXPORT void JNICALL
|
2016-02-06 11:25:20 +00:00
|
|
|
Java_eu_faircode_netguard_SinkholeService_jni_1start(
|
2016-02-08 13:33:33 +00:00
|
|
|
JNIEnv *env, jobject instance, jint tun, jboolean fwd53, jint loglevel_) {
|
2016-01-19 19:58:51 +00:00
|
|
|
|
2016-01-28 11:20:52 +00:00
|
|
|
loglevel = loglevel_;
|
2016-02-05 13:52:52 +00:00
|
|
|
max_tun_msg = 0;
|
2016-02-08 15:34:54 +00:00
|
|
|
log_android(ANDROID_LOG_WARN,
|
|
|
|
"Starting tun %d fwd53 %d level %d thread %x",
|
|
|
|
tun, fwd53, loglevel, thread_id);
|
2016-01-18 18:37:52 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
// Set blocking
|
|
|
|
int flags = fcntl(tun, F_GETFL, 0);
|
|
|
|
if (flags < 0 || fcntl(tun, F_SETFL, flags & ~O_NONBLOCK) < 0)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "fcntl tun ~O_NONBLOCK error %d: %s",
|
|
|
|
errno, strerror(errno));
|
2016-01-13 09:26:06 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
if (thread_id && pthread_kill(thread_id, 0) == 0)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "Already running thread %x", thread_id);
|
|
|
|
else {
|
|
|
|
jint rs = (*env)->GetJavaVM(env, &jvm);
|
|
|
|
if (rs != JNI_OK)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "GetJavaVM failed");
|
2016-01-13 09:26:06 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
// Get arguments
|
|
|
|
struct arguments *args = malloc(sizeof(struct arguments));
|
|
|
|
// args->env = will be set in thread
|
|
|
|
args->instance = (*env)->NewGlobalRef(env, instance);
|
|
|
|
args->tun = tun;
|
|
|
|
args->fwd53 = fwd53;
|
2016-01-12 12:15:21 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
// Start native thread
|
|
|
|
int err = pthread_create(&thread_id, NULL, handle_events, (void *) args);
|
|
|
|
if (err == 0)
|
|
|
|
log_android(ANDROID_LOG_WARN, "Started thread %x", thread_id);
|
|
|
|
else
|
|
|
|
log_android(ANDROID_LOG_ERROR, "pthread_create error %d: %s", err, strerror(err));
|
2016-01-13 10:11:11 +00:00
|
|
|
}
|
2016-02-09 12:39:49 +00:00
|
|
|
}
|
2016-01-13 10:11:11 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
JNIEXPORT void JNICALL
|
|
|
|
Java_eu_faircode_netguard_SinkholeService_jni_1stop(
|
|
|
|
JNIEnv *env, jobject instance, jint tun, jboolean clear) {
|
|
|
|
pthread_t t = thread_id;
|
|
|
|
log_android(ANDROID_LOG_WARN, "Stop tun %d clear %d thread %x", tun, (int) clear, t);
|
|
|
|
if (t && pthread_kill(t, 0) == 0) {
|
|
|
|
stopping = 1;
|
|
|
|
log_android(ANDROID_LOG_WARN, "Kill thread %x", t);
|
|
|
|
int err = pthread_kill(t, SIGUSR1);
|
|
|
|
if (err != 0)
|
|
|
|
log_android(ANDROID_LOG_WARN, "pthread_kill error %d: %s", err, strerror(err));
|
2016-02-06 11:25:20 +00:00
|
|
|
else {
|
2016-02-09 12:39:49 +00:00
|
|
|
log_android(ANDROID_LOG_WARN, "Join thread %x", t);
|
|
|
|
err = pthread_join(t, NULL);
|
|
|
|
if (err != 0)
|
|
|
|
log_android(ANDROID_LOG_WARN, "pthread_join error %d: %s", err, strerror(err));
|
2016-02-06 11:25:20 +00:00
|
|
|
}
|
2016-01-13 09:26:06 +00:00
|
|
|
|
2016-02-10 18:28:40 +00:00
|
|
|
if (clear) {
|
|
|
|
clear_icmp();
|
|
|
|
clear_udp();
|
|
|
|
clear_tcp();
|
|
|
|
}
|
2016-01-11 22:06:35 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
log_android(ANDROID_LOG_WARN, "Stopped thread %x", t);
|
2016-01-13 09:26:06 +00:00
|
|
|
} else
|
2016-02-09 12:39:49 +00:00
|
|
|
log_android(ANDROID_LOG_WARN, "Not running thread %x", t);
|
2016-01-13 09:26:06 +00:00
|
|
|
}
|
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
JNIEXPORT jintArray JNICALL
|
|
|
|
Java_eu_faircode_netguard_SinkholeService_jni_1get_1session_1count(JNIEnv *env, jobject instance) {
|
|
|
|
if (pthread_mutex_lock(&lock))
|
|
|
|
log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed");
|
2016-01-18 14:29:01 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
jintArray jarray = (*env)->NewIntArray(env, 3);
|
|
|
|
jint *jcount = (*env)->GetIntArrayElements(env, jarray, NULL);
|
2016-02-10 18:28:40 +00:00
|
|
|
jcount[0] = get_icmp_sessions();
|
|
|
|
jcount[1] = get_udp_sessions();
|
|
|
|
jcount[2] = get_tcp_sessions();
|
2016-02-02 13:31:44 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
if (pthread_mutex_unlock(&lock))
|
|
|
|
log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed");
|
2016-02-02 13:31:44 +00:00
|
|
|
|
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
(*env)->ReleaseIntArrayElements(env, jarray, jcount, NULL);
|
|
|
|
return jarray;
|
|
|
|
}
|
2016-02-02 13:31:44 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
JNIEXPORT void JNICALL
|
|
|
|
Java_eu_faircode_netguard_SinkholeService_jni_1pcap(JNIEnv *env, jclass type, jstring name_) {
|
|
|
|
if (pthread_mutex_lock(&lock))
|
|
|
|
log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed");
|
2016-02-02 13:31:44 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
if (name_ == NULL) {
|
|
|
|
if (pcap_file != NULL) {
|
|
|
|
int flags = fcntl(fileno(pcap_file), F_GETFL, 0);
|
|
|
|
if (flags < 0 || fcntl(fileno(pcap_file), F_SETFL, flags & ~O_NONBLOCK) < 0)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "PCAP fcntl ~O_NONBLOCK error %d: %s",
|
|
|
|
errno, strerror(errno));
|
2016-02-02 13:31:44 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
if (fsync(fileno(pcap_file)))
|
|
|
|
log_android(ANDROID_LOG_ERROR, "PCAP fsync error %d: %s", errno, strerror(errno));
|
2016-02-02 13:31:44 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
if (fclose(pcap_file))
|
|
|
|
log_android(ANDROID_LOG_ERROR, "PCAP fclose error %d: %s", errno, strerror(errno));
|
2016-02-02 13:31:44 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
pcap_file = NULL;
|
|
|
|
}
|
|
|
|
log_android(ANDROID_LOG_INFO, "PCAP disabled");
|
2016-01-24 21:46:25 +00:00
|
|
|
}
|
|
|
|
else {
|
2016-02-09 12:39:49 +00:00
|
|
|
const char *name = (*env)->GetStringUTFChars(env, name_, 0);
|
|
|
|
log_android(ANDROID_LOG_INFO, "PCAP file %s", name);
|
2016-01-20 13:11:04 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
pcap_file = fopen(name, "ab+");
|
|
|
|
if (pcap_file == NULL)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "PCAP fopen error %d: %s", errno, strerror(errno));
|
|
|
|
else {
|
|
|
|
int flags = fcntl(fileno(pcap_file), F_GETFL, 0);
|
|
|
|
if (flags < 0 || fcntl(fileno(pcap_file), F_SETFL, flags | O_NONBLOCK) < 0)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "PCAP fcntl O_NONBLOCK error %d: %s",
|
|
|
|
errno, strerror(errno));
|
2016-01-20 13:11:04 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
if (ftell(pcap_file) == 0) {
|
|
|
|
log_android(ANDROID_LOG_INFO, "Initializing PCAP");
|
|
|
|
write_pcap_hdr();
|
|
|
|
}
|
|
|
|
}
|
2016-01-20 13:11:04 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
(*env)->ReleaseStringUTFChars(env, name_, name);
|
2016-01-26 17:53:17 +00:00
|
|
|
}
|
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
if (pthread_mutex_unlock(&lock))
|
|
|
|
log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed");
|
2016-01-20 13:11:04 +00:00
|
|
|
}
|
|
|
|
|
2016-02-05 11:03:44 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
JNIEXPORT void JNICALL
|
|
|
|
Java_eu_faircode_netguard_SinkholeService_jni_1done(JNIEnv *env, jobject instance) {
|
|
|
|
log_android(ANDROID_LOG_INFO, "Done");
|
2016-02-05 13:52:52 +00:00
|
|
|
|
2016-02-10 18:28:40 +00:00
|
|
|
clear_icmp();
|
|
|
|
clear_udp();
|
|
|
|
clear_tcp();
|
2016-01-26 17:53:17 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
if (pthread_mutex_destroy(&lock))
|
|
|
|
log_android(ANDROID_LOG_ERROR, "pthread_mutex_destroy failed");
|
2016-01-14 14:02:32 +00:00
|
|
|
}
|
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
// JNI Util
|
2016-01-19 08:49:47 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
JNIEXPORT jstring JNICALL
|
|
|
|
Java_eu_faircode_netguard_Util_jni_1getprop(JNIEnv *env, jclass type, jstring name_) {
|
|
|
|
const char *name = (*env)->GetStringUTFChars(env, name_, 0);
|
2016-01-19 08:49:47 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
char value[250] = "";
|
|
|
|
__system_property_get(env, name, value);
|
2016-01-18 18:37:52 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
(*env)->ReleaseStringUTFChars(env, name_, name);
|
2016-01-10 07:14:47 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
return (*env)->NewStringUTF(env, value);
|
|
|
|
}
|
2016-01-19 08:49:47 +00:00
|
|
|
|
2016-02-09 15:31:56 +00:00
|
|
|
JNIEXPORT jboolean JNICALL
|
|
|
|
Java_eu_faircode_netguard_Util_is_1numeric_1address(JNIEnv *env, jclass type, jstring ip_) {
|
|
|
|
jboolean numeric = 0;
|
|
|
|
const char *ip = (*env)->GetStringUTFChars(env, ip_, 0);
|
|
|
|
|
|
|
|
struct addrinfo hints;
|
|
|
|
memset(&hints, 0, sizeof(struct addrinfo));
|
|
|
|
hints.ai_family = AF_UNSPEC;
|
|
|
|
hints.ai_flags = AI_NUMERICHOST;
|
|
|
|
struct addrinfo *result;
|
|
|
|
int err = getaddrinfo(ip, NULL, &hints, &result);
|
|
|
|
if (err)
|
|
|
|
log_android(ANDROID_LOG_WARN, "getaddrinfo(%s) error %d: %s", ip, err, gai_strerror(err));
|
|
|
|
else
|
|
|
|
numeric = (result != NULL);
|
|
|
|
|
|
|
|
(*env)->ReleaseStringUTFChars(env, ip_, ip);
|
|
|
|
return numeric;
|
|
|
|
}
|
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
void report_exit(const struct arguments *args, const char *fmt, ...) {
|
|
|
|
jclass cls = (*args->env)->GetObjectClass(args->env, args->instance);
|
|
|
|
jmethodID mid = jniGetMethodID(args->env, cls, "nativeExit", "(Ljava/lang/String;)V");
|
2016-01-10 07:14:47 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
jstring jreason = NULL;
|
|
|
|
if (fmt != NULL) {
|
|
|
|
char line[1024];
|
|
|
|
va_list argptr;
|
|
|
|
va_start(argptr, fmt);
|
|
|
|
vsprintf(line, fmt, argptr);
|
|
|
|
jreason = (*args->env)->NewStringUTF(args->env, line);
|
|
|
|
va_end(argptr);
|
2016-01-09 20:17:42 +00:00
|
|
|
}
|
2016-01-10 07:14:47 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
(*args->env)->CallVoidMethod(args->env, args->instance, mid, jreason);
|
|
|
|
jniCheckException(args->env);
|
2016-01-10 07:14:47 +00:00
|
|
|
|
2016-02-09 12:39:49 +00:00
|
|
|
if (jreason != NULL)
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, jreason);
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, cls);
|
2016-01-12 12:15:21 +00:00
|
|
|
}
|
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
static jmethodID midProtect = NULL;
|
|
|
|
|
2016-01-23 19:46:27 +00:00
|
|
|
int protect_socket(const struct arguments *args, int socket) {
|
2016-01-22 15:41:27 +00:00
|
|
|
jclass cls = (*args->env)->GetObjectClass(args->env, args->instance);
|
2016-01-28 17:21:57 +00:00
|
|
|
if (midProtect == NULL)
|
|
|
|
midProtect = jniGetMethodID(args->env, cls, "protect", "(I)Z");
|
2016-01-20 08:24:34 +00:00
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
jboolean isProtected = (*args->env)->CallBooleanMethod(
|
|
|
|
args->env, args->instance, midProtect, socket);
|
2016-01-22 15:41:27 +00:00
|
|
|
jniCheckException(args->env);
|
2016-01-20 08:24:34 +00:00
|
|
|
|
|
|
|
if (!isProtected) {
|
2016-01-22 15:41:27 +00:00
|
|
|
log_android(ANDROID_LOG_ERROR, "protect socket failed");
|
2016-01-20 08:24:34 +00:00
|
|
|
return -1;
|
|
|
|
}
|
|
|
|
|
2016-01-22 15:41:27 +00:00
|
|
|
(*args->env)->DeleteLocalRef(args->env, cls);
|
2016-01-21 11:55:08 +00:00
|
|
|
|
2016-01-20 08:24:34 +00:00
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-22 09:37:57 +00:00
|
|
|
// http://docs.oracle.com/javase/7/docs/technotes/guides/jni/spec/functions.html
|
|
|
|
// http://journals.ecs.soton.ac.uk/java/tutorial/native1.1/implementing/index.html
|
|
|
|
|
|
|
|
jobject jniGlobalRef(JNIEnv *env, jobject cls) {
|
|
|
|
jobject gcls = (*env)->NewGlobalRef(env, cls);
|
|
|
|
if (gcls == NULL)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "Global ref failed (out of memory?)");
|
|
|
|
return gcls;
|
|
|
|
}
|
|
|
|
|
|
|
|
jclass jniFindClass(JNIEnv *env, const char *name) {
|
|
|
|
jclass cls = (*env)->FindClass(env, name);
|
|
|
|
if (cls == NULL)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "Class %s not found", name);
|
|
|
|
else
|
|
|
|
jniCheckException(env);
|
|
|
|
return cls;
|
|
|
|
}
|
|
|
|
|
|
|
|
jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature) {
|
|
|
|
jmethodID method = (*env)->GetMethodID(env, cls, name, signature);
|
2016-01-28 10:58:39 +00:00
|
|
|
if (method == NULL) {
|
|
|
|
log_android(ANDROID_LOG_ERROR, "Method %s %s not found", name, signature);
|
|
|
|
jniCheckException(env);
|
2016-01-22 15:41:27 +00:00
|
|
|
}
|
2016-01-22 09:37:57 +00:00
|
|
|
return method;
|
|
|
|
}
|
|
|
|
|
|
|
|
jfieldID jniGetFieldID(JNIEnv *env, jclass cls, const char *name, const char *type) {
|
|
|
|
jfieldID field = (*env)->GetFieldID(env, cls, name, type);
|
|
|
|
if (field == NULL)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "Field %s type %s not found", name, type);
|
|
|
|
return field;
|
|
|
|
}
|
|
|
|
|
|
|
|
jobject jniNewObject(JNIEnv *env, jclass cls, jmethodID constructor, const char *name) {
|
|
|
|
jobject object = (*env)->NewObject(env, cls, constructor);
|
|
|
|
if (object == NULL)
|
|
|
|
log_android(ANDROID_LOG_ERROR, "Create object %s failed", name);
|
|
|
|
else
|
|
|
|
jniCheckException(env);
|
|
|
|
return object;
|
|
|
|
}
|
|
|
|
|
|
|
|
int jniCheckException(JNIEnv *env) {
|
|
|
|
jthrowable ex = (*env)->ExceptionOccurred(env);
|
|
|
|
if (ex) {
|
|
|
|
(*env)->ExceptionDescribe(env);
|
|
|
|
(*env)->ExceptionClear(env);
|
|
|
|
(*env)->DeleteLocalRef(env, ex);
|
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
static jmethodID midLogPacket = NULL;
|
|
|
|
|
2016-01-28 10:58:39 +00:00
|
|
|
void log_packet(const struct arguments *args, jobject jpacket) {
|
2016-01-28 17:39:13 +00:00
|
|
|
#ifdef PROFILE_JNI
|
2016-01-18 18:37:52 +00:00
|
|
|
float mselapsed;
|
|
|
|
struct timeval start, end;
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
#endif
|
|
|
|
|
2016-01-28 10:58:39 +00:00
|
|
|
jclass clsService = (*args->env)->GetObjectClass(args->env, args->instance);
|
2016-01-22 09:37:57 +00:00
|
|
|
|
|
|
|
const char *signature = "(Leu/faircode/netguard/Packet;)V";
|
2016-01-28 17:21:57 +00:00
|
|
|
if (midLogPacket == NULL)
|
|
|
|
midLogPacket = jniGetMethodID(args->env, clsService, "logPacket", signature);
|
2016-01-28 10:58:39 +00:00
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
(*args->env)->CallVoidMethod(args->env, args->instance, midLogPacket, jpacket);
|
2016-01-28 10:58:39 +00:00
|
|
|
jniCheckException(args->env);
|
|
|
|
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, clsService);
|
2016-02-08 17:14:03 +00:00
|
|
|
(*args->env)->DeleteLocalRef(args->env, jpacket);
|
2016-01-28 10:58:39 +00:00
|
|
|
|
2016-01-28 17:39:13 +00:00
|
|
|
#ifdef PROFILE_JNI
|
2016-01-28 10:58:39 +00:00
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
|
|
|
|
(end.tv_usec - start.tv_usec) / 1000.0;
|
2016-01-28 17:39:13 +00:00
|
|
|
if (mselapsed > PROFILE_JNI)
|
|
|
|
log_android(ANDROID_LOG_WARN, "log_packet %f", mselapsed);
|
2016-01-28 10:58:39 +00:00
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-01-30 08:51:41 +00:00
|
|
|
static jmethodID midDnsResolved = NULL;
|
|
|
|
static jmethodID midInitRR = NULL;
|
2016-01-30 09:59:19 +00:00
|
|
|
jfieldID fidQTime = NULL;
|
2016-01-30 08:51:41 +00:00
|
|
|
jfieldID fidQName = NULL;
|
|
|
|
jfieldID fidAName = NULL;
|
|
|
|
jfieldID fidResource = NULL;
|
|
|
|
jfieldID fidTTL = NULL;
|
|
|
|
|
|
|
|
void dns_resolved(const struct arguments *args,
|
|
|
|
const char *qname, const char *aname, const char *resource, int ttl) {
|
|
|
|
#ifdef PROFILE_JNI
|
|
|
|
float mselapsed;
|
|
|
|
struct timeval start, end;
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
jclass clsService = (*args->env)->GetObjectClass(args->env, args->instance);
|
|
|
|
|
|
|
|
const char *signature = "(Leu/faircode/netguard/ResourceRecord;)V";
|
|
|
|
if (midDnsResolved == NULL)
|
|
|
|
midDnsResolved = jniGetMethodID(args->env, clsService, "dnsResolved", signature);
|
|
|
|
|
|
|
|
const char *rr = "eu/faircode/netguard/ResourceRecord";
|
|
|
|
if (midInitRR == NULL)
|
|
|
|
midInitRR = jniGetMethodID(args->env, clsRR, "<init>", "()V");
|
|
|
|
|
|
|
|
jobject jrr = jniNewObject(args->env, clsRR, midInitRR, rr);
|
|
|
|
|
2016-01-30 09:59:19 +00:00
|
|
|
if (fidQTime == NULL) {
|
2016-01-30 08:51:41 +00:00
|
|
|
const char *string = "Ljava/lang/String;";
|
2016-01-30 09:59:19 +00:00
|
|
|
fidQTime = jniGetFieldID(args->env, clsRR, "Time", "J");
|
2016-01-30 08:51:41 +00:00
|
|
|
fidQName = jniGetFieldID(args->env, clsRR, "QName", string);
|
|
|
|
fidAName = jniGetFieldID(args->env, clsRR, "AName", string);
|
|
|
|
fidResource = jniGetFieldID(args->env, clsRR, "Resource", string);
|
|
|
|
fidTTL = jniGetFieldID(args->env, clsRR, "TTL", "I");
|
|
|
|
}
|
|
|
|
|
2016-01-30 09:59:19 +00:00
|
|
|
jlong jtime = time(NULL) * 1000LL;
|
2016-01-30 08:51:41 +00:00
|
|
|
jstring jqname = (*args->env)->NewStringUTF(args->env, qname);
|
|
|
|
jstring janame = (*args->env)->NewStringUTF(args->env, aname);
|
|
|
|
jstring jresource = (*args->env)->NewStringUTF(args->env, resource);
|
|
|
|
|
2016-01-30 09:59:19 +00:00
|
|
|
(*args->env)->SetLongField(args->env, jrr, fidQTime, jtime);
|
2016-01-30 08:51:41 +00:00
|
|
|
(*args->env)->SetObjectField(args->env, jrr, fidQName, jqname);
|
|
|
|
(*args->env)->SetObjectField(args->env, jrr, fidAName, janame);
|
|
|
|
(*args->env)->SetObjectField(args->env, jrr, fidResource, jresource);
|
|
|
|
(*args->env)->SetIntField(args->env, jrr, fidTTL, ttl);
|
|
|
|
|
|
|
|
(*args->env)->CallVoidMethod(args->env, args->instance, midDnsResolved, jrr);
|
|
|
|
jniCheckException(args->env);
|
|
|
|
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, jresource);
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, janame);
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, jqname);
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, jrr);
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, clsService);
|
|
|
|
|
|
|
|
#ifdef PROFILE_JNI
|
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
|
|
|
|
(end.tv_usec - start.tv_usec) / 1000.0;
|
|
|
|
if (mselapsed > PROFILE_JNI)
|
|
|
|
log_android(ANDROID_LOG_WARN, "log_packet %f", mselapsed);
|
|
|
|
#endif
|
|
|
|
}
|
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
static jmethodID midIsDomainBlocked = NULL;
|
|
|
|
|
2016-01-28 10:58:39 +00:00
|
|
|
jboolean is_domain_blocked(const struct arguments *args, const char *name) {
|
2016-01-28 17:39:13 +00:00
|
|
|
#ifdef PROFILE_JNI
|
2016-01-28 10:58:39 +00:00
|
|
|
float mselapsed;
|
|
|
|
struct timeval start, end;
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
jclass clsService = (*args->env)->GetObjectClass(args->env, args->instance);
|
|
|
|
|
|
|
|
const char *signature = "(Ljava/lang/String;)Z";
|
2016-01-28 17:21:57 +00:00
|
|
|
if (midIsDomainBlocked == NULL)
|
2016-01-30 08:51:41 +00:00
|
|
|
midIsDomainBlocked = jniGetMethodID(args->env, clsService, "isDomainBlocked", signature);
|
2016-01-28 10:58:39 +00:00
|
|
|
|
|
|
|
jstring jname = (*args->env)->NewStringUTF(args->env, name);
|
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
jboolean jallowed = (*args->env)->CallBooleanMethod(
|
|
|
|
args->env, args->instance, midIsDomainBlocked, jname);
|
2016-01-28 10:58:39 +00:00
|
|
|
jniCheckException(args->env);
|
|
|
|
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, jname);
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, clsService);
|
|
|
|
|
2016-01-28 17:39:13 +00:00
|
|
|
#ifdef PROFILE_JNI
|
2016-01-28 10:58:39 +00:00
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
|
|
|
|
(end.tv_usec - start.tv_usec) / 1000.0;
|
2016-01-28 17:39:13 +00:00
|
|
|
if (mselapsed > PROFILE_JNI)
|
|
|
|
log_android(ANDROID_LOG_WARN, "is_domain_blocked %f", mselapsed);
|
2016-01-28 10:58:39 +00:00
|
|
|
#endif
|
|
|
|
|
|
|
|
return jallowed;
|
|
|
|
}
|
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
static jmethodID midIsAddressAllowed = NULL;
|
2016-02-08 15:34:54 +00:00
|
|
|
jfieldID fidRaddr = NULL;
|
|
|
|
jfieldID fidRport = NULL;
|
2016-02-07 16:44:23 +00:00
|
|
|
struct allowed allowed;
|
2016-01-28 17:21:57 +00:00
|
|
|
|
2016-02-07 16:44:23 +00:00
|
|
|
struct allowed *is_address_allowed(const struct arguments *args, jobject jpacket) {
|
2016-01-28 17:39:13 +00:00
|
|
|
#ifdef PROFILE_JNI
|
2016-01-28 10:58:39 +00:00
|
|
|
float mselapsed;
|
|
|
|
struct timeval start, end;
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
#endif
|
|
|
|
|
|
|
|
jclass clsService = (*args->env)->GetObjectClass(args->env, args->instance);
|
|
|
|
|
2016-02-07 16:44:23 +00:00
|
|
|
const char *signature = "(Leu/faircode/netguard/Packet;)Leu/faircode/netguard/Allowed;";
|
2016-01-28 17:21:57 +00:00
|
|
|
if (midIsAddressAllowed == NULL)
|
2016-01-30 08:51:41 +00:00
|
|
|
midIsAddressAllowed = jniGetMethodID(args->env, clsService, "isAddressAllowed", signature);
|
2016-01-28 10:58:39 +00:00
|
|
|
|
2016-02-07 16:44:23 +00:00
|
|
|
jobject jallowed = (*args->env)->CallObjectMethod(
|
2016-01-28 17:21:57 +00:00
|
|
|
args->env, args->instance, midIsAddressAllowed, jpacket);
|
2016-01-28 10:58:39 +00:00
|
|
|
jniCheckException(args->env);
|
|
|
|
|
2016-02-07 16:44:23 +00:00
|
|
|
if (jallowed != NULL) {
|
2016-02-08 15:34:54 +00:00
|
|
|
if (fidRaddr == NULL) {
|
2016-02-07 16:44:23 +00:00
|
|
|
const char *string = "Ljava/lang/String;";
|
2016-02-08 15:34:54 +00:00
|
|
|
fidRaddr = jniGetFieldID(args->env, clsAllowed, "raddr", string);
|
|
|
|
fidRport = jniGetFieldID(args->env, clsAllowed, "rport", "I");
|
2016-02-07 16:44:23 +00:00
|
|
|
}
|
|
|
|
|
2016-02-08 15:34:54 +00:00
|
|
|
jstring jraddr = (*args->env)->GetObjectField(args->env, jallowed, fidRaddr);
|
|
|
|
if (jraddr == NULL)
|
|
|
|
*allowed.raddr = 0;
|
2016-02-08 13:33:33 +00:00
|
|
|
else {
|
2016-02-08 15:34:54 +00:00
|
|
|
const char *raddr = (*args->env)->GetStringUTFChars(args->env, jraddr, NULL);
|
|
|
|
strcpy(allowed.raddr, raddr);
|
|
|
|
(*args->env)->ReleaseStringUTFChars(args->env, jraddr, raddr);
|
2016-02-08 13:33:33 +00:00
|
|
|
}
|
2016-02-08 15:34:54 +00:00
|
|
|
allowed.rport = (uint16_t) (*args->env)->GetIntField(args->env, jallowed, fidRport);
|
2016-02-08 13:33:33 +00:00
|
|
|
|
2016-02-08 15:34:54 +00:00
|
|
|
(*args->env)->DeleteLocalRef(args->env, jraddr);
|
2016-02-07 16:44:23 +00:00
|
|
|
}
|
|
|
|
|
2016-02-08 17:14:03 +00:00
|
|
|
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, jpacket);
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, clsService);
|
|
|
|
(*args->env)->DeleteLocalRef(args->env, jallowed);
|
|
|
|
|
2016-01-28 17:39:13 +00:00
|
|
|
#ifdef PROFILE_JNI
|
2016-01-28 10:58:39 +00:00
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
|
|
|
|
(end.tv_usec - start.tv_usec) / 1000.0;
|
2016-01-28 17:39:13 +00:00
|
|
|
if (mselapsed > PROFILE_JNI)
|
|
|
|
log_android(ANDROID_LOG_WARN, "is_address_allowed %f", mselapsed);
|
2016-01-28 10:58:39 +00:00
|
|
|
#endif
|
|
|
|
|
2016-02-07 16:44:23 +00:00
|
|
|
return (jallowed == NULL ? NULL : &allowed);
|
2016-01-28 10:58:39 +00:00
|
|
|
}
|
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
jmethodID midInitPacket = NULL;
|
|
|
|
|
|
|
|
jfieldID fidTime = NULL;
|
|
|
|
jfieldID fidVersion = NULL;
|
|
|
|
jfieldID fidProtocol = NULL;
|
|
|
|
jfieldID fidFlags = NULL;
|
|
|
|
jfieldID fidSaddr = NULL;
|
|
|
|
jfieldID fidSport = NULL;
|
|
|
|
jfieldID fidDaddr = NULL;
|
|
|
|
jfieldID fidDport = NULL;
|
|
|
|
jfieldID fidData = NULL;
|
|
|
|
jfieldID fidUid = NULL;
|
|
|
|
jfieldID fidAllowed = NULL;
|
|
|
|
|
2016-01-28 10:58:39 +00:00
|
|
|
jobject create_packet(const struct arguments *args,
|
|
|
|
jint version,
|
|
|
|
jint protocol,
|
|
|
|
const char *flags,
|
|
|
|
const char *source,
|
|
|
|
jint sport,
|
|
|
|
const char *dest,
|
|
|
|
jint dport,
|
|
|
|
const char *data,
|
|
|
|
jint uid,
|
|
|
|
jboolean allowed) {
|
|
|
|
JNIEnv *env = args->env;
|
2016-01-22 09:37:57 +00:00
|
|
|
|
2016-01-28 17:39:13 +00:00
|
|
|
#ifdef PROFILE_JNI
|
|
|
|
float mselapsed;
|
|
|
|
struct timeval start, end;
|
|
|
|
gettimeofday(&start, NULL);
|
|
|
|
#endif
|
|
|
|
|
2016-01-31 18:50:52 +00:00
|
|
|
/*
|
|
|
|
jbyte b[] = {1,2,3};
|
|
|
|
jbyteArray ret = env->NewByteArray(3);
|
|
|
|
env->SetByteArrayRegion (ret, 0, 3, b);
|
|
|
|
*/
|
|
|
|
|
2016-01-22 09:37:57 +00:00
|
|
|
const char *packet = "eu/faircode/netguard/Packet";
|
2016-01-28 17:21:57 +00:00
|
|
|
if (midInitPacket == NULL)
|
|
|
|
midInitPacket = jniGetMethodID(env, clsPacket, "<init>", "()V");
|
|
|
|
jobject jpacket = jniNewObject(env, clsPacket, midInitPacket, packet);
|
2016-01-22 09:37:57 +00:00
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
if (fidTime == NULL) {
|
|
|
|
const char *string = "Ljava/lang/String;";
|
|
|
|
fidTime = jniGetFieldID(env, clsPacket, "time", "J");
|
|
|
|
fidVersion = jniGetFieldID(env, clsPacket, "version", "I");
|
|
|
|
fidProtocol = jniGetFieldID(env, clsPacket, "protocol", "I");
|
|
|
|
fidFlags = jniGetFieldID(env, clsPacket, "flags", string);
|
|
|
|
fidSaddr = jniGetFieldID(env, clsPacket, "saddr", string);
|
|
|
|
fidSport = jniGetFieldID(env, clsPacket, "sport", "I");
|
|
|
|
fidDaddr = jniGetFieldID(env, clsPacket, "daddr", string);
|
|
|
|
fidDport = jniGetFieldID(env, clsPacket, "dport", "I");
|
|
|
|
fidData = jniGetFieldID(env, clsPacket, "data", string);
|
|
|
|
fidUid = jniGetFieldID(env, clsPacket, "uid", "I");
|
|
|
|
fidAllowed = jniGetFieldID(env, clsPacket, "allowed", "Z");
|
|
|
|
}
|
|
|
|
|
2016-01-30 08:51:41 +00:00
|
|
|
struct timeval tv;
|
|
|
|
gettimeofday(&tv, NULL);
|
|
|
|
jlong t = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
|
|
|
|
jstring jflags = (*env)->NewStringUTF(env, flags);
|
|
|
|
jstring jsource = (*env)->NewStringUTF(env, source);
|
|
|
|
jstring jdest = (*env)->NewStringUTF(env, dest);
|
|
|
|
jstring jdata = (*env)->NewStringUTF(env, data);
|
|
|
|
|
2016-01-28 17:21:57 +00:00
|
|
|
(*env)->SetLongField(env, jpacket, fidTime, t);
|
|
|
|
(*env)->SetIntField(env, jpacket, fidVersion, version);
|
|
|
|
(*env)->SetIntField(env, jpacket, fidProtocol, protocol);
|
|
|
|
(*env)->SetObjectField(env, jpacket, fidFlags, jflags);
|
|
|
|
(*env)->SetObjectField(env, jpacket, fidSaddr, jsource);
|
|
|
|
(*env)->SetIntField(env, jpacket, fidSport, sport);
|
|
|
|
(*env)->SetObjectField(env, jpacket, fidDaddr, jdest);
|
|
|
|
(*env)->SetIntField(env, jpacket, fidDport, dport);
|
|
|
|
(*env)->SetObjectField(env, jpacket, fidData, jdata);
|
|
|
|
(*env)->SetIntField(env, jpacket, fidUid, uid);
|
|
|
|
(*env)->SetBooleanField(env, jpacket, fidAllowed, allowed);
|
2016-01-22 09:37:57 +00:00
|
|
|
|
2016-01-26 10:41:03 +00:00
|
|
|
(*env)->DeleteLocalRef(env, jdata);
|
2016-01-22 09:37:57 +00:00
|
|
|
(*env)->DeleteLocalRef(env, jdest);
|
|
|
|
(*env)->DeleteLocalRef(env, jsource);
|
|
|
|
(*env)->DeleteLocalRef(env, jflags);
|
2016-01-28 10:58:39 +00:00
|
|
|
// Caller needs to delete reference to packet
|
2016-01-18 18:37:52 +00:00
|
|
|
|
2016-01-28 17:39:13 +00:00
|
|
|
#ifdef PROFILE_JNI
|
|
|
|
gettimeofday(&end, NULL);
|
|
|
|
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
|
|
|
|
(end.tv_usec - start.tv_usec) / 1000.0;
|
|
|
|
if (mselapsed > PROFILE_JNI)
|
|
|
|
log_android(ANDROID_LOG_WARN, "create_packet %f", mselapsed);
|
|
|
|
#endif
|
|
|
|
|
2016-01-28 10:58:39 +00:00
|
|
|
return jpacket;
|
2016-01-18 18:37:52 +00:00
|
|
|
}
|