/* 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 . Copyright 2015-2019 by Marcel Bokhorst (M66B) */ #include "netguard.h" // It is assumed that no packets will get lost and that packets arrive in order // https://android.googlesource.com/platform/frameworks/base.git/+/master/services/core/jni/com_android_server_connectivity_Vpn.cpp // Global variables char socks5_addr[INET6_ADDRSTRLEN + 1]; int socks5_port = 0; char socks5_username[127 + 1]; char socks5_password[127 + 1]; int loglevel = ANDROID_LOG_WARN; extern int max_tun_msg; extern FILE *pcap_file; extern size_t pcap_record_size; extern long pcap_file_size; // JNI jclass clsPacket; jclass clsAllowed; jclass clsRR; jclass clsUsage; 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)); const char *allowed = "eu/faircode/netguard/Allowed"; clsAllowed = jniGlobalRef(env, jniFindClass(env, allowed)); const char *rr = "eu/faircode/netguard/ResourceRecord"; clsRR = jniGlobalRef(env, jniFindClass(env, rr)); const char *usage = "eu/faircode/netguard/Usage"; clsUsage = jniGlobalRef(env, jniFindClass(env, usage)); // 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); } 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); (*env)->DeleteGlobalRef(env, clsRR); } } // JNI ServiceSinkhole JNIEXPORT jlong JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1init( JNIEnv *env, jobject instance, jint sdk) { struct context *ctx = calloc(1, sizeof(struct context)); ctx->sdk = sdk; loglevel = ANDROID_LOG_WARN; *socks5_addr = 0; socks5_port = 0; *socks5_username = 0; *socks5_password = 0; pcap_file = NULL; if (pthread_mutex_init(&ctx->lock, NULL)) log_android(ANDROID_LOG_ERROR, "pthread_mutex_init failed"); // Create signal pipe if (pipe(ctx->pipefds)) log_android(ANDROID_LOG_ERROR, "Create pipe error %d: %s", errno, strerror(errno)); else for (int i = 0; i < 2; i++) { int flags = fcntl(ctx->pipefds[i], F_GETFL, 0); if (flags < 0 || fcntl(ctx->pipefds[i], F_SETFL, flags | O_NONBLOCK) < 0) log_android(ANDROID_LOG_ERROR, "fcntl pipefds[%d] O_NONBLOCK error %d: %s", i, errno, strerror(errno)); } return (jlong) ctx; } JNIEXPORT void JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1start( JNIEnv *env, jobject instance, jlong context, jint loglevel_) { struct context *ctx = (struct context *) context; loglevel = loglevel_; max_tun_msg = 0; ctx->stopping = 0; log_android(ANDROID_LOG_WARN, "Starting level %d", loglevel); } JNIEXPORT void JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1run( JNIEnv *env, jobject instance, jlong context, jint tun, jboolean fwd53, jint rcode) { struct context *ctx = (struct context *) context; log_android(ANDROID_LOG_WARN, "Running tun %d fwd53 %d level %d", tun, fwd53, loglevel); // 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)); // Get arguments struct arguments *args = malloc(sizeof(struct arguments)); args->env = env; args->instance = instance; args->tun = tun; args->fwd53 = fwd53; args->rcode = rcode; args->ctx = ctx; handle_events(args); } JNIEXPORT void JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1stop( JNIEnv *env, jobject instance, jlong context) { struct context *ctx = (struct context *) context; ctx->stopping = 1; log_android(ANDROID_LOG_WARN, "Write pipe wakeup"); if (write(ctx->pipefds[1], "w", 1) < 0) log_android(ANDROID_LOG_WARN, "Write pipe error %d: %s", errno, strerror(errno)); } JNIEXPORT void JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1clear( JNIEnv *env, jobject instance, jlong context) { struct context *ctx = (struct context *) context; clear(ctx); } JNIEXPORT jint JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1get_1mtu(JNIEnv *env, jobject instance) { return get_mtu(); } JNIEXPORT jintArray JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1get_1stats( JNIEnv *env, jobject instance, jlong context) { struct context *ctx = (struct context *) context; if (pthread_mutex_lock(&ctx->lock)) log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed"); jintArray jarray = (*env)->NewIntArray(env, 5); jint *jcount = (*env)->GetIntArrayElements(env, jarray, NULL); struct ng_session *s = ctx->ng_session; while (s != NULL) { if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) { if (!s->icmp.stop) jcount[0]++; } else if (s->protocol == IPPROTO_UDP) { if (s->udp.state == UDP_ACTIVE) jcount[1]++; } else if (s->protocol == IPPROTO_TCP) { if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE) jcount[2]++; } s = s->next; } if (pthread_mutex_unlock(&ctx->lock)) log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed"); jcount[3] = 0; DIR *d = opendir("/proc/self/fd"); if (d) { struct dirent *dir; while ((dir = readdir(d)) != NULL) if (dir->d_type != DT_DIR) jcount[3]++; closedir(d); } struct rlimit rlim; memset(&rlim, 0, sizeof(struct rlimit)); getrlimit(RLIMIT_NOFILE, &rlim); jcount[4] = (jint) rlim.rlim_cur; (*env)->ReleaseIntArrayElements(env, jarray, jcount, 0); return jarray; } JNIEXPORT void JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1pcap( JNIEnv *env, jclass type, jstring name_, jint record_size, jint file_size) { pcap_record_size = (size_t) record_size; pcap_file_size = file_size; //if (pthread_mutex_lock(&lock)) // log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed"); 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)); if (fsync(fileno(pcap_file))) log_android(ANDROID_LOG_ERROR, "PCAP fsync error %d: %s", errno, strerror(errno)); if (fclose(pcap_file)) log_android(ANDROID_LOG_ERROR, "PCAP fclose error %d: %s", errno, strerror(errno)); pcap_file = NULL; } log_android(ANDROID_LOG_WARN, "PCAP disabled"); } else { const char *name = (*env)->GetStringUTFChars(env, name_, 0); log_android(ANDROID_LOG_WARN, "PCAP file %s record size %d truncate @%ld", name, pcap_record_size, pcap_file_size); 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)); long size = ftell(pcap_file); if (size == 0) { log_android(ANDROID_LOG_WARN, "PCAP initialize"); write_pcap_hdr(); } else log_android(ANDROID_LOG_WARN, "PCAP current size %ld", size); } (*env)->ReleaseStringUTFChars(env, name_, name); } //if (pthread_mutex_unlock(&lock)) // log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed"); } JNIEXPORT void JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1socks5(JNIEnv *env, jobject instance, jstring addr_, jint port, jstring username_, jstring password_) { const char *addr = (*env)->GetStringUTFChars(env, addr_, 0); const char *username = (*env)->GetStringUTFChars(env, username_, 0); const char *password = (*env)->GetStringUTFChars(env, password_, 0); strcpy(socks5_addr, addr); socks5_port = port; strcpy(socks5_username, username); strcpy(socks5_password, password); log_android(ANDROID_LOG_WARN, "SOCKS5 %s:%d user=%s", socks5_addr, socks5_port, socks5_username); (*env)->ReleaseStringUTFChars(env, addr_, addr); (*env)->ReleaseStringUTFChars(env, username_, username); (*env)->ReleaseStringUTFChars(env, password_, password); } JNIEXPORT void JNICALL Java_eu_faircode_netguard_ServiceSinkhole_jni_1done( JNIEnv *env, jobject instance, jlong context) { struct context *ctx = (struct context *) context; log_android(ANDROID_LOG_INFO, "Done"); clear(ctx); if (pthread_mutex_destroy(&ctx->lock)) log_android(ANDROID_LOG_ERROR, "pthread_mutex_destroy failed"); for (int i = 0; i < 2; i++) if (close(ctx->pipefds[i])) log_android(ANDROID_LOG_ERROR, "Close pipe error %d: %s", errno, strerror(errno)); free(ctx); } // JNI Util JNIEXPORT jstring JNICALL Java_eu_faircode_netguard_Util_jni_1getprop(JNIEnv *env, jclass type, jstring name_) { const char *name = (*env)->GetStringUTFChars(env, name_, 0); char value[PROP_VALUE_MAX + 1] = ""; __system_property_get(name, value); (*env)->ReleaseStringUTFChars(env, name_, name); return (*env)->NewStringUTF(env, value); } 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_DEBUG, "getaddrinfo(%s) error %d: %s", ip, err, gai_strerror(err)); else numeric = (jboolean) (result != NULL); (*env)->ReleaseStringUTFChars(env, ip_, ip); return numeric; } 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"); 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); } (*args->env)->CallVoidMethod(args->env, args->instance, mid, jreason); jniCheckException(args->env); if (jreason != NULL) (*args->env)->DeleteLocalRef(args->env, jreason); (*args->env)->DeleteLocalRef(args->env, cls); } void report_error(const struct arguments *args, jint error, const char *fmt, ...) { jclass cls = (*args->env)->GetObjectClass(args->env, args->instance); jmethodID mid = jniGetMethodID(args->env, cls, "nativeError", "(ILjava/lang/String;)V"); 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); } (*args->env)->CallVoidMethod(args->env, args->instance, mid, error, jreason); jniCheckException(args->env); if (jreason != NULL) (*args->env)->DeleteLocalRef(args->env, jreason); (*args->env)->DeleteLocalRef(args->env, cls); } static jmethodID midProtect = NULL; int protect_socket(const struct arguments *args, int socket) { if (args->ctx->sdk >= 21) return 0; jclass cls = (*args->env)->GetObjectClass(args->env, args->instance); if (cls == NULL) { log_android(ANDROID_LOG_ERROR, "protect socket failed to get class"); return -1; } if (midProtect == NULL) midProtect = jniGetMethodID(args->env, cls, "protect", "(I)Z"); if (midProtect == NULL) { log_android(ANDROID_LOG_ERROR, "protect socket failed to get method"); return -1; } jboolean isProtected = (*args->env)->CallBooleanMethod( args->env, args->instance, midProtect, socket); jniCheckException(args->env); if (!isProtected) { log_android(ANDROID_LOG_ERROR, "protect socket failed"); return -1; } (*args->env)->DeleteLocalRef(args->env, cls); return 0; } // 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); if (method == NULL) { log_android(ANDROID_LOG_ERROR, "Method %s %s not found", name, signature); jniCheckException(env); } 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; } static jmethodID midLogPacket = NULL; void log_packet(const struct arguments *args, jobject jpacket) { #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/Packet;)V"; if (midLogPacket == NULL) midLogPacket = jniGetMethodID(args->env, clsService, "logPacket", signature); (*args->env)->CallVoidMethod(args->env, args->instance, midLogPacket, jpacket); jniCheckException(args->env); (*args->env)->DeleteLocalRef(args->env, clsService); (*args->env)->DeleteLocalRef(args->env, jpacket); #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 } static jmethodID midDnsResolved = NULL; static jmethodID midInitRR = NULL; jfieldID fidQTime = NULL; 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, "", "()V"); jobject jrr = jniNewObject(args->env, clsRR, midInitRR, rr); if (fidQTime == NULL) { const char *string = "Ljava/lang/String;"; fidQTime = jniGetFieldID(args->env, clsRR, "Time", "J"); 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"); } jlong jtime = time(NULL) * 1000LL; jstring jqname = (*args->env)->NewStringUTF(args->env, qname); jstring janame = (*args->env)->NewStringUTF(args->env, aname); jstring jresource = (*args->env)->NewStringUTF(args->env, resource); (*args->env)->SetLongField(args->env, jrr, fidQTime, jtime); (*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 } static jmethodID midIsDomainBlocked = NULL; jboolean is_domain_blocked(const struct arguments *args, const char *name) { #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 = "(Ljava/lang/String;)Z"; if (midIsDomainBlocked == NULL) midIsDomainBlocked = jniGetMethodID(args->env, clsService, "isDomainBlocked", signature); jstring jname = (*args->env)->NewStringUTF(args->env, name); jboolean jallowed = (*args->env)->CallBooleanMethod( args->env, args->instance, midIsDomainBlocked, jname); jniCheckException(args->env); (*args->env)->DeleteLocalRef(args->env, jname); (*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, "is_domain_blocked %f", mselapsed); #endif return jallowed; } static jmethodID midGetUidQ = NULL; jint get_uid_q(const struct arguments *args, jint version, jint protocol, const char *source, jint sport, const char *dest, jint dport) { #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 = "(IILjava/lang/String;ILjava/lang/String;I)I"; if (midGetUidQ == NULL) midGetUidQ = jniGetMethodID(args->env, clsService, "getUidQ", signature); jstring jsource = (*args->env)->NewStringUTF(args->env, source); jstring jdest = (*args->env)->NewStringUTF(args->env, dest); jint juid = (*args->env)->CallIntMethod( args->env, args->instance, midGetUidQ, version, protocol, jsource, sport, jdest, dport); jniCheckException(args->env); (*args->env)->DeleteLocalRef(args->env, jdest); (*args->env)->DeleteLocalRef(args->env, jsource); (*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, "get_uid_q %f", mselapsed); #endif return juid; } static jmethodID midIsAddressAllowed = NULL; jfieldID fidRaddr = NULL; jfieldID fidRport = NULL; struct allowed allowed; struct allowed *is_address_allowed(const struct arguments *args, jobject jpacket) { #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/Packet;)Leu/faircode/netguard/Allowed;"; if (midIsAddressAllowed == NULL) midIsAddressAllowed = jniGetMethodID(args->env, clsService, "isAddressAllowed", signature); jobject jallowed = (*args->env)->CallObjectMethod( args->env, args->instance, midIsAddressAllowed, jpacket); jniCheckException(args->env); if (jallowed != NULL) { if (fidRaddr == NULL) { const char *string = "Ljava/lang/String;"; fidRaddr = jniGetFieldID(args->env, clsAllowed, "raddr", string); fidRport = jniGetFieldID(args->env, clsAllowed, "rport", "I"); } jstring jraddr = (*args->env)->GetObjectField(args->env, jallowed, fidRaddr); if (jraddr == NULL) *allowed.raddr = 0; else { const char *raddr = (*args->env)->GetStringUTFChars(args->env, jraddr, NULL); strcpy(allowed.raddr, raddr); (*args->env)->ReleaseStringUTFChars(args->env, jraddr, raddr); } allowed.rport = (uint16_t) (*args->env)->GetIntField(args->env, jallowed, fidRport); (*args->env)->DeleteLocalRef(args->env, jraddr); } (*args->env)->DeleteLocalRef(args->env, jpacket); (*args->env)->DeleteLocalRef(args->env, clsService); (*args->env)->DeleteLocalRef(args->env, jallowed); #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, "is_address_allowed %f", mselapsed); #endif return (jallowed == NULL ? NULL : &allowed); } 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; 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; #ifdef PROFILE_JNI float mselapsed; struct timeval start, end; gettimeofday(&start, NULL); #endif /* jbyte b[] = {1,2,3}; jbyteArray ret = env->NewByteArray(3); env->SetByteArrayRegion (ret, 0, 3, b); */ const char *packet = "eu/faircode/netguard/Packet"; if (midInitPacket == NULL) midInitPacket = jniGetMethodID(env, clsPacket, "", "()V"); jobject jpacket = jniNewObject(env, clsPacket, midInitPacket, packet); 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"); } 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); (*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); (*env)->DeleteLocalRef(env, jdata); (*env)->DeleteLocalRef(env, jdest); (*env)->DeleteLocalRef(env, jsource); (*env)->DeleteLocalRef(env, jflags); // Caller needs to delete reference to packet #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 return jpacket; } jmethodID midAccountUsage = NULL; jmethodID midInitUsage = NULL; jfieldID fidUsageTime = NULL; jfieldID fidUsageVersion = NULL; jfieldID fidUsageProtocol = NULL; jfieldID fidUsageDAddr = NULL; jfieldID fidUsageDPort = NULL; jfieldID fidUsageUid = NULL; jfieldID fidUsageSent = NULL; jfieldID fidUsageReceived = NULL; void account_usage(const struct arguments *args, jint version, jint protocol, const char *daddr, jint dport, jint uid, jlong sent, jlong received) { #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/Usage;)V"; if (midAccountUsage == NULL) midAccountUsage = jniGetMethodID(args->env, clsService, "accountUsage", signature); const char *usage = "eu/faircode/netguard/Usage"; if (midInitUsage == NULL) midInitUsage = jniGetMethodID(args->env, clsUsage, "", "()V"); jobject jusage = jniNewObject(args->env, clsUsage, midInitUsage, usage); if (fidUsageTime == NULL) { const char *string = "Ljava/lang/String;"; fidUsageTime = jniGetFieldID(args->env, clsUsage, "Time", "J"); fidUsageVersion = jniGetFieldID(args->env, clsUsage, "Version", "I"); fidUsageProtocol = jniGetFieldID(args->env, clsUsage, "Protocol", "I"); fidUsageDAddr = jniGetFieldID(args->env, clsUsage, "DAddr", string); fidUsageDPort = jniGetFieldID(args->env, clsUsage, "DPort", "I"); fidUsageUid = jniGetFieldID(args->env, clsUsage, "Uid", "I"); fidUsageSent = jniGetFieldID(args->env, clsUsage, "Sent", "J"); fidUsageReceived = jniGetFieldID(args->env, clsUsage, "Received", "J"); } jlong jtime = time(NULL) * 1000LL; jstring jdaddr = (*args->env)->NewStringUTF(args->env, daddr); (*args->env)->SetLongField(args->env, jusage, fidUsageTime, jtime); (*args->env)->SetIntField(args->env, jusage, fidUsageVersion, version); (*args->env)->SetIntField(args->env, jusage, fidUsageProtocol, protocol); (*args->env)->SetObjectField(args->env, jusage, fidUsageDAddr, jdaddr); (*args->env)->SetIntField(args->env, jusage, fidUsageDPort, dport); (*args->env)->SetIntField(args->env, jusage, fidUsageUid, uid); (*args->env)->SetLongField(args->env, jusage, fidUsageSent, sent); (*args->env)->SetLongField(args->env, jusage, fidUsageReceived, received); (*args->env)->CallVoidMethod(args->env, args->instance, midAccountUsage, jusage); jniCheckException(args->env); (*args->env)->DeleteLocalRef(args->env, jdaddr); (*args->env)->DeleteLocalRef(args->env, jusage); (*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 }