From d21d75edaf2330d3625df49a7f8fd663df3bab44 Mon Sep 17 00:00:00 2001 From: M66B Date: Mon, 18 Jan 2016 12:19:40 +0100 Subject: [PATCH] Native refactoring --- app/app.iml | 1 + app/src/main/jni/netguard/netguard.c | 327 ++++++++++++++------------- app/src/main/jni/netguard/netguard.h | 16 +- 3 files changed, 185 insertions(+), 159 deletions(-) diff --git a/app/app.iml b/app/app.iml index 6e13ec56..c8002dfb 100644 --- a/app/app.iml +++ b/app/app.iml @@ -98,6 +98,7 @@ + diff --git a/app/src/main/jni/netguard/netguard.c b/app/src/main/jni/netguard/netguard.c index 552ee01b..d63a5679 100644 --- a/app/src/main/jni/netguard/netguard.c +++ b/app/src/main/jni/netguard/netguard.c @@ -33,6 +33,7 @@ static JavaVM *jvm; pthread_t thread_id; int signaled = 0; struct session *session = NULL; + int loglevel = ANDROID_LOG_WARN; char *pcap_fn = NULL; @@ -133,16 +134,7 @@ void sig_handler(int sig, siginfo_t *info, void *context) { signaled = 1; } -void *handle_events(void *a) { - struct arguments *args = (struct arguments *) a; - ng_log(ANDROID_LOG_INFO, "Start events tun=%d thread %ld", args->tun, thread_id); - - JNIEnv *env; - jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL); - if (rs != JNI_OK) - ng_log(ANDROID_LOG_ERROR, "AttachCurrentThread failed"); - - int max; +void handle_events(void *a) { fd_set rfds; fd_set wfds; fd_set efds; @@ -152,6 +144,18 @@ void *handle_events(void *a) { sigset_t emptyset; struct sigaction sa; + struct arguments *args = (struct arguments *) a; + ng_log(ANDROID_LOG_INFO, "Start events tun=%d thread %ld", args->tun, thread_id); + + // Attach to Java + JNIEnv *env; + jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL); + if (rs != JNI_OK) { + ng_log(ANDROID_LOG_ERROR, "AttachCurrentThread failed"); + return; + } + args->env = env; + // Block SIGUSR1 sigemptyset(&blockset); sigaddset(&blockset, SIGUSR1); @@ -167,101 +171,14 @@ void *handle_events(void *a) { // Loop while (1) { - time_t now = time(NULL); ng_log(ANDROID_LOG_DEBUG, "Loop thread %ld", thread_id); // Select - FD_ZERO(&rfds); - FD_ZERO(&wfds); - FD_ZERO(&efds); - - // Always read tun - FD_SET(args->tun, &rfds); - FD_SET(args->tun, &efds); - - max = args->tun; - - struct session *last = NULL; - struct session *cur = session; - while (cur != NULL) { - // TODO differentiate timeouts - if (cur->time + TCPTIMEOUT < now) { - // TODO send keep alives? - char dest[20]; - inet_ntop(AF_INET, &(cur->daddr), dest, sizeof(dest)); - ng_log(ANDROID_LOG_WARN, "Idle %s/%u lport %u", - dest, ntohs(cur->dest), cur->lport); - if (cur->state == TCP_SYN_RECV || - cur->state == TCP_ESTABLISHED || - cur->state == TCP_CLOSE_WAIT) { - if (write_tcp(cur, NULL, 0, 0, 0, 1, 0, args->tun) < 0) { // FIN - ng_log(ANDROID_LOG_ERROR, - "write FIN lport %u error %d: %s", - cur->lport, errno, strerror((errno))); - cur->state = TCP_TIME_WAIT; // Will close socket - } - else { - ng_log(ANDROID_LOG_DEBUG, "Half close initiated"); - cur->local_seq++; - if (cur->state == TCP_SYN_RECV || cur->state == TCP_ESTABLISHED) - cur->state = TCP_FIN_WAIT1; - else // close wait - cur->state = TCP_LAST_ACK; - } - - } else - cur->state = TCP_TIME_WAIT; // Will close socket - } - - if (cur->state == TCP_TIME_WAIT) { - // Log - char dest[20]; - inet_ntop(AF_INET, &(cur->daddr), dest, sizeof(dest)); - ng_log(ANDROID_LOG_INFO, "Close %s/%u lport %u", - dest, ntohs(cur->dest), cur->lport); - - // TODO keep for some time - - // TODO non blocking? - if (close(cur->socket)) - ng_log(ANDROID_LOG_ERROR, "close error %d: %s", errno, strerror(errno)); - - if (last == NULL) - session = cur->next; - else - last->next = cur->next; - - struct session *c = cur; - cur = cur->next; - free(c); - continue; - - } else if (cur->state != TCP_TIME_WAIT) { - if (cur->state == TCP_LISTEN) { - FD_SET(cur->socket, &efds); - FD_SET(cur->socket, &wfds); - if (cur->socket > max) - max = cur->socket; - } - else if (cur->state == TCP_ESTABLISHED || - cur->state == TCP_SYN_RECV || - cur->state == TCP_CLOSE_WAIT) { - // Check for errors / data - FD_SET(cur->socket, &efds); - FD_SET(cur->socket, &rfds); - if (cur->socket > max) - max = cur->socket; - } - } - - last = cur; - cur = cur->next; - } - ts.tv_sec = SELECTWAIT; ts.tv_nsec = 0; // TODO let timeout depend on session timeouts sigemptyset(&emptyset); + int max = get_selects(args, &rfds, &wfds, &efds); int ready = pselect(max + 1, &rfds, &wfds, &efds, session == NULL ? NULL : &ts, &emptyset); if (ready < 0) { if (errno == EINTR) { @@ -279,6 +196,7 @@ void *handle_events(void *a) { } } + // Count sessions int sessions = 0; struct session *s = session; while (s != NULL) { @@ -291,57 +209,10 @@ void *handle_events(void *a) { else { ng_log(ANDROID_LOG_DEBUG, "pselect sessions %d ready %d", sessions, ready); - // Check tun exception - if (FD_ISSET(args->tun, &efds)) { - ng_log(ANDROID_LOG_ERROR, "tun exception"); - break; // over and out - } + // Check upstream + check_tun(args, &rfds, &wfds, &efds); - // Check tun read - if (FD_ISSET(args->tun, &rfds)) { - uint8_t buffer[MAXPKT]; - ssize_t length = read(args->tun, buffer, MAXPKT); - if (length < 0) { - ng_log(ANDROID_LOG_ERROR, "tun read error %d: %s", errno, strerror(errno)); - if (errno == EINTR) - continue; - else - break; // over and out - } - else if (length > 0) { - // Write pcap record - if (pcap_fn != NULL) { - struct timespec ts; - if (clock_gettime(CLOCK_REALTIME, &ts)) - ng_log(ANDROID_LOG_ERROR, "clock_gettime error %d: %s", - errno, strerror(errno)); - - // TODO use stack - int plen = (length < MAXPCAP ? length : MAXPCAP); - struct pcaprec_hdr_s *pcap_rec = - malloc(sizeof(struct pcaprec_hdr_s) + plen); - - pcap_rec->ts_sec = ts.tv_sec; - pcap_rec->ts_usec = ts.tv_nsec / 1000; - pcap_rec->incl_len = plen; - pcap_rec->orig_len = length; - memcpy(((uint8_t *) pcap_rec) + sizeof(struct pcaprec_hdr_s), buffer, plen); - - pcap_write(pcap_rec, sizeof(struct pcaprec_hdr_s) + plen); - - free(pcap_rec); - } - - // Handle IP from tun - handle_ip(env, args->instance, args, buffer, length); - } - else { - ng_log(ANDROID_LOG_ERROR, "tun empty read"); - break; // over and out - } - } - - // Check sockets + // Check downstream check_sockets(args, &rfds, &wfds, &efds); } } @@ -356,6 +227,152 @@ void *handle_events(void *a) { // TODO conditionally report to Java } +int get_selects(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) { + time_t now = time(NULL); + + // Select + FD_ZERO(rfds); + FD_ZERO(wfds); + FD_ZERO(efds); + + // Always read tun + FD_SET(args->tun, rfds); + FD_SET(args->tun, efds); + + int max = args->tun; + + struct session *last = NULL; + struct session *cur = session; + while (cur != NULL) { + // TODO differentiate timeouts + if (cur->time + TCPTIMEOUT < now) { + // TODO send keep alives? + char dest[20]; + inet_ntop(AF_INET, &(cur->daddr), dest, sizeof(dest)); + ng_log(ANDROID_LOG_WARN, "Idle %s/%u lport %u", + dest, ntohs(cur->dest), cur->lport); + if (cur->state == TCP_SYN_RECV || + cur->state == TCP_ESTABLISHED || + cur->state == TCP_CLOSE_WAIT) { + if (write_tcp(cur, NULL, 0, 0, 0, 1, 0, args->tun) < 0) { // FIN + ng_log(ANDROID_LOG_ERROR, + "write FIN lport %u error %d: %s", + cur->lport, errno, strerror((errno))); + cur->state = TCP_TIME_WAIT; // Will close socket + } + else { + ng_log(ANDROID_LOG_DEBUG, "Half close initiated"); + cur->local_seq++; + if (cur->state == TCP_SYN_RECV || cur->state == TCP_ESTABLISHED) + cur->state = TCP_FIN_WAIT1; + else // close wait + cur->state = TCP_LAST_ACK; + } + + } else + cur->state = TCP_TIME_WAIT; // Will close socket + } + + if (cur->state == TCP_TIME_WAIT) { + // Log + char dest[20]; + inet_ntop(AF_INET, &(cur->daddr), dest, sizeof(dest)); + ng_log(ANDROID_LOG_INFO, "Close %s/%u lport %u", + dest, ntohs(cur->dest), cur->lport); + + // TODO keep for some time + + // TODO non blocking? + if (close(cur->socket)) + ng_log(ANDROID_LOG_ERROR, "close error %d: %s", errno, strerror(errno)); + + if (last == NULL) + session = cur->next; + else + last->next = cur->next; + + struct session *c = cur; + cur = cur->next; + free(c); + continue; + + } else if (cur->state != TCP_TIME_WAIT) { + if (cur->state == TCP_LISTEN) { + FD_SET(cur->socket, efds); + FD_SET(cur->socket, wfds); + if (cur->socket > max) + max = cur->socket; + } + else if (cur->state == TCP_ESTABLISHED || + cur->state == TCP_SYN_RECV || + cur->state == TCP_CLOSE_WAIT) { + // Check for errors / data + FD_SET(cur->socket, efds); + FD_SET(cur->socket, rfds); + if (cur->socket > max) + max = cur->socket; + } + } + + last = cur; + cur = cur->next; + } + return max; +} + +int check_tun(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) { + // Check tun exception + if (FD_ISSET(args->tun, efds)) { + ng_log(ANDROID_LOG_ERROR, "tun exception"); + return -1; // over and out + } + + // Check tun read + if (FD_ISSET(args->tun, rfds)) { + uint8_t buffer[MAXPKT]; + ssize_t length = read(args->tun, buffer, MAXPKT); + if (length < 0) { + ng_log(ANDROID_LOG_ERROR, "tun read error %d: %s", errno, strerror(errno)); + if (errno == EINTR) + return 0; + else + return -1; // over and out + } + else if (length > 0) { + // Write pcap record + if (pcap_fn != NULL) { + struct timespec ts; + if (clock_gettime(CLOCK_REALTIME, &ts)) + ng_log(ANDROID_LOG_ERROR, "clock_gettime error %d: %s", + errno, strerror(errno)); + + // TODO use stack + int plen = (length < MAXPCAP ? length : MAXPCAP); + struct pcaprec_hdr_s *pcap_rec = + malloc(sizeof(struct pcaprec_hdr_s) + plen); + + pcap_rec->ts_sec = ts.tv_sec; + pcap_rec->ts_usec = ts.tv_nsec / 1000; + pcap_rec->incl_len = plen; + pcap_rec->orig_len = length; + memcpy(((uint8_t *) pcap_rec) + sizeof(struct pcaprec_hdr_s), buffer, plen); + + pcap_write(pcap_rec, sizeof(struct pcaprec_hdr_s) + plen); + + free(pcap_rec); + } + + // Handle IP from tun + handle_ip(args, buffer, length); + } + else { + ng_log(ANDROID_LOG_ERROR, "tun empty read"); + return -1; // over and out + } + } + return 0; +} + void check_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) { struct session *cur = session; while (cur != NULL) { @@ -469,8 +486,7 @@ void check_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_ } } -void handle_ip(JNIEnv *env, jobject instance, const struct arguments *args, - const uint8_t *buffer, const uint16_t length) { +void handle_ip(const struct arguments *args, const uint8_t *buffer, const uint16_t length) { uint8_t protocol; void *saddr; void *daddr; @@ -592,6 +608,8 @@ void handle_ip(JNIEnv *env, jobject instance, const struct arguments *args, // Call back //if ((protocol == IPPROTO_TCP && syn) || protocol == IPPROTO_UDP) { + JNIEnv *env = args->env; + jobject instance = args->instance; if (protocol == IPPROTO_TCP || protocol == IPPROTO_UDP) { jclass cls = (*env)->GetObjectClass(env, instance); jmethodID mid = (*env)->GetMethodID( @@ -625,8 +643,7 @@ void handle_ip(JNIEnv *env, jobject instance, const struct arguments *args, } } -void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args, - const uint8_t *buffer, uint16_t length, int uid) { +void handle_tcp(const struct arguments *args, const uint8_t *buffer, uint16_t length, int uid) { // Check version uint8_t version = (*buffer) >> 4; if (version != 4) @@ -690,7 +707,7 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args, daddr.sin_addr.s_addr = iphdr->daddr; // Open socket - syn->socket = open_socket(env, instance, &daddr); + syn->socket = open_socket(args, &daddr); if (syn->socket < 0) { syn->state = TCP_TIME_WAIT; // Remote will retry @@ -904,7 +921,7 @@ void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args, } } -int open_socket(JNIEnv *env, jobject instance, const struct sockaddr_in *daddr) { +int open_socket(const struct arguments *args, const struct sockaddr_in *daddr) { int sock = -1; // Get TCP socket @@ -915,6 +932,8 @@ int open_socket(JNIEnv *env, jobject instance, const struct sockaddr_in *daddr) } // Protect + JNIEnv *env = args->env; + jobject instance = args->instance; jclass cls = (*env)->GetObjectClass(env, instance); jmethodID mid = (*env)->GetMethodID(env, cls, "protect", "(I)Z"); if (mid == 0) { diff --git a/app/src/main/jni/netguard/netguard.h b/app/src/main/jni/netguard/netguard.h index a75c313e..501d19dc 100644 --- a/app/src/main/jni/netguard/netguard.h +++ b/app/src/main/jni/netguard/netguard.h @@ -1,3 +1,5 @@ +#include + #define TAG "NetGuard.JNI" #define MAXPKT 32768 // TODO TCP parameters (net.inet.tcp.keepinit, etc) @@ -10,6 +12,7 @@ #define MAXPCAP 80 struct arguments { + JNIEnv *env; jobject instance; int tun; }; @@ -57,16 +60,19 @@ typedef struct pcaprec_hdr_s { #define LINKTYPE_RAW 101 -void *handle_events(void *); +void handle_events(void *); + +int get_selects(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds); + +int check_tun(const struct arguments *, fd_set *, fd_set *, fd_set *); void check_sockets(const struct arguments *, fd_set *, fd_set *, fd_set *); -void handle_ip(JNIEnv *, jobject, const struct arguments *, const uint8_t *, const uint16_t); +void handle_ip(const struct arguments *, const uint8_t *, const uint16_t); -void handle_tcp(JNIEnv *, jobject, const struct arguments *args, - const uint8_t *, const uint16_t, int uid); +void handle_tcp(const struct arguments *, const uint8_t *, const uint16_t, int uid); -int open_socket(JNIEnv *, jobject, const struct sockaddr_in *); +int open_socket(const struct arguments *, const struct sockaddr_in *); int get_local_port(const int);