NetGuard/app/src/main/jni/netguard/netguard.c

3492 lines
122 KiB
C
Raw Normal View History

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-09 11:10:11 +00:00
#include <jni.h>
2016-01-13 08:23:21 +00:00
#include <android/log.h>
2016-01-09 20:17:42 +00:00
#include <stdio.h>
2016-01-15 09:08:18 +00:00
#include <stdlib.h>
2016-01-23 11:48:17 +00:00
#include <ctype.h>
2016-01-10 07:14:47 +00:00
#include <time.h>
2016-01-09 15:56:23 +00:00
#include <unistd.h>
2016-01-13 08:23:21 +00:00
#include <pthread.h>
#include <errno.h>
#include <fcntl.h>
#include <sys/socket.h>
2016-01-25 12:58:44 +00:00
#include <dlfcn.h>
2016-01-13 08:23:21 +00:00
2016-01-09 15:56:23 +00:00
#include <arpa/inet.h>
2016-01-09 11:10:11 +00:00
#include <netinet/ip.h>
#include <netinet/ip6.h>
2016-01-09 15:56:23 +00:00
#include <netinet/udp.h>
#include <netinet/tcp.h>
2016-02-02 13:31:44 +00:00
#include <netinet/ip_icmp.h>
#include <netinet/icmp6.h>
2016-01-09 11:10:11 +00:00
2016-01-17 16:41:54 +00:00
#include "netguard.h"
2016-01-28 17:39:13 +00:00
// #define PROFILE_EVENTS 5
// #define PROFILE_UID 5
// #define PROFILE_JNI 5
// TODO TCP options
2016-01-12 20:15:25 +00:00
// TODO TCP fragmentation
2016-01-15 11:57:32 +00:00
2016-01-16 08:07:04 +00:00
// It is assumed that no packets will get lost and that packets arrive in order
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-01-28 17:21:57 +00:00
static JavaVM *jvm = NULL;
static pthread_t thread_id = 0;
static pthread_mutex_t lock;
static jboolean stopping = 0;
static jboolean signaled = 0;
2016-01-22 12:06:50 +00:00
2016-02-02 13:31:44 +00:00
static struct icmp_session *icmp_session = NULL;
2016-01-28 17:21:57 +00:00
static struct udp_session *udp_session = NULL;
static struct tcp_session *tcp_session = NULL;
2016-01-23 14:50:18 +00:00
2016-01-28 17:21:57 +00:00
static int loglevel = 0;
static FILE *pcap_file = NULL;
2016-01-11 22:06:35 +00:00
2016-01-13 08:23:21 +00:00
// JNI
2016-01-11 22:06:35 +00:00
jclass clsPacket;
2016-01-30 08:51:41 +00:00
jclass clsRR;
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-01-30 08:51:41 +00:00
const char *rr = "eu/faircode/netguard/ResourceRecord";
clsRR = jniGlobalRef(env, jniFindClass(env, rr));
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-14 14:02:32 +00:00
JNIEXPORT void JNICALL
Java_eu_faircode_netguard_SinkholeService_jni_1init(JNIEnv *env) {
2016-02-02 13:31:44 +00:00
icmp_session = NULL;
2016-01-20 13:11:04 +00:00
udp_session = NULL;
2016-01-20 11:35:51 +00:00
tcp_session = NULL;
2016-01-22 12:06:50 +00:00
loglevel = ANDROID_LOG_WARN;
pcap_file = NULL;
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-01-28 10:58:39 +00:00
Java_eu_faircode_netguard_SinkholeService_jni_1start(JNIEnv *env, jobject instance,
2016-01-28 11:20:52 +00:00
jint tun, jint loglevel_) {
2016-01-19 19:58:51 +00:00
2016-01-28 11:20:52 +00:00
loglevel = loglevel_;
log_android(ANDROID_LOG_WARN, "Starting tun=%d level %d thread %x", tun, loglevel, thread_id);
2016-01-18 20:54:45 +00:00
// Set blocking
2016-01-23 19:46:27 +00:00
int flags = fcntl(tun, F_GETFL, 0);
2016-01-18 20:54:45 +00:00
if (flags < 0 || fcntl(tun, F_SETFL, flags & ~O_NONBLOCK) < 0)
2016-01-19 08:49:47 +00:00
log_android(ANDROID_LOG_ERROR, "fcntl tun ~O_NONBLOCK error %d: %s",
errno, strerror(errno));
2016-01-12 16:19:27 +00:00
2016-01-28 11:18:13 +00:00
if (thread_id && pthread_kill(thread_id, 0) == 0)
log_android(ANDROID_LOG_ERROR, "Already running thread %x", thread_id);
2016-01-14 06:46:23 +00:00
else {
jint rs = (*env)->GetJavaVM(env, &jvm);
if (rs != JNI_OK)
log_android(ANDROID_LOG_ERROR, "GetJavaVM failed");
2016-01-14 06:46:23 +00:00
2016-01-19 19:58:51 +00:00
// Get arguments
2016-01-14 06:46:23 +00:00
struct arguments *args = malloc(sizeof(struct arguments));
2016-01-24 11:50:40 +00:00
// args->env = will be set in thread
2016-01-14 06:46:23 +00:00
args->instance = (*env)->NewGlobalRef(env, instance);
args->tun = tun;
2016-01-23 11:48:17 +00:00
2016-01-19 19:58:51 +00:00
// Start native thread
int err = pthread_create(&thread_id, NULL, handle_events, (void *) args);
2016-01-17 13:20:07 +00:00
if (err == 0)
log_android(ANDROID_LOG_WARN, "Started thread %x", thread_id);
2016-01-17 13:20:07 +00:00
else
log_android(ANDROID_LOG_ERROR, "pthread_create error %d: %s", err, strerror(err));
2016-01-14 06:46:23 +00:00
}
2016-01-12 12:15:21 +00:00
}
2016-01-09 11:10:11 +00:00
JNIEXPORT void JNICALL
2016-01-17 05:35:26 +00:00
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) {
2016-01-22 09:43:59 +00:00
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-01-14 06:46:23 +00:00
else {
log_android(ANDROID_LOG_WARN, "Join thread %x", t);
int err = pthread_join(t, NULL);
2016-01-14 06:46:23 +00:00
if (err != 0)
log_android(ANDROID_LOG_WARN, "pthread_join error %d: %s", err, strerror(err));
2016-01-14 06:46:23 +00:00
}
2016-01-24 11:50:40 +00:00
2016-01-20 08:24:34 +00:00
if (clear)
clear_sessions();
log_android(ANDROID_LOG_WARN, "Stopped thread %x", t);
} else
log_android(ANDROID_LOG_WARN, "Not running thread %x", t);
2016-01-09 11:10:11 +00:00
}
2016-01-17 09:42:21 +00:00
JNIEXPORT void JNICALL
Java_eu_faircode_netguard_SinkholeService_jni_1done(JNIEnv *env, jobject instance) {
log_android(ANDROID_LOG_INFO, "Done");
2016-01-20 08:24:34 +00:00
clear_sessions();
if (pthread_mutex_destroy(&lock))
log_android(ANDROID_LOG_ERROR, "pthread_mutex_destroy failed");
2016-01-17 09:42:21 +00:00
}
JNIEXPORT void JNICALL
2016-01-31 17:46:44 +00:00
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");
if (name_ == NULL) {
2016-01-22 12:06:50 +00:00
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));
2016-01-22 12:06:50 +00:00
if (fclose(pcap_file))
2016-01-24 11:50:40 +00:00
log_android(ANDROID_LOG_ERROR, "PCAP fclose error %d: %s", errno, strerror(errno));
pcap_file = NULL;
2016-01-22 12:06:50 +00:00
}
log_android(ANDROID_LOG_INFO, "PCAP disabled");
}
else {
const char *name = (*env)->GetStringUTFChars(env, name_, 0);
log_android(ANDROID_LOG_INFO, "PCAP file %s", name);
2016-01-22 12:06:50 +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 {
2016-01-23 19:46:27 +00:00
int flags = fcntl(fileno(pcap_file), F_GETFL, 0);
2016-01-22 12:06:50 +00:00
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-24 11:50:40 +00:00
2016-01-31 17:46:44 +00:00
if (ftell(pcap_file) == 0) {
log_android(ANDROID_LOG_INFO, "Initializing PCAP");
2016-01-25 08:37:58 +00:00
write_pcap_hdr();
2016-01-31 17:46:44 +00:00
}
2016-01-22 12:06:50 +00:00
}
(*env)->ReleaseStringUTFChars(env, name_, name);
}
if (pthread_mutex_unlock(&lock))
log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed");
}
2016-01-20 08:24:34 +00:00
2016-01-25 12:58:44 +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);
char value[250];
__system_property_get(env, name, value);
2016-01-25 12:58:44 +00:00
(*env)->ReleaseStringUTFChars(env, name_, name);
return (*env)->NewStringUTF(env, value);
}
2016-01-11 22:06:35 +00:00
// Private functions
2016-01-20 08:24:34 +00:00
void clear_sessions() {
2016-02-02 13:31:44 +00:00
struct icmp_session *i = icmp_session;
while (i != NULL) {
if (i->socket >= 0 && close(i->socket))
log_android(ANDROID_LOG_ERROR, "ICMP close %d error %d: %s",
i->socket, errno, strerror(errno));
struct icmp_session *p = i;
i = i->next;
free(p);
}
icmp_session = NULL;
2016-01-20 13:11:04 +00:00
struct udp_session *u = udp_session;
while (u != NULL) {
2016-01-25 08:27:12 +00:00
if (u->socket >= 0 && close(u->socket))
log_android(ANDROID_LOG_ERROR, "UDP close %d error %d: %s",
u->socket, errno, strerror(errno));
2016-01-20 13:11:04 +00:00
struct udp_session *p = u;
u = u->next;
free(p);
}
udp_session = NULL;
struct tcp_session *t = tcp_session;
while (t != NULL) {
2016-01-25 08:27:12 +00:00
if (t->socket >= 0 && close(t->socket))
log_android(ANDROID_LOG_ERROR, "TCP close %d error %d: %s",
u->socket, errno, strerror(errno));
2016-01-20 13:11:04 +00:00
struct tcp_session *p = t;
t = t->next;
2016-02-05 09:43:28 +00:00
clear_tcp_data(p);
2016-01-20 08:24:34 +00:00
free(p);
}
2016-01-20 11:35:51 +00:00
tcp_session = NULL;
2016-01-20 08:24:34 +00:00
}
2016-02-05 09:43:28 +00:00
void clear_tcp_data(struct tcp_session *cur) {
struct segment *s = cur->data_rx;
while (s != NULL) {
struct segment *p = s;
s = s->next;
free(p->data);
free(p);
}
}
void handle_signal(int sig, siginfo_t *info, void *context) {
log_android(ANDROID_LOG_DEBUG, "Signal %d", sig);
2016-01-14 06:46:23 +00:00
signaled = 1;
}
2016-01-23 19:46:27 +00:00
void *handle_events(void *a) {
int sdk;
2016-01-13 08:23:21 +00:00
fd_set rfds;
fd_set wfds;
fd_set efds;
struct timespec ts;
sigset_t blockset;
sigset_t emptyset;
struct sigaction sa;
2016-01-18 11:19:40 +00:00
struct arguments *args = (struct arguments *) a;
log_android(ANDROID_LOG_WARN, "Start events tun=%d thread %x", args->tun, thread_id);
2016-01-18 11:19:40 +00:00
// Attach to Java
JNIEnv *env;
jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
if (rs != JNI_OK) {
log_android(ANDROID_LOG_ERROR, "AttachCurrentThread failed");
2016-01-23 19:46:27 +00:00
return NULL;
2016-01-18 11:19:40 +00:00
}
args->env = env;
// Get SDK version
sdk = sdk_int(env);
// Block SIGUSR1
sigemptyset(&blockset);
sigaddset(&blockset, SIGUSR1);
sigprocmask(SIG_BLOCK, &blockset, NULL);
/// Handle SIGUSR1
sa.sa_sigaction = handle_signal;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
2016-01-28 10:58:39 +00:00
// Terminate existing sessions not allowed anymore
check_allowed(args);
2016-01-22 09:43:59 +00:00
stopping = 0;
2016-01-14 06:46:23 +00:00
signaled = 0;
// Loop
while (!stopping) {
2016-01-28 11:18:13 +00:00
log_android(ANDROID_LOG_DEBUG, "Loop thread %x", thread_id);
2016-01-13 08:23:21 +00:00
2016-01-21 11:04:41 +00:00
// Check sessions
2016-02-02 17:15:20 +00:00
int sessions = check_sessions(args);
// https://bugzilla.mozilla.org/show_bug.cgi?id=1093893
int idle = (sessions == 0 && sdk >= 16);
log_android(ANDROID_LOG_DEBUG, "sessions %d idle %d sdk %d", sessions, idle, sdk);
2016-01-21 11:04:41 +00:00
2016-01-13 08:23:21 +00:00
// Select
ts.tv_sec = SELECT_TIMEOUT;
ts.tv_nsec = 0;
sigemptyset(&emptyset);
2016-01-20 13:11:04 +00:00
int max = get_selects(args, &rfds, &wfds, &efds);
int ready = pselect(max + 1, &rfds, &wfds, &efds, idle ? NULL : &ts, &emptyset);
2016-01-13 08:23:21 +00:00
if (ready < 0) {
if (errno == EINTR) {
2016-01-22 09:43:59 +00:00
if (stopping && signaled) { ;
log_android(ANDROID_LOG_WARN,
"pselect signaled tun %d thread %x", args->tun, thread_id);
2016-01-24 11:50:40 +00:00
report_exit(args, NULL);
2016-01-14 06:46:23 +00:00
break;
} else {
2016-01-25 08:27:12 +00:00
// TODO check if SIGUSR1 is free
log_android(ANDROID_LOG_DEBUG,
"pselect interrupted tun %d thread %x", args->tun, thread_id);
2016-01-14 06:46:23 +00:00
continue;
}
} else {
log_android(ANDROID_LOG_ERROR,
"pselect tun %d thread %x error %d: %s",
args->tun, thread_id, errno, strerror(errno));
report_exit(args, "pselect tun %d thread %x error %d: %s",
args->tun, thread_id, errno, strerror(errno));
2016-01-14 06:46:23 +00:00
break;
}
2016-01-13 08:23:21 +00:00
}
if (ready == 0)
2016-02-02 17:15:20 +00:00
log_android(ANDROID_LOG_DEBUG, "pselect timeout");
2016-01-13 08:23:21 +00:00
else {
2016-01-28 17:39:13 +00:00
log_android(ANDROID_LOG_DEBUG, "pselect ready %d", ready);
if (pthread_mutex_lock(&lock))
log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed");
2016-01-28 17:39:13 +00:00
#ifdef PROFILE_EVENTS
struct timeval start, end;
float mselapsed;
gettimeofday(&start, NULL);
#endif
2016-01-18 11:19:40 +00:00
// Check upstream
2016-01-29 06:59:35 +00:00
int error = 0;
2016-01-20 13:11:04 +00:00
if (check_tun(args, &rfds, &wfds, &efds) < 0)
2016-01-26 07:05:42 +00:00
error = 1;
else {
2016-01-28 17:39:13 +00:00
#ifdef PROFILE_EVENTS
2016-01-26 07:05:42 +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_EVENTS)
log_android(ANDROID_LOG_WARN, "tun %f", mselapsed);
2016-01-26 07:05:42 +00:00
gettimeofday(&start, NULL);
#endif
2016-02-02 13:31:44 +00:00
// Check ICMP downstream
check_icmp_sockets(args, &rfds, &wfds, &efds);
2016-01-26 07:05:42 +00:00
// Check UDP downstream
check_udp_sockets(args, &rfds, &wfds, &efds);
2016-01-20 08:24:34 +00:00
2016-01-26 07:05:42 +00:00
// Check TCP downstream
check_tcp_sockets(args, &rfds, &wfds, &efds);
}
2016-01-25 18:23:41 +00:00
if (pthread_mutex_unlock(&lock))
log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed");
2016-01-26 07:05:42 +00:00
if (error)
break;
2016-01-28 17:39:13 +00:00
#ifdef PROFILE_EVENTS
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_EVENTS)
log_android(ANDROID_LOG_WARN, "sockets %f", mselapsed);
#endif
2016-01-20 08:24:34 +00:00
}
2016-01-18 11:19:40 +00:00
}
2016-01-17 09:42:21 +00:00
2016-01-18 11:19:40 +00:00
(*env)->DeleteGlobalRef(env, args->instance);
2016-01-21 11:55:08 +00:00
// Detach from Java
2016-01-18 11:19:40 +00:00
rs = (*jvm)->DetachCurrentThread(jvm);
if (rs != JNI_OK)
log_android(ANDROID_LOG_ERROR, "DetachCurrentThread failed");
2016-01-18 11:19:40 +00:00
2016-01-21 11:55:08 +00:00
// Cleanup
free(args);
2016-01-28 11:18:13 +00:00
log_android(ANDROID_LOG_WARN, "Stopped events tun=%d thread %x", args->tun, thread_id);
thread_id = 0;
2016-01-23 19:46:27 +00:00
return NULL;
2016-01-21 11:55:08 +00:00
}
2016-01-25 08:27:12 +00:00
void report_exit(const struct arguments *args, const char *fmt, ...) {
2016-01-22 15:41:27 +00:00
jclass cls = (*args->env)->GetObjectClass(args->env, args->instance);
2016-01-24 11:50:40 +00:00
jmethodID mid = jniGetMethodID(args->env, cls, "nativeExit", "(Ljava/lang/String;)V");
2016-01-25 08:27:12 +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-22 15:41:27 +00:00
2016-01-24 11:50:40 +00:00
(*args->env)->CallVoidMethod(args->env, args->instance, mid, jreason);
2016-01-22 15:41:27 +00:00
jniCheckException(args->env);
2016-01-24 11:50:40 +00:00
if (jreason != NULL)
(*args->env)->DeleteLocalRef(args->env, jreason);
2016-01-22 15:41:27 +00:00
(*args->env)->DeleteLocalRef(args->env, cls);
2016-01-18 11:19:40 +00:00
}
2016-01-17 09:42:21 +00:00
2016-01-28 10:58:39 +00:00
void check_allowed(const struct arguments *args) {
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
2016-02-02 13:31:44 +00:00
struct icmp_session *i = icmp_session;
while (i != NULL) {
if (!i->stop) {
if (i->version == 4) {
inet_ntop(AF_INET, &i->saddr.ip4, source, sizeof(source));
inet_ntop(AF_INET, &i->daddr.ip4, dest, sizeof(dest));
}
else {
inet_ntop(AF_INET6, &i->saddr.ip6, source, sizeof(source));
inet_ntop(AF_INET6, &i->daddr.ip6, dest, sizeof(dest));
}
jobject objPacket = create_packet(
args, i->version, IPPROTO_ICMP, "",
source, 0, dest, 0, "", i->uid, 0);
if (!is_address_allowed(args, objPacket)) {
i->stop = 1;
log_android(ANDROID_LOG_WARN, "ICMP terminate %d uid %d", i->socket, i->uid);
}
}
i = i->next;
}
2016-02-04 09:09:31 +00:00
struct udp_session *l = NULL;
2016-01-28 10:58:39 +00:00
struct udp_session *u = udp_session;
while (u != NULL) {
2016-02-04 09:09:31 +00:00
if (u->state == UDP_ACTIVE) {
2016-01-28 10:58:39 +00:00
if (u->version == 4) {
inet_ntop(AF_INET, &u->saddr.ip4, source, sizeof(source));
inet_ntop(AF_INET, &u->daddr.ip4, dest, sizeof(dest));
}
else {
inet_ntop(AF_INET6, &u->saddr.ip6, source, sizeof(source));
inet_ntop(AF_INET6, &u->daddr.ip6, dest, sizeof(dest));
}
jobject objPacket = create_packet(
args, u->version, IPPROTO_UDP, "",
2016-01-28 14:19:58 +00:00
source, ntohs(u->source), dest, ntohs(u->dest), "", u->uid, 0);
2016-01-28 10:58:39 +00:00
if (!is_address_allowed(args, objPacket)) {
2016-02-04 09:09:31 +00:00
u->state = UDP_FINISHING;
log_android(ANDROID_LOG_WARN, "UDP terminate session socket %d uid %d", u->socket,
u->uid);
2016-01-28 10:58:39 +00:00
}
}
2016-02-04 09:09:31 +00:00
else if (u->state == UDP_BLOCKED) {
log_android(ANDROID_LOG_WARN, "UDP remove blocked session uid %d", u->uid);
if (l == NULL)
udp_session = u->next;
else
l->next = u->next;
struct udp_session *c = u;
u = u->next;
free(c);
continue;
}
l = u;
2016-01-28 10:58:39 +00:00
u = u->next;
}
struct tcp_session *t = tcp_session;
while (t != NULL) {
if (t->state != TCP_TIME_WAIT && t->state != TCP_CLOSE) {
if (t->version == 4) {
inet_ntop(AF_INET, &t->saddr.ip4, source, sizeof(source));
inet_ntop(AF_INET, &t->daddr.ip4, dest, sizeof(dest));
}
else {
inet_ntop(AF_INET6, &t->saddr.ip6, source, sizeof(source));
inet_ntop(AF_INET6, &t->daddr.ip6, dest, sizeof(dest));
}
jobject objPacket = create_packet(
args, t->version, IPPROTO_TCP, "",
2016-01-28 14:19:58 +00:00
source, ntohs(t->source), dest, ntohs(t->dest), "", t->uid, 0);
2016-01-28 10:58:39 +00:00
if (!is_address_allowed(args, objPacket)) {
t->state = TCP_TIME_WAIT;
log_android(ANDROID_LOG_WARN, "TCP terminate socket %d uid %d", t->socket, t->uid);
}
}
t = t->next;
}
}
2016-02-02 17:15:20 +00:00
int check_sessions(const struct arguments *args) {
int count = 0;
2016-01-18 11:19:40 +00:00
time_t now = time(NULL);
2016-01-17 09:42:21 +00:00
2016-02-02 13:31:44 +00:00
// Check ICMP sessions
struct icmp_session *il = NULL;
struct icmp_session *i = icmp_session;
while (i != NULL) {
int timeout = ICMP_TIMEOUT;
if (i->stop || i->time + timeout < now) {
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
if (i->version == 4) {
inet_ntop(AF_INET, &i->saddr.ip4, source, sizeof(source));
inet_ntop(AF_INET, &i->daddr.ip4, dest, sizeof(dest));
}
else {
inet_ntop(AF_INET6, &i->saddr.ip6, source, sizeof(source));
inet_ntop(AF_INET6, &i->daddr.ip6, dest, sizeof(dest));
}
log_android(ANDROID_LOG_INFO, "ICMP idle %d/%d sec stop %d from %s to %s",
now - i->time, timeout, i->stop, dest, source);
if (close(i->socket))
log_android(ANDROID_LOG_ERROR, "ICMP close %d error %d: %s",
i->socket, errno, strerror(errno));
i->socket = -1;
if (il == NULL)
icmp_session = i->next;
else
il->next = i->next;
struct icmp_session *c = i;
i = i->next;
free(c);
}
else {
2016-02-02 17:15:20 +00:00
count++;
2016-02-02 13:31:44 +00:00
il = i;
i = i->next;
}
}
2016-01-21 11:04:41 +00:00
// Check UDP sessions
2016-01-20 13:11:04 +00:00
struct udp_session *ul = NULL;
struct udp_session *u = udp_session;
while (u != NULL) {
2016-02-04 09:09:31 +00:00
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
if (u->version == 4) {
inet_ntop(AF_INET, &u->saddr.ip4, source, sizeof(source));
inet_ntop(AF_INET, &u->daddr.ip4, dest, sizeof(dest));
}
else {
inet_ntop(AF_INET6, &u->saddr.ip6, source, sizeof(source));
inet_ntop(AF_INET6, &u->daddr.ip6, dest, sizeof(dest));
}
// Check session timeout
int timeout = (ntohs(u->dest) == 53 ? UDP_TIMEOUT_53 : UDP_TIMEOUT_ANY);
if (u->state == UDP_ACTIVE && u->time + timeout < now) {
log_android(ANDROID_LOG_INFO, "UDP idle %d/%d sec state %d from %s/%u to %s/%u",
now - u->time, timeout, u->state,
source, ntohs(u->source), dest, ntohs(u->dest));
u->state = UDP_FINISHING;
}
// Check finished sessions
if (u->state == UDP_FINISHING) {
log_android(ANDROID_LOG_INFO, "UDP close from %s/%u to %s/%u socket %d",
source, ntohs(u->source), dest, ntohs(u->dest), u->socket);
2016-01-20 08:24:34 +00:00
2016-01-20 13:11:04 +00:00
if (close(u->socket))
log_android(ANDROID_LOG_ERROR, "UDP close %d error %d: %s",
u->socket, errno, strerror(errno));
2016-01-25 08:27:12 +00:00
u->socket = -1;
2016-01-20 13:11:04 +00:00
2016-02-04 09:09:31 +00:00
u->time = time(NULL);
u->state = UDP_CLOSED;
}
// Cleanup lingering sessions
if ((u->state == UDP_CLOSED || u->state == UDP_BLOCKED) &&
u->time + UDP_KEEP_TIMEOUT < now) {
2016-01-20 13:11:04 +00:00
if (ul == NULL)
udp_session = u->next;
else
ul->next = u->next;
struct udp_session *c = u;
u = u->next;
free(c);
}
2016-01-21 11:04:41 +00:00
else {
2016-02-02 17:15:20 +00:00
count++;
2016-01-21 11:04:41 +00:00
ul = u;
u = u->next;
}
2016-01-20 13:11:04 +00:00
}
2016-01-21 11:04:41 +00:00
// Check TCP sessions
2016-01-20 13:11:04 +00:00
struct tcp_session *tl = NULL;
struct tcp_session *t = tcp_session;
while (t != NULL) {
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
2016-01-25 12:28:52 +00:00
if (t->version == 4) {
inet_ntop(AF_INET, &t->saddr.ip4, source, sizeof(source));
inet_ntop(AF_INET, &t->daddr.ip4, dest, sizeof(dest));
} else {
inet_ntop(AF_INET6, &t->saddr.ip6, source, sizeof(source));
inet_ntop(AF_INET6, &t->daddr.ip6, dest, sizeof(dest));
}
2016-01-21 11:04:41 +00:00
2016-02-04 09:09:31 +00:00
// Check session timeout
int timeout = 0;
2016-01-20 13:11:04 +00:00
if (t->state == TCP_LISTEN || t->state == TCP_SYN_RECV)
timeout = TCP_INIT_TIMEOUT;
2016-01-20 13:11:04 +00:00
else if (t->state == TCP_ESTABLISHED)
timeout = TCP_IDLE_TIMEOUT;
else
timeout = TCP_CLOSE_TIMEOUT;
2016-01-21 11:04:41 +00:00
if (t->state != TCP_TIME_WAIT && t->state != TCP_CLOSE && t->time + timeout < now) {
2016-01-18 11:19:40 +00:00
// TODO send keep alives?
2016-02-04 09:09:31 +00:00
log_android(ANDROID_LOG_WARN, "TCP idle %d/%d sec from %s/%u to %s/%u state %s",
2016-01-24 13:10:02 +00:00
now - t->time, timeout,
2016-01-24 12:39:04 +00:00
source, ntohs(t->source), dest, ntohs(t->dest), strstate(t->state));
2016-01-12 08:45:58 +00:00
2016-01-24 11:50:40 +00:00
write_rst(args, t);
2016-01-18 11:19:40 +00:00
}
2016-02-04 09:09:31 +00:00
// Check finished sessions
2016-01-20 13:11:04 +00:00
if (t->state == TCP_TIME_WAIT) {
2016-02-04 09:09:31 +00:00
log_android(ANDROID_LOG_INFO, "TCP close from %s/%u to %s/%u socket %d",
2016-01-21 11:04:41 +00:00
source, ntohs(t->source), dest, ntohs(t->dest), t->socket);
2016-01-18 11:19:40 +00:00
2016-01-20 13:11:04 +00:00
if (close(t->socket))
log_android(ANDROID_LOG_ERROR, "close %d error %d: %s",
t->socket, errno, strerror(errno));
2016-01-25 08:27:12 +00:00
t->socket = -1;
2016-01-18 11:19:40 +00:00
2016-01-21 11:04:41 +00:00
t->time = time(NULL);
t->state = TCP_CLOSE;
}
// Cleanup lingering sessions
if (t->state == TCP_CLOSE && t->time + TCP_KEEP_TIMEOUT < now) {
2016-01-20 13:11:04 +00:00
if (tl == NULL)
tcp_session = t->next;
2016-01-18 11:19:40 +00:00
else
2016-01-20 13:11:04 +00:00
tl->next = t->next;
2016-01-18 11:19:40 +00:00
2016-01-20 13:11:04 +00:00
struct tcp_session *c = t;
t = t->next;
2016-02-05 09:43:28 +00:00
clear_tcp_data(c);
2016-01-18 11:19:40 +00:00
free(c);
2016-01-21 11:04:41 +00:00
}
else {
2016-02-02 17:15:20 +00:00
count++;
2016-01-21 11:04:41 +00:00
tl = t;
t = t->next;
}
}
2016-02-02 17:15:20 +00:00
return count;
2016-01-21 11:04:41 +00:00
}
int get_selects(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) {
// Initialize
FD_ZERO(rfds);
FD_ZERO(wfds);
FD_ZERO(efds);
2016-01-18 11:19:40 +00:00
2016-01-21 11:04:41 +00:00
// Always select tun
FD_SET(args->tun, rfds);
FD_SET(args->tun, efds);
int max = args->tun;
2016-02-02 13:31:44 +00:00
// Select ICMP sockets
struct icmp_session *i = icmp_session;
while (i != NULL) {
if (!i->stop) {
FD_SET(i->socket, efds);
FD_SET(i->socket, rfds);
if (i->socket > max)
max = i->socket;
}
i = i->next;
}
2016-01-21 11:04:41 +00:00
// Select UDP sockets
struct udp_session *u = udp_session;
while (u != NULL) {
2016-02-04 09:09:31 +00:00
if (u->state == UDP_ACTIVE) {
2016-01-21 16:24:23 +00:00
FD_SET(u->socket, efds);
FD_SET(u->socket, rfds);
if (u->socket > max)
max = u->socket;
}
2016-01-21 11:04:41 +00:00
u = u->next;
}
// Select TCP sockets
struct tcp_session *t = tcp_session;
while (t != NULL) {
// Select sockets
if (t->state == TCP_LISTEN) {
2016-01-18 14:29:01 +00:00
// Check for connected / errors
2016-01-20 13:11:04 +00:00
FD_SET(t->socket, efds);
FD_SET(t->socket, wfds);
if (t->socket > max)
max = t->socket;
2016-01-18 14:29:01 +00:00
}
2016-01-20 13:11:04 +00:00
else if (t->state == TCP_ESTABLISHED ||
t->state == TCP_SYN_RECV ||
t->state == TCP_CLOSE_WAIT) {
2016-01-18 14:29:01 +00:00
// Check for data / errors
2016-01-20 13:11:04 +00:00
FD_SET(t->socket, efds);
2016-01-22 18:03:32 +00:00
if (t->send_window > 0)
FD_SET(t->socket, rfds);
2016-01-20 13:11:04 +00:00
if (t->socket > max)
max = t->socket;
2016-01-11 22:06:35 +00:00
}
2016-01-18 11:19:40 +00:00
2016-01-20 13:11:04 +00:00
t = t->next;
2016-01-11 22:06:35 +00:00
}
2016-01-18 14:29:01 +00:00
2016-01-18 11:19:40 +00:00
return max;
}
2016-01-12 16:19:27 +00:00
2016-01-25 08:27:12 +00:00
int check_tun(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) {
2016-01-18 14:29:01 +00:00
// Check tun error
2016-01-18 11:19:40 +00:00
if (FD_ISSET(args->tun, efds)) {
2016-01-25 08:27:12 +00:00
log_android(ANDROID_LOG_ERROR, "tun %d exception", args->tun);
if (fcntl(args->tun, F_GETFL) < 0) {
log_android(ANDROID_LOG_ERROR, "fcntl tun %d F_GETFL error %d: %s",
args->tun, errno, strerror(errno));
report_exit(args, "fcntl tun %d F_GETFL error %d: %s",
args->tun, errno, strerror(errno));
} else
report_exit(args, "tun %d exception", args->tun);
2016-01-24 11:50:40 +00:00
return -1;
2016-01-18 11:19:40 +00:00
}
2016-01-13 08:23:21 +00:00
2016-01-18 11:19:40 +00:00
// Check tun read
if (FD_ISSET(args->tun, rfds)) {
2016-02-03 06:58:25 +00:00
uint8_t *buffer = malloc(TUN_MAXMSG);
ssize_t length = read(args->tun, buffer, TUN_MAXMSG);
2016-01-18 11:19:40 +00:00
if (length < 0) {
2016-02-03 06:58:25 +00:00
free(buffer);
2016-01-25 08:27:12 +00:00
log_android(ANDROID_LOG_ERROR, "tun read error %d: %s", errno, strerror(errno));
2016-02-02 13:31:44 +00:00
if (errno == EINTR || errno == EAGAIN)
2016-01-24 11:50:40 +00:00
return 0;
else {
2016-01-25 08:27:12 +00:00
report_exit(args, "tun read error %d: %s", errno, strerror(errno));
2016-01-24 11:50:40 +00:00
return -1;
}
2016-01-18 11:19:40 +00:00
}
else if (length > 0) {
// Write pcap record
2016-01-22 12:06:50 +00:00
if (pcap_file != NULL)
2016-01-23 19:46:27 +00:00
write_pcap_rec(buffer, (size_t) length);
2016-01-18 11:19:40 +00:00
// Handle IP from tun
2016-01-23 19:46:27 +00:00
handle_ip(args, buffer, (size_t) length);
2016-02-03 06:58:25 +00:00
free(buffer);
2016-01-18 11:19:40 +00:00
}
else {
2016-01-18 14:29:01 +00:00
// tun eof
2016-02-03 06:58:25 +00:00
free(buffer);
2016-01-25 08:27:12 +00:00
log_android(ANDROID_LOG_ERROR, "tun %d empty read", args->tun);
report_exit(args, "tun %d empty read", args->tun);
2016-01-18 14:29:01 +00:00
return -1;
2016-01-18 11:19:40 +00:00
}
}
2016-01-18 14:29:01 +00:00
2016-01-18 11:19:40 +00:00
return 0;
2016-01-11 22:06:35 +00:00
}
2016-02-02 13:31:44 +00:00
void check_icmp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) {
struct icmp_session *cur = icmp_session;
while (cur != NULL) {
if (cur->socket >= 0) {
// Check socket error
if (FD_ISSET(cur->socket, efds)) {
cur->time = time(NULL);
int serr = 0;
socklen_t optlen = sizeof(int);
int err = getsockopt(cur->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen);
if (err < 0)
log_android(ANDROID_LOG_ERROR, "ICMP getsockopt error %d: %s",
errno, strerror(errno));
else if (serr)
log_android(ANDROID_LOG_ERROR, "ICMP SO_ERROR %d: %s", serr, strerror(serr));
cur->stop = 1;
}
else {
// Check socket read
if (FD_ISSET(cur->socket, rfds)) {
cur->time = time(NULL);
2016-02-03 17:32:25 +00:00
uint16_t blen = (uint16_t) (cur->version == 4 ? ICMP4_MAXMSG : ICMP6_MAXMSG);
2016-02-03 06:58:25 +00:00
uint8_t *buffer = malloc(blen);
ssize_t bytes = recv(cur->socket, buffer, blen, 0);
2016-02-02 13:31:44 +00:00
if (bytes < 0) {
// Socket error
log_android(ANDROID_LOG_WARN, "ICMP recv error %d: %s",
errno, strerror(errno));
if (errno != EINTR && errno != EAGAIN)
cur->stop = 1;
}
else if (bytes == 0) {
// Socket eof
log_android(ANDROID_LOG_WARN, "ICMP recv empty");
cur->stop = 1;
} else {
// Socket read data
char dest[INET6_ADDRSTRLEN + 1];
if (cur->version == 4)
inet_ntop(AF_INET, &cur->daddr.ip4, dest, sizeof(dest));
else
inet_ntop(AF_INET6, &cur->daddr.ip6, dest, sizeof(dest));
2016-02-02 14:11:09 +00:00
// cur->id should be equal to icmp->icmp_id
// but for some unexplained reason this is not the case
// some bits seems to be set extra
2016-02-02 13:31:44 +00:00
struct icmp *icmp = (struct icmp *) buffer;
log_android(cur->id == icmp->icmp_id ? ANDROID_LOG_INFO : ANDROID_LOG_WARN,
"ICMP recv bytes %d from %s for tun type %d code %d id %x/%x seq %d",
bytes, dest,
icmp->icmp_type, icmp->icmp_code,
cur->id, icmp->icmp_id, icmp->icmp_seq);
// restore original ID
icmp->icmp_id = cur->id;
2016-02-02 14:11:09 +00:00
uint16_t csum = 0;
if (cur->version == 6) {
// Untested
struct ip6_hdr_pseudo pseudo;
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo));
memcpy(&pseudo.ip6ph_src, &cur->daddr.ip6, 16);
memcpy(&pseudo.ip6ph_dst, &cur->saddr.ip6, 16);
pseudo.ip6ph_len = bytes - sizeof(struct ip6_hdr);
pseudo.ip6ph_nxt = IPPROTO_ICMPV6;
csum = calc_checksum(
0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo));
}
2016-02-02 13:31:44 +00:00
icmp->icmp_cksum = 0;
2016-02-03 17:32:25 +00:00
icmp->icmp_cksum = ~calc_checksum(csum, buffer, (size_t) bytes);
2016-02-02 13:31:44 +00:00
// Forward to tun
if (write_icmp(args, cur, buffer, (size_t) bytes) < 0)
cur->stop = 1;
}
2016-02-03 06:58:25 +00:00
free(buffer);
2016-02-02 13:31:44 +00:00
}
}
}
cur = cur->next;
}
}
2016-01-20 13:11:04 +00:00
void check_udp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) {
struct udp_session *cur = udp_session;
while (cur != NULL) {
2016-01-25 08:27:12 +00:00
if (cur->socket >= 0) {
// Check socket error
if (FD_ISSET(cur->socket, efds)) {
cur->time = time(NULL);
2016-01-20 11:14:15 +00:00
2016-01-25 08:27:12 +00:00
int serr = 0;
socklen_t optlen = sizeof(int);
int err = getsockopt(cur->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen);
if (err < 0)
log_android(ANDROID_LOG_ERROR, "UDP getsockopt error %d: %s",
errno, strerror(errno));
else if (serr)
log_android(ANDROID_LOG_ERROR, "UDP SO_ERROR %d: %s", serr, strerror(serr));
2016-01-20 13:11:04 +00:00
2016-02-04 09:09:31 +00:00
cur->state = UDP_FINISHING;
2016-01-25 08:27:12 +00:00
}
else {
// Check socket read
if (FD_ISSET(cur->socket, rfds)) {
cur->time = time(NULL);
2016-01-20 08:24:34 +00:00
2016-02-03 17:32:25 +00:00
uint16_t blen = (uint16_t) (cur->version == 4 ? UDP4_MAXMSG : UDP6_MAXMSG);
2016-02-03 06:58:25 +00:00
uint8_t *buffer = malloc(blen);
ssize_t bytes = recv(cur->socket, buffer, blen, 0);
2016-01-25 08:27:12 +00:00
if (bytes < 0) {
// Socket error
2016-01-26 19:52:38 +00:00
log_android(ANDROID_LOG_WARN, "UDP recv error %d: %s",
2016-01-25 08:27:12 +00:00
errno, strerror(errno));
2016-01-20 13:11:04 +00:00
2016-02-02 13:31:44 +00:00
if (errno != EINTR && errno != EAGAIN)
2016-02-04 09:09:31 +00:00
cur->state = UDP_FINISHING;
2016-01-25 08:27:12 +00:00
}
else if (bytes == 0) {
// Socket eof
log_android(ANDROID_LOG_WARN, "UDP recv empty");
2016-02-04 09:09:31 +00:00
cur->state = UDP_FINISHING;
2016-01-20 13:11:04 +00:00
2016-01-25 08:27:12 +00:00
} else {
// Socket read data
char dest[INET6_ADDRSTRLEN + 1];
if (cur->version == 4)
inet_ntop(AF_INET, &cur->daddr.ip4, dest, sizeof(dest));
else
inet_ntop(AF_INET6, &cur->daddr.ip6, dest, sizeof(dest));
2016-02-02 13:31:44 +00:00
log_android(ANDROID_LOG_INFO, "UDP recv bytes %d from %s/%u for tun",
2016-01-25 08:27:12 +00:00
bytes, dest, ntohs(cur->dest));
2016-01-29 18:10:23 +00:00
// Process DNS response
if (ntohs(cur->dest) == 53)
parse_dns_response(args, buffer, (size_t) bytes);
// Forward to tun
2016-01-25 08:27:12 +00:00
if (write_udp(args, cur, buffer, (size_t) bytes) < 0)
2016-02-04 09:09:31 +00:00
cur->state = UDP_FINISHING;
2016-01-25 08:27:12 +00:00
else {
// Prevent too many open files
if (ntohs(cur->dest) == 53)
2016-02-04 09:09:31 +00:00
cur->state = UDP_FINISHING;
2016-01-25 08:27:12 +00:00
}
2016-01-24 12:39:04 +00:00
}
2016-02-03 06:58:25 +00:00
free(buffer);
2016-01-20 11:14:15 +00:00
}
}
}
2016-01-20 13:11:04 +00:00
cur = cur->next;
2016-01-20 08:24:34 +00:00
}
}
2016-01-31 15:38:38 +00:00
int32_t get_qname(const uint8_t *data, const size_t datalen, uint16_t off, char *qname) {
2016-01-29 18:10:23 +00:00
*qname = 0;
uint16_t c = 0;
uint8_t noff = 0;
uint16_t ptr = off;
uint8_t len = *(data + ptr);
uint8_t parts = 0;
while (len && parts < 10) {
parts++;
if (len & 0xC0) {
2016-01-31 15:38:38 +00:00
ptr = (uint16_t) ((len & 0x3F) * 256 + *(data + ptr + 1));
2016-01-29 18:10:23 +00:00
len = *(data + ptr);
log_android(ANDROID_LOG_DEBUG, "DNS qname compression ptr %d len %d", ptr, len);
if (!c) {
c = 1;
off += 2;
}
}
else if (ptr + 1 + len <= datalen) {
memcpy(qname + noff, data + ptr + 1, len);
*(qname + noff + len) = '.';
noff += (len + 1);
ptr += (len + 1);
len = *(data + ptr);
}
else
break;
}
ptr++;
if (len > 0 || noff == 0) {
log_android(ANDROID_LOG_ERROR, "DNS qname invalid len %d noff %d part %d", len, noff,
parts);
return -1;
}
*(qname + noff - 1) = 0;
return (c ? off : ptr);
}
void parse_dns_response(const struct arguments *args, const uint8_t *data, const size_t datalen) {
if (datalen < sizeof(struct dns_header) + 1) {
log_android(ANDROID_LOG_WARN, "DNS response length %d", datalen);
return;
}
// Check if standard DNS query
// TODO multiple qnames
const struct dns_header *dns = (struct dns_header *) data;
int qcount = ntohs(dns->q_count);
int acount = ntohs(dns->ans_count);
2016-01-29 19:30:52 +00:00
if (dns->qr == 1 && dns->opcode == 0 && qcount > 0 && acount > 0) {
2016-01-30 08:51:41 +00:00
log_android(ANDROID_LOG_DEBUG, "DNS response qcount %d acount %d", qcount, acount);
if (qcount > 1)
log_android(ANDROID_LOG_WARN, "DNS response qcount %d acount %d", qcount, acount);
2016-01-29 18:10:23 +00:00
// http://tools.ietf.org/html/rfc1035
2016-01-29 19:30:52 +00:00
char qname[DNS_QNAME_MAX + 1];
2016-01-30 08:51:41 +00:00
char name[DNS_QNAME_MAX + 1];
2016-01-31 15:38:38 +00:00
int32_t off = sizeof(struct dns_header);
2016-01-29 18:10:23 +00:00
for (int q = 0; q < qcount; q++) {
2016-01-31 15:38:38 +00:00
off = get_qname(data, datalen, (uint16_t) off, name);
2016-01-29 18:10:23 +00:00
if (off > 0 && off + 4 <= datalen) {
uint16_t qtype = ntohs(*((uint16_t *) (data + off)));
uint16_t qclass = ntohs(*((uint16_t *) (data + off + 2)));
2016-01-30 08:51:41 +00:00
log_android(ANDROID_LOG_DEBUG,
2016-01-29 18:10:23 +00:00
"DNS question %d qtype %d qclass %d qname %s",
2016-01-30 08:51:41 +00:00
q, qtype, qclass, name);
2016-01-29 18:10:23 +00:00
off += 4;
2016-01-29 19:30:52 +00:00
// TODO multiple qnames?
if (q == 0)
2016-01-30 08:51:41 +00:00
strcpy(qname, name);
2016-01-29 18:10:23 +00:00
}
else {
2016-01-30 08:51:41 +00:00
log_android(ANDROID_LOG_WARN,
2016-01-29 18:10:23 +00:00
"DNS response Q invalid off %d datalen %d",
off, datalen);
return;
}
}
for (int a = 0; a < acount; a++) {
2016-01-31 15:38:38 +00:00
off = get_qname(data, datalen, (uint16_t) off, name);
2016-01-29 18:10:23 +00:00
if (off > 0 && off + 10 <= datalen) {
uint16_t qtype = ntohs(*((uint16_t *) (data + off)));
uint16_t qclass = ntohs(*((uint16_t *) (data + off + 2)));
uint32_t ttl = ntohl(*((uint32_t *) (data + off + 4)));
uint16_t rdlength = ntohs(*((uint16_t *) (data + off + 8)));
off += 10;
if (off + rdlength <= datalen) {
2016-01-29 19:30:52 +00:00
if (qclass == DNS_QCLASS_IN &&
2016-01-30 08:51:41 +00:00
(qtype == DNS_QTYPE_A || qtype == DNS_QTYPE_AAAA)) {
2016-01-29 19:30:52 +00:00
char rd[INET6_ADDRSTRLEN + 1];
if (qtype == DNS_QTYPE_A)
inet_ntop(AF_INET, data + off, rd, sizeof(rd));
else if (qclass == DNS_QCLASS_IN && qtype == DNS_QTYPE_AAAA)
inet_ntop(AF_INET6, data + off, rd, sizeof(rd));
2016-01-30 08:51:41 +00:00
dns_resolved(args, qname, name, rd, ttl);
log_android(ANDROID_LOG_DEBUG,
2016-01-29 19:30:52 +00:00
"DNS answer %d qname %s qtype %d ttl %d data %s",
2016-01-30 08:51:41 +00:00
a, name, qtype, ttl, rd);
2016-01-29 19:30:52 +00:00
2016-01-29 18:10:23 +00:00
} else
2016-01-30 08:51:41 +00:00
log_android(ANDROID_LOG_DEBUG,
2016-01-29 18:10:23 +00:00
"DNS answer %d qname %s qclass %d qtype %d ttl %d length %d",
2016-01-30 08:51:41 +00:00
a, name, qclass, qtype, ttl, rdlength);
2016-01-29 18:10:23 +00:00
off += rdlength;
}
else {
2016-01-30 08:51:41 +00:00
log_android(ANDROID_LOG_WARN,
2016-01-29 18:10:23 +00:00
"DNS response A invalid off %d rdlength %d datalen %d",
off, rdlength, datalen);
return;
}
}
else {
2016-01-30 08:51:41 +00:00
log_android(ANDROID_LOG_WARN,
2016-01-29 18:10:23 +00:00
"DNS response A invalid off %d datalen %d",
off, datalen);
return;
}
}
}
2016-01-30 12:03:29 +00:00
else if (acount > 0)
2016-01-30 08:51:41 +00:00
log_android(ANDROID_LOG_WARN,
2016-01-29 18:10:23 +00:00
"DNS response qr %d opcode %d qcount %d acount %d",
dns->qr, dns->opcode, qcount, acount);
}
2016-01-20 13:11:04 +00:00
void check_tcp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) {
2016-01-20 11:35:51 +00:00
struct tcp_session *cur = tcp_session;
2016-01-17 16:39:11 +00:00
while (cur != NULL) {
2016-01-25 08:27:12 +00:00
if (cur->socket >= 1) {
int oldstate = cur->state;
uint32_t oldlocal = cur->local_seq;
uint32_t oldremote = cur->remote_seq;
2016-01-25 17:57:05 +00:00
// TODO if logging
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
if (cur->version == 4) {
inet_ntop(AF_INET, &cur->saddr.ip4, source, sizeof(source));
inet_ntop(AF_INET, &cur->daddr.ip4, dest, sizeof(dest));
} else {
inet_ntop(AF_INET6, &cur->saddr.ip6, source, sizeof(source));
inet_ntop(AF_INET6, &cur->daddr.ip6, dest, sizeof(dest));
}
2016-01-25 08:27:12 +00:00
// Check socket error
if (FD_ISSET(cur->socket, efds)) {
cur->time = time(NULL);
2016-01-20 11:40:26 +00:00
2016-01-25 08:27:12 +00:00
int serr = 0;
socklen_t optlen = sizeof(int);
int err = getsockopt(cur->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen);
if (err < 0)
log_android(ANDROID_LOG_ERROR, "getsockopt error %d: %s", errno,
strerror(errno));
else if (serr)
log_android(ANDROID_LOG_ERROR, "SO_ERROR %d: %s", serr, strerror(serr));
2016-01-17 19:40:40 +00:00
2016-01-25 08:27:12 +00:00
write_rst(args, cur);
}
else {
// Assume socket okay
if (cur->state == TCP_LISTEN) {
// Check socket connect
if (FD_ISSET(cur->socket, wfds)) {
cur->time = time(NULL);
log_android(ANDROID_LOG_INFO, "Connected from %s/%u to %s/%u",
source, ntohs(cur->source), dest, ntohs(cur->dest));
2016-01-20 11:40:26 +00:00
2016-01-25 08:27:12 +00:00
if (write_syn_ack(args, cur) >= 0) {
cur->local_seq++; // local SYN
cur->remote_seq++; // remote SYN
cur->state = TCP_SYN_RECV;
}
2016-01-17 16:39:11 +00:00
}
}
2016-01-20 11:40:26 +00:00
2016-01-25 08:27:12 +00:00
else if (cur->state == TCP_SYN_RECV ||
cur->state == TCP_ESTABLISHED ||
cur->state == TCP_CLOSE_WAIT) {
// Check socket read
if (FD_ISSET(cur->socket, rfds) && cur->send_window > 0) {
cur->time = time(NULL);
size_t len = (cur->send_window > TCP_SEND_WINDOW
? TCP_SEND_WINDOW
: cur->send_window);
uint8_t *buffer = malloc(len);
ssize_t bytes = recv(cur->socket, buffer, len, 0);
if (bytes < 0) {
// Socket error
log_android(ANDROID_LOG_ERROR, "recv error %d: %s",
errno, strerror(errno));
2016-02-02 13:31:44 +00:00
if (errno != EINTR && errno != EAGAIN)
2016-01-25 08:27:12 +00:00
write_rst(args, cur);
}
else if (bytes == 0) {
// Socket eof
// TCP: application close
log_android(ANDROID_LOG_INFO, "recv empty state %s",
strstate(cur->state));
2016-01-17 19:40:40 +00:00
2016-01-25 08:27:12 +00:00
if (write_fin_ack(args, cur, 0) >= 0) {
cur->local_seq++; // local FIN
2016-01-17 16:39:11 +00:00
2016-01-25 08:27:12 +00:00
if (cur->state == TCP_SYN_RECV || cur->state == TCP_ESTABLISHED)
cur->state = TCP_FIN_WAIT1;
else if (cur->state == TCP_CLOSE_WAIT)
cur->state = TCP_LAST_ACK;
else
log_android(ANDROID_LOG_ERROR, "Unknown state %s",
strstate(cur->state));
2016-01-18 14:29:01 +00:00
2016-01-25 08:27:12 +00:00
log_android(ANDROID_LOG_INFO, "Half close state %s",
strstate(cur->state));
2016-01-25 08:27:12 +00:00
}
} else {
// Socket read data
log_android(ANDROID_LOG_DEBUG,
"recv bytes %d state %s", bytes, strstate(cur->state));
// Forward to tun
if (write_data(args, cur, buffer, (size_t) bytes) >= 0)
cur->local_seq += bytes;
2016-01-17 16:39:11 +00:00
}
2016-01-25 08:27:12 +00:00
free(buffer);
2016-01-17 16:39:11 +00:00
}
}
}
2016-01-25 08:27:12 +00:00
if (cur->state != oldstate || cur->local_seq != oldlocal ||
2016-01-25 17:57:05 +00:00
cur->remote_seq != oldremote)
2016-01-28 11:20:52 +00:00
log_android(ANDROID_LOG_DEBUG,
"TCP session %s/%u new state %s local %u remote %u",
2016-01-25 08:27:12 +00:00
dest, ntohs(cur->dest), strstate(cur->state),
cur->local_seq - cur->local_start,
cur->remote_seq - cur->remote_start);
}
2016-01-17 16:39:11 +00:00
cur = cur->next;
}
}
2016-01-26 14:07:26 +00:00
// https://en.wikipedia.org/wiki/IPv6_packet#Extension_headers
// http://www.iana.org/assignments/protocol-numbers/protocol-numbers.xhtml
int is_lower_layer(int protocol) {
// No next header = 59
return (protocol == 0 || // Hop-by-Hop Options
protocol == 60 || // Destination Options (before routing header)
protocol == 43 || // Routing
protocol == 44 || // Fragment
protocol == 51 || // Authentication Header (AH)
protocol == 50 || // Encapsulating Security Payload (ESP)
protocol == 60 || // Destination Options (before upper-layer header)
protocol == 135); // Mobility
}
int is_upper_layer(int protocol) {
2016-01-26 14:07:26 +00:00
return (protocol == IPPROTO_TCP ||
protocol == IPPROTO_UDP ||
protocol == IPPROTO_ICMP ||
2016-02-02 13:31:44 +00:00
protocol == IPPROTO_ICMPV6);
}
void handle_ip(const struct arguments *args, const uint8_t *pkt, const size_t length) {
2016-01-15 18:51:07 +00:00
uint8_t protocol;
void *saddr;
void *daddr;
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
2016-01-15 18:51:07 +00:00
char flags[10];
int flen = 0;
uint8_t *payload;
2016-01-28 17:39:13 +00:00
#ifdef PROFILE_EVENTS
float mselapsed;
struct timeval start, end;
gettimeofday(&start, NULL);
#endif
2016-01-15 18:51:07 +00:00
// Get protocol, addresses & payload
uint8_t version = (*pkt) >> 4;
2016-01-15 18:51:07 +00:00
if (version == 4) {
struct iphdr *ip4hdr = (struct iphdr *) pkt;
2016-01-15 18:51:07 +00:00
protocol = ip4hdr->protocol;
saddr = &ip4hdr->saddr;
daddr = &ip4hdr->daddr;
2016-01-18 20:18:23 +00:00
if (ip4hdr->frag_off & IP_MF) {
log_android(ANDROID_LOG_ERROR, "IP fragment");
2016-01-15 18:51:07 +00:00
flags[flen++] = '+';
2016-01-18 20:18:23 +00:00
}
2016-01-15 18:51:07 +00:00
2016-01-23 19:46:27 +00:00
uint8_t ipoptlen = (uint8_t) ((ip4hdr->ihl - 5) * 4);
payload = (uint8_t *) (pkt + sizeof(struct iphdr) + ipoptlen);
2016-01-15 18:51:07 +00:00
if (ntohs(ip4hdr->tot_len) != length) {
log_android(ANDROID_LOG_ERROR, "Invalid length %u header length %u",
length, ntohs(ip4hdr->tot_len));
2016-01-15 18:51:07 +00:00
return;
}
2016-01-22 16:00:50 +00:00
if (loglevel < ANDROID_LOG_WARN) {
2016-01-23 19:46:27 +00:00
if (!calc_checksum(0, (uint8_t *) ip4hdr, sizeof(struct iphdr))) {
2016-01-22 16:00:50 +00:00
log_android(ANDROID_LOG_ERROR, "Invalid IP checksum");
return;
}
2016-01-15 18:51:07 +00:00
}
}
else if (version == 6) {
struct ip6_hdr *ip6hdr = (struct ip6_hdr *) pkt;
2016-01-15 18:51:07 +00:00
// Skip extension headers
uint16_t off = 0;
2016-01-15 18:51:07 +00:00
protocol = ip6hdr->ip6_nxt;
if (!is_upper_layer(protocol)) {
log_android(ANDROID_LOG_WARN, "IP6 extension %d", protocol);
off = sizeof(struct ip6_hdr);
struct ip6_ext *ext = (struct ip6_ext *) (pkt + off);
while (is_lower_layer(ext->ip6e_nxt) && !is_upper_layer(protocol)) {
protocol = ext->ip6e_nxt;
log_android(ANDROID_LOG_WARN, "IP6 extension %d", protocol);
off += (8 + ext->ip6e_len);
ext = (struct ip6_ext *) (pkt + off);
}
if (!is_upper_layer(protocol)) {
off = 0;
protocol = ip6hdr->ip6_nxt;
log_android(ANDROID_LOG_WARN, "IP6 final extension %d", protocol);
}
}
2016-01-15 18:51:07 +00:00
saddr = &ip6hdr->ip6_src;
daddr = &ip6hdr->ip6_dst;
payload = (uint8_t *) (pkt + sizeof(struct ip6_hdr) + off);
2016-01-15 18:51:07 +00:00
// TODO check length
// TODO checksum
}
else {
log_android(ANDROID_LOG_WARN, "Unknown version %d", version);
2016-01-15 18:51:07 +00:00
return;
}
inet_ntop(version == 4 ? AF_INET : AF_INET6, saddr, source, sizeof(source));
inet_ntop(version == 4 ? AF_INET : AF_INET6, daddr, dest, sizeof(dest));
// Get ports & flags
2016-01-28 14:19:58 +00:00
int syn = 0;
2016-01-19 20:16:54 +00:00
int32_t sport = -1;
int32_t dport = -1;
2016-02-02 13:31:44 +00:00
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6) {
struct icmp *icmp = (struct icmp *) payload;
// http://lwn.net/Articles/443051/
sport = ntohs(icmp->icmp_id);
2016-02-04 12:00:33 +00:00
dport = ntohs(icmp->icmp_id);
2016-02-02 13:31:44 +00:00
} else if (protocol == IPPROTO_UDP) {
struct udphdr *udp = (struct udphdr *) payload;
sport = ntohs(udp->source);
dport = ntohs(udp->dest);
// TODO checksum (IPv6)
}
else if (protocol == IPPROTO_TCP) {
2016-01-23 19:46:27 +00:00
struct tcphdr *tcp = (struct tcphdr *) payload;
2016-01-15 18:51:07 +00:00
sport = ntohs(tcp->source);
dport = ntohs(tcp->dest);
if (tcp->syn) {
syn = 1;
flags[flen++] = 'S';
}
if (tcp->ack)
flags[flen++] = 'A';
if (tcp->psh)
flags[flen++] = 'P';
if (tcp->fin)
flags[flen++] = 'F';
2016-01-17 09:42:21 +00:00
if (tcp->rst)
2016-01-15 18:51:07 +00:00
flags[flen++] = 'R';
// TODO checksum
}
flags[flen] = 0;
// Get uid
jint uid = -1;
2016-02-02 13:31:44 +00:00
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6 ||
2016-02-04 09:09:31 +00:00
(protocol == IPPROTO_UDP && dport != 53 && !has_udp_session(pkt, payload)) ||
(protocol == IPPROTO_TCP && syn)) {
2016-01-28 11:35:44 +00:00
log_android(ANDROID_LOG_INFO, "get uid %s/%u version %d protocol %d syn %d",
dest, dport, version, protocol, syn);
2016-01-18 19:05:58 +00:00
int tries = 0;
2016-01-20 09:27:18 +00:00
usleep(1000 * UID_DELAY);
while (uid < 0 && tries++ < UID_MAXTRY) {
2016-01-19 20:10:48 +00:00
// Check IPv6 table first
2016-01-23 19:46:27 +00:00
int dump = (tries == UID_MAXTRY);
2016-01-19 20:10:48 +00:00
if (version == 4) {
int8_t saddr128[16];
memset(saddr128, 0, 10);
2016-01-23 19:46:27 +00:00
saddr128[10] = (uint8_t) 0xFF;
saddr128[11] = (uint8_t) 0xFF;
2016-01-19 20:10:48 +00:00
memcpy(saddr128 + 12, saddr, 4);
2016-01-23 19:46:27 +00:00
uid = get_uid(protocol, 6, saddr128, (const uint16_t) sport, dump);
2016-01-19 20:10:48 +00:00
}
if (uid < 0)
2016-01-23 19:46:27 +00:00
uid = get_uid(protocol, version, saddr, (const uint16_t) sport, dump);
2016-01-18 19:05:58 +00:00
// Retry delay
if (uid < 0 && tries < UID_MAXTRY) {
2016-01-20 09:27:18 +00:00
log_android(ANDROID_LOG_WARN, "get uid %s/%u syn %d try %d",
dest, dport, syn, tries);
usleep(1000 * UID_DELAYTRY);
2016-01-18 19:05:58 +00:00
}
2016-01-15 18:51:07 +00:00
}
2016-01-18 19:05:58 +00:00
if (uid < 0)
2016-01-19 08:49:47 +00:00
log_android(ANDROID_LOG_ERROR, "uid not found");
2016-01-15 18:51:07 +00:00
}
log_android(ANDROID_LOG_DEBUG,
"Packet v%d %s/%u -> %s/%u proto %d flags %s uid %d",
version, source, sport, dest, dport, protocol, flags, uid);
2016-01-28 17:39:13 +00:00
#ifdef PROFILE_EVENTS
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_EVENTS)
log_android(ANDROID_LOG_WARN, "handle ip %f", mselapsed);
#endif
2016-01-15 18:51:07 +00:00
2016-01-19 19:58:51 +00:00
// Check if allowed
jboolean allowed = 0;
2016-02-02 13:31:44 +00:00
if (protocol == IPPROTO_UDP && dport == 53)
allowed = 1; // allow DNS
2016-02-04 09:09:31 +00:00
else if (protocol == IPPROTO_UDP && has_udp_session(pkt, payload))
allowed = 1; // could be a lingering session
else if (protocol == IPPROTO_TCP && !syn)
allowed = 1; // assume session
2016-02-02 13:31:44 +00:00
else {
2016-01-28 11:46:22 +00:00
jobject objPacket = create_packet(
2016-01-28 14:19:58 +00:00
args, version, protocol, flags, source, sport, dest, dport, "", uid, 0);
2016-01-28 10:58:39 +00:00
allowed = is_address_allowed(args, objPacket);
2016-01-19 19:58:51 +00:00
}
2016-01-19 20:16:54 +00:00
// Handle allowed traffic
2016-01-19 19:58:51 +00:00
if (allowed) {
2016-02-02 13:31:44 +00:00
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6)
handle_icmp(args, pkt, length, payload, uid);
else if (protocol == IPPROTO_UDP)
handle_udp(args, pkt, length, payload, uid);
2016-01-28 10:58:39 +00:00
else if (protocol == IPPROTO_TCP)
handle_tcp(args, pkt, length, payload, uid);
2016-01-19 19:58:51 +00:00
}
2016-02-04 09:09:31 +00:00
else {
if (protocol == IPPROTO_UDP)
block_udp(args, pkt, length, payload, uid);
2016-02-03 18:50:12 +00:00
log_android(ANDROID_LOG_INFO, "Address %s/%u syn %d not allowed", dest, dport, syn);
2016-02-04 09:09:31 +00:00
}
2016-01-28 17:39:13 +00:00
#ifdef PROFILE_EVENTS
gettimeofday(&end, NULL);
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
(end.tv_usec - start.tv_usec) / 1000.0;
if (mselapsed > PROFILE_EVENTS)
log_android(ANDROID_LOG_WARN, "handle protocol %f", mselapsed);
#endif
2016-01-15 18:51:07 +00:00
}
2016-02-02 13:31:44 +00:00
jboolean handle_icmp(const struct arguments *args,
2016-02-04 09:09:31 +00:00
const uint8_t *pkt, size_t length,
const uint8_t *payload,
2016-02-02 13:31:44 +00:00
int uid) {
// Get headers
const uint8_t version = (*pkt) >> 4;
const struct iphdr *ip4 = (struct iphdr *) pkt;
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt;
struct icmp *icmp = (struct icmp *) payload;
size_t icmplen = length - (payload - pkt);
// Search session
struct icmp_session *cur = icmp_session;
while (cur != NULL &&
!(!cur->stop && cur->version == version &&
(version == 4 ? cur->saddr.ip4 == ip4->saddr &&
cur->daddr.ip4 == ip4->daddr
: memcmp(&cur->saddr.ip6, &ip6->ip6_src, 16) == 0 &&
2016-02-04 09:09:31 +00:00
memcmp(&cur->daddr.ip6, &ip6->ip6_dst, 16) == 0)))
2016-02-02 13:31:44 +00:00
cur = cur->next;
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
if (version == 4) {
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source));
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest));
} else {
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source));
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest));
}
// Create new session if needed
if (cur == NULL) {
log_android(ANDROID_LOG_INFO, "ICMP new session from %s to %s", source, dest);
// Register session
struct icmp_session *i = malloc(sizeof(struct icmp_session));
i->time = time(NULL);
i->uid = uid;
i->version = version;
if (version == 4) {
i->saddr.ip4 = (__be32) ip4->saddr;
i->daddr.ip4 = (__be32) ip4->daddr;
} else {
memcpy(&i->saddr.ip6, &ip6->ip6_src, 16);
memcpy(&i->daddr.ip6, &ip6->ip6_dst, 16);
}
i->id = icmp->icmp_id; // store original ID
i->stop = 0;
i->next = NULL;
// Open UDP socket
i->socket = open_icmp_socket(args, i);
if (i->socket < 0) {
free(i);
return 0;
}
log_android(ANDROID_LOG_DEBUG, "ICMP socket %d id %x", i->socket, i->id);
2016-02-04 09:09:31 +00:00
i->next = icmp_session;
icmp_session = i;
2016-02-02 13:31:44 +00:00
cur = i;
}
// Modify ID
// http://lwn.net/Articles/443051/
icmp->icmp_id = ~icmp->icmp_id;
2016-02-02 14:11:09 +00:00
uint16_t csum = 0;
if (version == 6) {
// Untested
struct ip6_hdr_pseudo pseudo;
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo));
memcpy(&pseudo.ip6ph_src, &ip6->ip6_dst, 16);
memcpy(&pseudo.ip6ph_dst, &ip6->ip6_src, 16);
pseudo.ip6ph_len = ip6->ip6_ctlun.ip6_un1.ip6_un1_plen;
pseudo.ip6ph_nxt = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo));
}
2016-02-02 13:31:44 +00:00
icmp->icmp_cksum = 0;
2016-02-03 17:32:25 +00:00
icmp->icmp_cksum = ~calc_checksum(csum, (uint8_t *) icmp, icmplen);
2016-02-02 13:31:44 +00:00
log_android(ANDROID_LOG_INFO,
"ICMP forward from tun %s to %s type %d code %d id %x seq %d data %d",
source, dest,
icmp->icmp_type, icmp->icmp_code, icmp->icmp_id, icmp->icmp_seq, icmplen);
cur->time = time(NULL);
struct sockaddr_in server4;
struct sockaddr_in6 server6;
if (version == 4) {
server4.sin_family = AF_INET;
server4.sin_addr.s_addr = (__be32) ip4->daddr;
server4.sin_port = 0;
} else {
server6.sin6_family = AF_INET6;
memcpy(&server6.sin6_addr, &ip6->ip6_dst, 16);
server6.sin6_port = 0;
}
// Send raw ICMP message
if (sendto(cur->socket, icmp, (socklen_t) icmplen, MSG_NOSIGNAL,
(const struct sockaddr *) (version == 4 ? &server4 : &server6),
(socklen_t) (version == 4 ? sizeof(server4) : sizeof(server6))) != icmplen) {
log_android(ANDROID_LOG_ERROR, "ICMP sendto error %d: %s", errno, strerror(errno));
if (errno != EINTR && errno != EAGAIN) {
cur->stop = 1;
return 0;
}
}
return 1;
}
2016-02-04 09:09:31 +00:00
int has_udp_session(const uint8_t *pkt, const uint8_t *payload) {
2016-01-28 11:46:22 +00:00
// Get headers
const uint8_t version = (*pkt) >> 4;
const struct iphdr *ip4 = (struct iphdr *) pkt;
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt;
const struct udphdr *udphdr = (struct udphdr *) payload;
// Search session
struct udp_session *cur = udp_session;
while (cur != NULL &&
2016-02-04 09:09:31 +00:00
!(cur->version == version &&
2016-01-28 11:46:22 +00:00
cur->source == udphdr->source && cur->dest == udphdr->dest &&
(version == 4 ? cur->saddr.ip4 == ip4->saddr &&
cur->daddr.ip4 == ip4->daddr
: memcmp(&cur->saddr.ip6, &ip6->ip6_src, 16) == 0 &&
2016-01-28 14:19:58 +00:00
memcmp(&cur->daddr.ip6, &ip6->ip6_dst, 16) == 0)))
2016-01-28 11:46:22 +00:00
cur = cur->next;
2016-01-28 14:19:58 +00:00
2016-01-28 11:46:22 +00:00
return (cur != NULL);
}
2016-02-04 09:09:31 +00:00
void block_udp(const struct arguments *args,
const uint8_t *pkt, size_t length,
const uint8_t *payload,
int uid) {
// Get headers
const uint8_t version = (*pkt) >> 4;
const struct iphdr *ip4 = (struct iphdr *) pkt;
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt;
const struct udphdr *udphdr = (struct udphdr *) payload;
const uint8_t *data = payload + sizeof(struct udphdr);
const size_t datalen = length - (data - pkt);
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
if (version == 4) {
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source));
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest));
} else {
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source));
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest));
}
log_android(ANDROID_LOG_INFO, "UDP blocked session from %s/%u to %s/%u",
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest));
// Register session
struct udp_session *u = malloc(sizeof(struct udp_session));
u->time = time(NULL);
u->uid = uid;
u->version = version;
if (version == 4) {
u->saddr.ip4 = (__be32) ip4->saddr;
u->daddr.ip4 = (__be32) ip4->daddr;
} else {
memcpy(&u->saddr.ip6, &ip6->ip6_src, 16);
memcpy(&u->daddr.ip6, &ip6->ip6_dst, 16);
}
u->source = udphdr->source;
u->dest = udphdr->dest;
u->state = UDP_BLOCKED;
u->socket = -1;
u->next = udp_session;
udp_session = u;
}
jboolean handle_udp(const struct arguments *args,
2016-02-04 09:09:31 +00:00
const uint8_t *pkt, size_t length,
const uint8_t *payload,
int uid) {
2016-01-20 08:24:34 +00:00
// Get headers
const uint8_t version = (*pkt) >> 4;
const struct iphdr *ip4 = (struct iphdr *) pkt;
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt;
const struct udphdr *udphdr = (struct udphdr *) payload;
const uint8_t *data = payload + sizeof(struct udphdr);
const size_t datalen = length - (data - pkt);
2016-01-20 08:24:34 +00:00
2016-01-20 13:11:04 +00:00
// Search session
struct udp_session *cur = udp_session;
while (cur != NULL &&
2016-02-04 09:09:31 +00:00
!(cur->version == version &&
2016-01-28 11:46:22 +00:00
cur->source == udphdr->source && cur->dest == udphdr->dest &&
(version == 4 ? cur->saddr.ip4 == ip4->saddr &&
cur->daddr.ip4 == ip4->daddr
: memcmp(&cur->saddr.ip6, &ip6->ip6_src, 16) == 0 &&
2016-02-04 09:09:31 +00:00
memcmp(&cur->daddr.ip6, &ip6->ip6_dst, 16) == 0)))
2016-01-20 13:11:04 +00:00
cur = cur->next;
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
if (version == 4) {
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source));
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest));
} else {
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source));
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest));
}
2016-01-23 14:50:18 +00:00
2016-02-04 09:09:31 +00:00
if (cur != NULL && cur->state != UDP_ACTIVE) {
log_android(ANDROID_LOG_INFO, "UDP ignore session from %s/%u to %s/%u state %d",
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest), cur->state);
return 0;
}
2016-01-20 13:11:04 +00:00
// Create new session if needed
if (cur == NULL) {
log_android(ANDROID_LOG_INFO, "UDP new session from %s/%u to %s/%u",
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest));
2016-01-23 08:39:21 +00:00
2016-01-20 13:11:04 +00:00
// Register session
struct udp_session *u = malloc(sizeof(struct udp_session));
2016-01-26 17:53:17 +00:00
u->time = time(NULL);
2016-01-20 13:11:04 +00:00
u->uid = uid;
u->version = version;
2016-01-25 17:57:05 +00:00
if (version == 4) {
u->saddr.ip4 = (__be32) ip4->saddr;
u->daddr.ip4 = (__be32) ip4->daddr;
} else {
memcpy(&u->saddr.ip6, &ip6->ip6_src, 16);
memcpy(&u->daddr.ip6, &ip6->ip6_dst, 16);
}
2016-01-25 17:57:05 +00:00
2016-01-20 13:11:04 +00:00
u->source = udphdr->source;
u->dest = udphdr->dest;
2016-02-04 09:09:31 +00:00
u->state = UDP_ACTIVE;
2016-01-20 13:11:04 +00:00
u->next = NULL;
// Open UDP socket
2016-01-25 12:28:52 +00:00
u->socket = open_udp_socket(args, u);
2016-01-20 13:11:04 +00:00
if (u->socket < 0) {
2016-01-25 12:28:52 +00:00
free(u);
2016-01-20 13:11:04 +00:00
return 0;
}
2016-01-25 08:27:12 +00:00
2016-01-25 12:28:52 +00:00
log_android(ANDROID_LOG_DEBUG, "UDP socket %d", u->socket);
2016-01-20 13:11:04 +00:00
2016-02-04 09:09:31 +00:00
u->next = udp_session;
udp_session = u;
2016-01-20 13:11:04 +00:00
2016-01-25 12:28:52 +00:00
cur = u;
2016-01-20 13:11:04 +00:00
}
2016-01-20 08:24:34 +00:00
2016-01-23 14:50:18 +00:00
// Check for DNS
if (ntohs(udphdr->dest) == 53) {
char qname[DNS_QNAME_MAX + 1];
uint16_t qtype;
uint16_t qclass;
2016-01-29 06:59:35 +00:00
if (get_dns_query(args, cur, data, datalen, &qtype, &qclass, qname) >= 0) {
2016-01-30 08:51:41 +00:00
log_android(ANDROID_LOG_DEBUG,
2016-01-29 18:10:23 +00:00
"DNS query qtype %d qclass %d name %s",
qtype, qclass, qname);
2016-01-26 17:53:17 +00:00
if (check_domain(args, cur, data, datalen, qclass, qtype, qname)) {
2016-01-28 14:19:58 +00:00
// Log qname
2016-01-28 11:46:22 +00:00
char name[DNS_QNAME_MAX + 40 + 1];
sprintf(name, "qtype %d qname %s", qtype, qname);
jobject objPacket = create_packet(
args, version, IPPROTO_UDP, "",
source, ntohs(cur->source), dest, ntohs(cur->dest),
2016-01-29 06:59:35 +00:00
name, 0, 0);
2016-01-28 11:46:22 +00:00
log_packet(args, objPacket);
2016-01-28 14:19:58 +00:00
// Session done
2016-02-04 09:09:31 +00:00
cur->state = UDP_FINISHING;
return 0;
}
}
}
2016-01-20 08:24:34 +00:00
// Check for DHCP (tethering)
if (ntohs(udphdr->source) == 68 || ntohs(udphdr->dest) == 67) {
if (check_dhcp(args, cur, data, datalen) >= 0)
return 1;
}
2016-01-25 17:57:05 +00:00
log_android(ANDROID_LOG_INFO, "UDP forward from tun %s/%u to %s/%u data %d",
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest), datalen);
2016-01-20 08:24:34 +00:00
2016-01-21 16:24:23 +00:00
cur->time = time(NULL);
struct sockaddr_in server4;
struct sockaddr_in6 server6;
if (version == 4) {
server4.sin_family = AF_INET;
server4.sin_addr.s_addr = (__be32) ip4->daddr;
server4.sin_port = udphdr->dest;
} else {
server6.sin6_family = AF_INET6;
memcpy(&server6.sin6_addr, &ip6->ip6_dst, 16);
server6.sin6_port = udphdr->dest;
}
2016-01-20 08:24:34 +00:00
if (sendto(cur->socket, data, (socklen_t) datalen, MSG_NOSIGNAL,
2016-01-25 14:21:21 +00:00
(const struct sockaddr *) (version == 4 ? &server4 : &server6),
(socklen_t) (version == 4 ? sizeof(server4) : sizeof(server6))) != datalen) {
log_android(ANDROID_LOG_ERROR, "UDP sendto error %d: %s", errno, strerror(errno));
2016-02-02 13:31:44 +00:00
if (errno != EINTR && errno != EAGAIN) {
2016-02-04 09:09:31 +00:00
cur->state = UDP_FINISHING;
2016-02-02 13:31:44 +00:00
return 0;
}
2016-01-20 08:24:34 +00:00
}
return 1;
}
2016-01-29 06:59:35 +00:00
int get_dns_query(const struct arguments *args, const struct udp_session *u,
const uint8_t *data, const size_t datalen,
2016-01-29 18:10:23 +00:00
uint16_t *qtype, uint16_t *qclass, char *qname) {
if (datalen < sizeof(struct dns_header) + 1) {
2016-01-29 18:10:23 +00:00
log_android(ANDROID_LOG_WARN, "DNS query length %d", datalen);
return -1;
}
2016-01-25 14:21:21 +00:00
// Check if standard DNS query
// TODO multiple qnames
const struct dns_header *dns = (struct dns_header *) data;
2016-01-29 06:59:35 +00:00
int qcount = ntohs(dns->q_count);
if (dns->qr == 0 && dns->opcode == 0 && qcount > 0) {
if (qcount > 1)
2016-01-29 18:10:23 +00:00
log_android(ANDROID_LOG_WARN, "DNS query qcount %d", qcount);
// http://tools.ietf.org/html/rfc1035
2016-01-29 18:10:23 +00:00
int off = get_qname(data, datalen, sizeof(struct dns_header), qname);
if (off > 0 && off + 4 == datalen) {
*qtype = ntohs(*((uint16_t *) (data + off)));
*qclass = ntohs(*((uint16_t *) (data + off + 2)));
return 0;
2016-01-23 14:50:18 +00:00
}
else
2016-01-29 18:10:23 +00:00
log_android(ANDROID_LOG_WARN, "DNS query invalid off %d datalen %d", off, datalen);
2016-01-23 14:50:18 +00:00
}
return -1;
}
2016-01-26 17:53:17 +00:00
int check_domain(const struct arguments *args, const struct udp_session *u,
const uint8_t *data, const size_t datalen,
uint16_t qclass, uint16_t qtype, const char *name) {
2016-01-28 10:58:39 +00:00
if (qclass == DNS_QCLASS_IN &&
(qtype == DNS_QTYPE_A || qtype == DNS_QTYPE_AAAA) &&
is_domain_blocked(args, name)) {
2016-02-05 09:43:28 +00:00
log_android(ANDROID_LOG_INFO, "DNS query type %d name %s blocked", qtype, name);
2016-01-28 10:58:39 +00:00
// Build response
size_t rlen = datalen + sizeof(struct dns_rr) + (qtype == DNS_QTYPE_A ? 4 : 16);
uint8_t *response = malloc(rlen);
// Copy header & query
memcpy(response, data, datalen);
// Modify copied header
struct dns_header *rh = (struct dns_header *) response;
rh->qr = 1;
rh->aa = 0;
rh->tc = 0;
rh->rd = 0;
rh->ra = 0;
rh->z = 0;
rh->ad = 0;
rh->cd = 0;
rh->rcode = 0;
rh->ans_count = htons(1);
rh->auth_count = 0;
rh->add_count = 0;
// Build answer
struct dns_rr *answer = (struct dns_rr *) (response + datalen);
answer->qname_ptr = htons(sizeof(struct dns_header) | 0xC000);
answer->qtype = htons(qtype);
answer->qclass = htons(qclass);
answer->ttl = htonl(DNS_TTL);
answer->rdlength = htons(qtype == DNS_QTYPE_A ? 4 : 16);
// Add answer address
uint8_t *addr = response + datalen + sizeof(struct dns_rr);
if (qtype == DNS_QTYPE_A)
inet_pton(AF_INET, "127.0.0.1", addr);
else
inet_pton(AF_INET6, "::1", addr);
// Experiment
rlen = datalen;
rh->rcode = 3; // NXDOMAIN
rh->ans_count = 0;
// Send response
2016-01-29 06:59:35 +00:00
if (write_udp(args, u, response, rlen) < 0)
log_android(ANDROID_LOG_WARN, "UDP DNS write error %d: %s", errno, strerror(errno));
2016-01-28 10:58:39 +00:00
free(response);
return 1;
}
2016-01-23 14:50:18 +00:00
return 0;
}
int check_dhcp(const struct arguments *args, const struct udp_session *u,
const uint8_t *data, const size_t datalen) {
2016-01-26 17:53:17 +00:00
// This is untested
// Android routing of DHCP is eroneous
log_android(ANDROID_LOG_WARN, "DHCP check");
if (datalen < sizeof(struct dhcp_packet)) {
log_android(ANDROID_LOG_WARN, "DHCP packet size %d", datalen);
return -1;
}
const struct dhcp_packet *request = (struct dhcp_packet *) data;
if (ntohl(request->option_format) != DHCP_OPTION_MAGIC_NUMBER) {
log_android(ANDROID_LOG_WARN, "DHCP invalid magic %x", request->option_format);
return -1;
}
if (request->htype != 1 || request->hlen != 6) {
log_android(ANDROID_LOG_WARN, "DHCP unknown hardware htype %d hlen %d",
request->htype, request->hlen);
return -1;
}
log_android(ANDROID_LOG_WARN, "DHCP opcode", request->opcode);
// Discover: source 0.0.0.0:68 destination 255.255.255.255:67
// Offer: source 10.1.10.1:67 destination 255.255.255.255:68
// Request: source 0.0.0.0:68 destination 255.255.255.255:67
// Ack: source: 10.1.10.1 destination: 255.255.255.255
if (request->opcode == 1) { // Discover/request
struct dhcp_packet *response = calloc(500, 1);
// Hack
inet_pton(AF_INET, "10.1.10.1", &u->saddr);
/*
Discover:
DHCP option 53: DHCP Discover
DHCP option 50: 192.168.1.100 requested
DHCP option 55: Parameter Request List:
Request Subnet Mask (1), Router (3), Domain Name (15), Domain Name Server (6)
Request
DHCP option 53: DHCP Request
DHCP option 50: 192.168.1.100 requested
DHCP option 54: 192.168.1.1 DHCP server.
*/
memcpy(response, request, sizeof(struct dhcp_packet));
2016-01-29 06:59:35 +00:00
response->opcode = (uint8_t) (request->siaddr == 0 ? 2 /* Offer */ : /* Ack */ 4);
response->secs = 0;
response->flags = 0;
memset(&response->ciaddr, 0, sizeof(response->ciaddr));
inet_pton(AF_INET, "10.1.10.2", &response->yiaddr);
inet_pton(AF_INET, "10.1.10.1", &response->siaddr);
memset(&response->giaddr, 0, sizeof(response->giaddr));
// https://tools.ietf.org/html/rfc2132
2016-01-29 06:59:35 +00:00
uint8_t *options = (uint8_t *) (response + sizeof(struct dhcp_packet));
int idx = 0;
*(options + idx++) = 53; // Message type
*(options + idx++) = 1;
2016-01-29 06:59:35 +00:00
*(options + idx++) = (uint8_t) (request->siaddr == 0 ? 2 : 5);
/*
1 DHCPDISCOVER
2 DHCPOFFER
3 DHCPREQUEST
4 DHCPDECLINE
5 DHCPACK
6 DHCPNAK
7 DHCPRELEASE
8 DHCPINFORM
*/
*(options + idx++) = 1; // subnet mask
*(options + idx++) = 4; // IP4 length
inet_pton(AF_INET, "255.255.255.0", options + idx);
idx += 4;
*(options + idx++) = 3; // gateway
*(options + idx++) = 4; // IP4 length
inet_pton(AF_INET, "10.1.10.1", options + idx);
idx += 4;
*(options + idx++) = 51; // lease time
*(options + idx++) = 4; // quad
*((uint32_t *) (options + idx)) = 3600;
idx += 4;
*(options + idx++) = 54; // DHCP
*(options + idx++) = 4; // IP4 length
inet_pton(AF_INET, "10.1.10.1", options + idx);
idx += 4;
*(options + idx++) = 6; // DNS
*(options + idx++) = 4; // IP4 length
inet_pton(AF_INET, "8.8.8.8", options + idx);
idx += 4;
*(options + idx++) = 255; // End
/*
DHCP option 53: DHCP Offer
DHCP option 1: 255.255.255.0 subnet mask
DHCP option 3: 192.168.1.1 router
DHCP option 51: 86400s (1 day) IP address lease time
DHCP option 54: 192.168.1.1 DHCP server
DHCP option 6: DNS servers 9.7.10.15
*/
2016-01-31 15:38:38 +00:00
write_udp(args, u, (uint8_t *) response, 500);
free(response);
}
2016-01-31 15:38:38 +00:00
return 0;
}
jboolean handle_tcp(const struct arguments *args,
const uint8_t *pkt, size_t length,
const uint8_t *payload,
2016-01-28 10:58:39 +00:00
int uid) {
2016-01-11 22:06:35 +00:00
// Get headers
const uint8_t version = (*pkt) >> 4;
const struct iphdr *ip4 = (struct iphdr *) pkt;
const struct ip6_hdr *ip6 = (struct ip6_hdr *) pkt;
const struct tcphdr *tcphdr = (struct tcphdr *) payload;
const uint8_t tcpoptlen = (uint8_t) ((tcphdr->doff - 5) * 4);
const uint8_t *data = payload + sizeof(struct tcphdr) + tcpoptlen;
const size_t datalen = length - (data - pkt);
2016-01-11 22:06:35 +00:00
2016-01-16 08:07:04 +00:00
// Search session
2016-01-20 11:35:51 +00:00
struct tcp_session *cur = tcp_session;
2016-01-25 17:57:05 +00:00
while (cur != NULL &&
2016-01-28 11:46:22 +00:00
!(cur->version == version &&
cur->source == tcphdr->source && cur->dest == tcphdr->dest &&
2016-01-25 17:57:05 +00:00
(version == 4 ? cur->saddr.ip4 == ip4->saddr &&
cur->daddr.ip4 == ip4->daddr
: memcmp(&cur->saddr.ip6, &ip6->ip6_src, 16) == 0 &&
2016-02-04 09:09:31 +00:00
memcmp(&cur->daddr.ip6, &ip6->ip6_dst, 16) == 0)))
2016-01-11 22:06:35 +00:00
cur = cur->next;
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
2016-01-25 17:57:05 +00:00
if (version == 4) {
inet_ntop(AF_INET, &ip4->saddr, source, sizeof(source));
inet_ntop(AF_INET, &ip4->daddr, dest, sizeof(dest));
} else {
inet_ntop(AF_INET6, &ip6->ip6_src, source, sizeof(source));
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest));
}
2016-01-25 17:57:05 +00:00
log_android(ANDROID_LOG_DEBUG,
"TCP received from %s/%u for %s/%u seq %u ack %u window %u data %d",
source, ntohs(tcphdr->source),
dest, ntohs(tcphdr->dest),
ntohl(tcphdr->seq) - (cur == NULL ? 0 : cur->remote_start),
ntohl(tcphdr->ack_seq) - (cur == NULL ? 0 : cur->local_start),
ntohs(tcphdr->window), datalen);
2016-01-11 22:06:35 +00:00
if (cur == NULL) {
if (tcphdr->syn) {
2016-01-29 18:10:23 +00:00
log_android(ANDROID_LOG_INFO,
"TCP new session from %s/%u to %s/%u window %u uid %d",
source, ntohs(tcphdr->source),
2016-01-22 18:03:32 +00:00
dest, ntohs(tcphdr->dest),
ntohs(tcphdr->window), uid);
2016-01-11 22:06:35 +00:00
2016-01-16 08:07:04 +00:00
// Register session
2016-01-20 11:35:51 +00:00
struct tcp_session *syn = malloc(sizeof(struct tcp_session));
2016-01-12 08:45:58 +00:00
syn->time = time(NULL);
2016-01-15 09:28:31 +00:00
syn->uid = uid;
2016-01-25 17:57:05 +00:00
syn->version = version;
2016-01-22 18:03:32 +00:00
syn->send_window = ntohs(tcphdr->window);
2016-01-14 14:02:32 +00:00
syn->remote_seq = ntohl(tcphdr->seq); // ISN remote
2016-01-23 19:46:27 +00:00
syn->local_seq = (uint32_t) rand(); // ISN local
2016-01-17 05:48:33 +00:00
syn->remote_start = syn->remote_seq;
syn->local_start = syn->local_seq;
2016-01-25 17:57:05 +00:00
if (version == 4) {
syn->saddr.ip4 = (__be32) ip4->saddr;
syn->daddr.ip4 = (__be32) ip4->daddr;
} else {
memcpy(&syn->saddr.ip6, &ip6->ip6_src, 16);
memcpy(&syn->daddr.ip6, &ip6->ip6_dst, 16);
}
2016-01-11 22:06:35 +00:00
syn->source = tcphdr->source;
syn->dest = tcphdr->dest;
2016-01-16 11:32:55 +00:00
syn->state = TCP_LISTEN;
2016-02-05 09:43:28 +00:00
syn->data_rx = NULL;
2016-01-11 22:06:35 +00:00
syn->next = NULL;
2016-01-15 11:13:12 +00:00
// TODO handle SYN data?
2016-01-18 14:29:01 +00:00
if (datalen)
2016-01-25 17:57:05 +00:00
log_android(ANDROID_LOG_WARN, "TCP SYN session from %s/%u to %s/%u data %u",
source, ntohs(tcphdr->source),
dest, ntohs(tcphdr->dest), datalen);
2016-01-13 09:26:06 +00:00
// Open socket
2016-01-25 12:28:52 +00:00
syn->socket = open_tcp_socket(args, syn);
2016-01-15 07:48:18 +00:00
if (syn->socket < 0) {
2016-01-18 14:29:01 +00:00
// Remote might retry
2016-01-15 07:48:18 +00:00
free(syn);
2016-01-21 16:24:23 +00:00
return 0;
2016-01-15 07:48:18 +00:00
}
2016-01-11 22:06:35 +00:00
2016-01-25 12:28:52 +00:00
log_android(ANDROID_LOG_DEBUG, "TCP socket %d lport %d",
syn->socket, get_local_port(syn->socket));
2016-02-04 09:09:31 +00:00
syn->next = tcp_session;
tcp_session = syn;
2016-01-11 22:06:35 +00:00
}
2016-01-15 09:28:31 +00:00
else {
2016-01-25 17:57:05 +00:00
log_android(ANDROID_LOG_WARN, "TCP unknown session from %s/%u to %s/%u uid %d",
source, ntohs(tcphdr->source),
dest, ntohs(tcphdr->dest), uid);
2016-01-18 14:29:01 +00:00
2016-01-20 11:35:51 +00:00
struct tcp_session rst;
memset(&rst, 0, sizeof(struct tcp_session));
rst.version = 4;
rst.local_seq = 0;
rst.remote_seq = ntohl(tcphdr->seq);
2016-01-25 17:57:05 +00:00
if (version == 4) {
rst.saddr.ip4 = (__be32) ip4->saddr;
rst.daddr.ip4 = (__be32) ip4->daddr;
} else {
memcpy(&rst.saddr.ip6, &ip6->ip6_src, 16);
memcpy(&rst.daddr.ip6, &ip6->ip6_dst, 16);
}
2016-01-17 09:42:21 +00:00
rst.source = tcphdr->source;
rst.dest = tcphdr->dest;
2016-01-24 11:50:40 +00:00
write_rst(args, &rst);
2016-01-19 19:58:51 +00:00
return 0;
2016-01-15 09:28:31 +00:00
}
2016-01-11 22:06:35 +00:00
}
else {
2016-02-05 09:43:28 +00:00
char flags[10];
int flen = 0;
if (tcphdr->syn)
flags[flen++] = 'S';
if (tcphdr->ack)
flags[flen++] = 'A';
if (tcphdr->fin)
flags[flen++] = 'F';
if (tcphdr->rst)
flags[flen++] = 'R';
flags[flen] = 0;
char session[250];
sprintf(session,
"session %s from %s/%u to %s/%u state %s local %u/%u remote %u/%u data %u window %u",
flags,
source, ntohs(tcphdr->source),
dest, ntohs(cur->dest), strstate(cur->state),
cur->local_seq - cur->local_start,
ntohl(tcphdr->ack_seq) - cur->local_start,
cur->remote_seq - cur->remote_start,
ntohl(tcphdr->seq) - cur->remote_start,
datalen,
ntohs(tcphdr->window));
2016-01-18 14:29:01 +00:00
// Session found
2016-01-21 11:04:41 +00:00
if (cur->state == TCP_CLOSE) {
2016-02-05 09:43:28 +00:00
log_android(ANDROID_LOG_WARN, "TCP closed %s", session);
2016-01-24 11:50:40 +00:00
write_rst(args, cur);
2016-01-21 16:24:23 +00:00
return 0;
2016-01-21 11:04:41 +00:00
}
else {
int oldstate = cur->state;
uint32_t oldlocal = cur->local_seq;
uint32_t oldremote = cur->remote_seq;
2016-01-16 11:32:55 +00:00
2016-02-05 09:43:28 +00:00
log_android(ANDROID_LOG_DEBUG, "TCP %s", session);
2016-01-12 08:45:58 +00:00
2016-01-21 11:04:41 +00:00
cur->time = time(NULL);
2016-01-22 18:03:32 +00:00
cur->send_window = ntohs(tcphdr->window);
2016-01-18 14:29:01 +00:00
2016-02-05 09:43:28 +00:00
// Do not change the order of the conditions
2016-02-05 09:43:28 +00:00
// Buffer data to forward
if (datalen) {
uint32_t seq = ntohl(tcphdr->seq);
if (seq < cur->remote_seq)
log_android(ANDROID_LOG_DEBUG, "TCP already forwarded %s", session);
else {
struct segment *p = NULL;
struct segment *s = cur->data_rx;
while (s != NULL && s->seq < seq) {
p = s;
s = s->next;
}
if (s == NULL || s->seq > seq) {
struct segment *n = malloc(sizeof(struct segment));
n->seq = seq;
n->len = datalen;
n->psh = tcphdr->psh;
n->data = malloc(datalen);
memcpy(n->data, data, datalen);
n->next = s;
if (p == NULL)
cur->data_rx = n;
else
p->next = n;
}
else if (s != NULL && s->seq == seq)
log_android(ANDROID_LOG_DEBUG, "TCP alreading buffered %s", session);
}
}
2016-01-21 12:21:13 +00:00
2016-02-05 09:43:28 +00:00
// Forward buffered data
int fwd = 0;
while (cur->data_rx != NULL && cur->data_rx->seq == cur->remote_seq) {
log_android(ANDROID_LOG_DEBUG, "TCP forwarding %s segment %u...%u",
session,
cur->data_rx->seq - cur->remote_start,
cur->data_rx->seq + cur->data_rx->len - cur->remote_start);
unsigned int more = (cur->data_rx->psh ? 0 : MSG_MORE);
if (send(cur->socket, cur->data_rx->data, cur->data_rx->len,
MSG_NOSIGNAL | more) < 0) {
2016-01-21 12:21:13 +00:00
log_android(ANDROID_LOG_ERROR, "send error %d: %s", errno, strerror(errno));
if (errno == EINTR || errno == EAGAIN) {
2016-02-05 09:43:28 +00:00
// Retry later
return 0;
} else {
write_rst(args, cur);
return 0;
}
2016-01-21 11:04:41 +00:00
}
2016-01-22 15:13:33 +00:00
2016-02-05 09:43:28 +00:00
fwd = 1;
cur->remote_seq = cur->data_rx->seq + cur->data_rx->len;
struct segment *p = cur->data_rx;
cur->data_rx = cur->data_rx->next;
free(p->data);
free(p);
}
// Log data buffered
struct segment *s = cur->data_rx;
while (s != NULL) {
log_android(ANDROID_LOG_WARN, "TCP buffered %s %u...%u",
session,
s->seq - cur->remote_start,
s->seq + s->len - cur->remote_start);
s = s->next;
}
// Acknowledge forwarded data
int ok = 1;
if (fwd) {
if (!(tcphdr->fin ||
cur->state == TCP_FIN_WAIT1 ||
cur->state == TCP_FIN_WAIT2 ||
cur->state == TCP_CLOSING)) {
if (!write_ack(args, cur, 0) >= 0)
2016-01-22 15:13:33 +00:00
ok = 0;
2016-01-20 18:27:13 +00:00
}
}
2016-01-14 21:48:05 +00:00
2016-01-21 11:04:41 +00:00
if (ok) {
if (tcphdr->rst) {
// No sequence check
2016-02-05 09:43:28 +00:00
log_android(ANDROID_LOG_INFO, "TCP received reset %s", session);
2016-01-21 11:04:41 +00:00
cur->state = TCP_TIME_WAIT;
2016-01-21 16:24:23 +00:00
return 0;
2016-01-21 11:04:41 +00:00
}
else {
2016-02-05 09:43:28 +00:00
if (ntohl(tcphdr->ack_seq) == cur->local_seq) {
2016-01-21 11:04:41 +00:00
if (tcphdr->syn) {
2016-02-05 09:43:28 +00:00
log_android(ANDROID_LOG_WARN, "TCP repeated SYN %s", session);
2016-01-21 11:04:41 +00:00
// The socket is likely not opened yet
// Note: perfect, ordered packet receive assumed
} else if (tcphdr->fin /* ACK */) {
// Shutdown socket for writing
if (shutdown(cur->socket, SHUT_WR)) {
log_android(ANDROID_LOG_ERROR, "shutdown WR error %d: %s",
errno, strerror(errno));
// Data might be lost
2016-01-24 11:50:40 +00:00
write_rst(args, cur);
2016-01-21 16:24:23 +00:00
return 0;
2016-01-21 11:04:41 +00:00
}
else {
2016-01-24 11:50:40 +00:00
if (write_ack(args, cur, 1) >= 0) {
2016-01-21 11:04:41 +00:00
cur->remote_seq += 1; // FIN
if (cur->state == TCP_ESTABLISHED /* && !tcphdr->ack */)
cur->state = TCP_CLOSE_WAIT;
else if (cur->state == TCP_FIN_WAIT1 && tcphdr->ack)
cur->state = TCP_TIME_WAIT;
else if (cur->state == TCP_FIN_WAIT1 && !tcphdr->ack)
cur->state = TCP_CLOSING;
else if (cur->state == TCP_FIN_WAIT2 /* && !tcphdr->ack */)
cur->state = TCP_TIME_WAIT;
2016-01-21 16:24:23 +00:00
else {
2016-01-21 11:04:41 +00:00
log_android(ANDROID_LOG_ERROR,
2016-02-05 09:43:28 +00:00
"TCP invalid FIN %s", session);
2016-01-21 16:24:23 +00:00
return 0;
}
2016-01-21 11:04:41 +00:00
}
else
2016-01-21 16:24:23 +00:00
return 0;
}
}
2016-01-21 11:04:41 +00:00
else if (tcphdr->ack) {
if (cur->state == TCP_SYN_RECV)
cur->state = TCP_ESTABLISHED;
else if (cur->state == TCP_ESTABLISHED) {
2016-02-05 09:43:28 +00:00
// Do nothing
} else if (cur->state == TCP_LAST_ACK) {
2016-01-21 11:04:41 +00:00
// socket has been shutdown already
cur->state = TCP_TIME_WAIT;
}
else if (cur->state == TCP_FIN_WAIT1)
cur->state = TCP_FIN_WAIT2;
else if (cur->state == TCP_CLOSING)
cur->state = TCP_TIME_WAIT;
2016-01-21 16:24:23 +00:00
else {
2016-02-05 09:43:28 +00:00
log_android(ANDROID_LOG_ERROR, "TCP invalid %s", session);
2016-01-21 16:24:23 +00:00
return 0;
}
}
2016-01-21 11:04:41 +00:00
2016-01-21 16:24:23 +00:00
else {
2016-02-05 09:43:28 +00:00
log_android(ANDROID_LOG_ERROR, "TCP unknown %s", session);
2016-01-21 16:24:23 +00:00
return 0;
}
2016-01-20 18:27:13 +00:00
}
else {
2016-02-05 09:43:28 +00:00
if (ntohl(tcphdr->ack_seq) < cur->local_seq) {
log_android(ANDROID_LOG_WARN, "TCP previous %s", session);
return 1;
}
2016-01-21 16:24:23 +00:00
else {
2016-02-05 09:43:28 +00:00
log_android(ANDROID_LOG_ERROR, "TCP future %s", session);
return 0;
2016-01-21 16:24:23 +00:00
}
}
}
2016-01-17 13:30:21 +00:00
}
2016-01-16 11:32:55 +00:00
2016-01-21 11:04:41 +00:00
if (cur->state != oldstate || cur->local_seq != oldlocal ||
cur->remote_seq != oldremote)
log_android(ANDROID_LOG_INFO,
2016-01-25 17:57:05 +00:00
"TCP session from %s/%u to %s/%u new state %s local %u remote %u window %u",
2016-01-21 11:04:41 +00:00
source, ntohs(tcphdr->source),
dest, ntohs(cur->dest),
strstate(cur->state),
cur->local_seq - cur->local_start,
2016-01-22 18:03:32 +00:00
cur->remote_seq - cur->remote_start,
ntohs(tcphdr->window));
2016-01-21 11:04:41 +00:00
}
2016-01-13 09:26:06 +00:00
}
2016-01-19 19:58:51 +00:00
return 1;
2016-01-13 09:26:06 +00:00
}
2016-02-02 13:31:44 +00:00
int open_icmp_socket(const struct arguments *args, const struct icmp_session *cur) {
int sock;
// Get UDP socket
sock = socket(cur->version == 4 ? PF_INET : PF_INET6, SOCK_DGRAM, IPPROTO_ICMP);
if (sock < 0) {
log_android(ANDROID_LOG_ERROR, "ICMP socket error %d: %s", errno, strerror(errno));
return -1;
}
// Protect socket
if (protect_socket(args, sock) < 0)
return -1;
return sock;
}
2016-01-25 12:28:52 +00:00
int open_udp_socket(const struct arguments *args, const struct udp_session *cur) {
int sock;
// Get UDP socket
sock = socket(cur->version == 4 ? PF_INET : PF_INET6, SOCK_DGRAM, IPPROTO_UDP);
if (sock < 0) {
log_android(ANDROID_LOG_ERROR, "UDP socket error %d: %s", errno, strerror(errno));
return -1;
}
// Protect socket
if (protect_socket(args, sock) < 0)
return -1;
2016-02-03 07:47:10 +00:00
// Check for broadcast/multicast
2016-01-25 12:28:52 +00:00
if (cur->version == 4) {
uint32_t broadcast4 = INADDR_BROADCAST;
if (memcmp(&cur->daddr.ip4, &broadcast4, sizeof(broadcast4)) == 0) {
2016-02-03 07:47:10 +00:00
log_android(ANDROID_LOG_WARN, "UDP4 broadcast");
2016-01-25 12:28:52 +00:00
int on = 1;
if (setsockopt(sock, SOL_SOCKET, SO_BROADCAST, &on, sizeof(on)))
log_android(ANDROID_LOG_ERROR, "UDP setsockopt SO_BROADCAST error %d: %s",
2016-01-25 12:28:52 +00:00
errno, strerror(errno));
}
} else {
2016-02-03 07:47:10 +00:00
// http://man7.org/linux/man-pages/man7/ipv6.7.html
if (*((uint8_t *) &cur->daddr.ip6) == 0xFF) {
log_android(ANDROID_LOG_WARN, "UDP6 broadcast");
int loop = 1; // true
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_LOOP, &loop, sizeof(loop)))
log_android(ANDROID_LOG_ERROR, "UDP setsockopt IPV6_MULTICAST_LOOP error %d: %s",
errno, strerror(errno));
int ttl = -1; // route default
if (setsockopt(sock, IPPROTO_IPV6, IPV6_MULTICAST_HOPS, &ttl, sizeof(ttl)))
log_android(ANDROID_LOG_ERROR, "UDP setsockopt IPV6_MULTICAST_HOPS error %d: %s",
errno, strerror(errno));
struct ipv6_mreq mreq6;
memcpy(&mreq6.ipv6mr_multiaddr, &cur->daddr.ip6, sizeof(struct in6_addr));
mreq6.ipv6mr_interface = INADDR_ANY;
if (setsockopt(sock, IPPROTO_IPV6, IPV6_ADD_MEMBERSHIP, &mreq6, sizeof(mreq6)))
log_android(ANDROID_LOG_ERROR, "UDP setsockopt IPV6_ADD_MEMBERSHIP error %d: %s",
errno, strerror(errno));
}
2016-01-25 12:28:52 +00:00
}
return sock;
}
int open_tcp_socket(const struct arguments *args, const struct tcp_session *cur) {
2016-01-23 19:46:27 +00:00
int sock;
2016-01-13 09:26:06 +00:00
// Get TCP socket
2016-01-24 11:50:40 +00:00
// TODO socket options?
2016-01-25 17:57:05 +00:00
if ((sock = socket(cur->version == 4 ? PF_INET : PF_INET6, SOCK_STREAM, 0)) < 0) {
log_android(ANDROID_LOG_ERROR, "socket error %d: %s", errno, strerror(errno));
2016-01-13 09:26:06 +00:00
return -1;
}
// Protect
2016-01-20 08:24:34 +00:00
if (protect_socket(args, sock) < 0)
2016-01-13 09:26:06 +00:00
return -1;
2016-01-12 12:15:21 +00:00
2016-01-18 20:54:45 +00:00
// Set non blocking
2016-01-23 19:46:27 +00:00
int flags = fcntl(sock, F_GETFL, 0);
2016-01-18 20:54:45 +00:00
if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
log_android(ANDROID_LOG_ERROR, "fcntl socket O_NONBLOCK error %d: %s",
errno, strerror(errno));
2016-01-13 10:11:11 +00:00
return -1;
}
2016-01-25 17:57:05 +00:00
// Build target address
struct sockaddr_in addr4;
struct sockaddr_in6 addr6;
if (cur->version == 4) {
addr4.sin_family = AF_INET;
2016-01-25 17:57:05 +00:00
addr4.sin_addr.s_addr = (__be32) cur->daddr.ip4;
addr4.sin_port = cur->dest;
} else {
addr6.sin6_family = AF_INET6;
2016-01-25 17:57:05 +00:00
memcpy(&addr6.sin6_addr, &cur->daddr.ip6, 16);
addr6.sin6_port = cur->dest;
}
2016-01-13 09:26:06 +00:00
// Initiate connect
2016-01-25 17:57:05 +00:00
int err = connect(sock,
(const struct sockaddr *) (cur->version == 4 ? &addr4 : &addr6),
(socklen_t) (cur->version == 4
? sizeof(struct sockaddr_in)
: sizeof(struct sockaddr_in6)));
2016-01-13 09:26:06 +00:00
if (err < 0 && errno != EINPROGRESS) {
log_android(ANDROID_LOG_ERROR, "connect error %d: %s", errno, strerror(errno));
2016-01-13 09:26:06 +00:00
return -1;
2016-01-11 22:06:35 +00:00
}
2016-01-13 09:26:06 +00:00
2016-02-05 09:43:28 +00:00
// Set blocking
if (fcntl(sock, F_SETFL, flags & ~O_NONBLOCK) < 0) {
log_android(ANDROID_LOG_ERROR, "fcntl socket ~O_NONBLOCK error %d: %s",
errno, strerror(errno));
return -1;
}
2016-01-13 09:26:06 +00:00
return sock;
2016-01-11 22:06:35 +00:00
}
2016-01-23 19:46:27 +00:00
int32_t get_local_port(const int sock) {
2016-01-13 09:26:06 +00:00
struct sockaddr_in sin;
2016-01-23 19:46:27 +00:00
socklen_t len = sizeof(sin);
if (getsockname(sock, (struct sockaddr *) &sin, &len) < 0) {
log_android(ANDROID_LOG_ERROR, "getsockname error %d: %s", errno, strerror(errno));
2016-01-13 09:26:06 +00:00
return -1;
} else
return ntohs(sin.sin_port);
}
2016-01-24 11:50:40 +00:00
int write_syn_ack(const struct arguments *args, struct tcp_session *cur) {
if (write_tcp(args, cur, NULL, 0, 1, 1, 1, 0, 0) < 0) {
2016-01-18 14:29:01 +00:00
cur->state = TCP_TIME_WAIT;
return -1;
}
return 0;
}
2016-01-24 11:50:40 +00:00
int write_ack(const struct arguments *args, struct tcp_session *cur, size_t bytes) {
if (write_tcp(args, cur, NULL, 0, bytes, 0, 1, 0, 0) < 0) {
2016-01-18 14:29:01 +00:00
cur->state = TCP_TIME_WAIT;
return -1;
}
return 0;
}
2016-01-23 19:46:27 +00:00
int write_data(const struct arguments *args, struct tcp_session *cur,
2016-01-24 11:50:40 +00:00
const uint8_t *buffer, size_t length) {
if (write_tcp(args, cur, buffer, length, 0, 0, 1, 0, 0) < 0) {
2016-01-18 14:29:01 +00:00
cur->state = TCP_TIME_WAIT;
2016-01-20 19:07:35 +00:00
return -1;
}
return 0;
}
2016-01-24 11:50:40 +00:00
int write_fin_ack(const struct arguments *args, struct tcp_session *cur, size_t bytes) {
if (write_tcp(args, cur, NULL, 0, bytes, 0, 1, 1, 0) < 0) {
2016-01-18 14:29:01 +00:00
cur->state = TCP_TIME_WAIT;
return -1;
}
return 0;
}
2016-01-24 11:50:40 +00:00
void write_rst(const struct arguments *args, struct tcp_session *cur) {
write_tcp(args, cur, NULL, 0, 0, 0, 0, 0, 1);
2016-01-26 17:53:17 +00:00
if (cur->state != TCP_CLOSE)
cur->state = TCP_TIME_WAIT;
2016-01-18 14:29:01 +00:00
}
2016-01-17 19:40:40 +00:00
2016-01-25 17:57:05 +00:00
// TODO common UDP/TCP
2016-02-02 13:31:44 +00:00
ssize_t write_icmp(const struct arguments *args, const struct icmp_session *cur,
uint8_t *data, size_t datalen) {
size_t len;
u_int8_t *buffer;
struct icmp *icmp = (struct icmp *) data;
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
// Build packet
if (cur->version == 4) {
len = sizeof(struct iphdr) + datalen;
buffer = malloc(len);
struct iphdr *ip4 = (struct iphdr *) buffer;
if (datalen)
memcpy(buffer + sizeof(struct iphdr), data, datalen);
// Build IP4 header
memset(ip4, 0, sizeof(struct iphdr));
ip4->version = 4;
ip4->ihl = sizeof(struct iphdr) >> 2;
ip4->tot_len = htons(len);
ip4->ttl = IPDEFTTL;
ip4->protocol = IPPROTO_ICMP;
ip4->saddr = cur->daddr.ip4;
ip4->daddr = cur->saddr.ip4;
// Calculate IP4 checksum
ip4->check = ~calc_checksum(0, (uint8_t *) ip4, sizeof(struct iphdr));
}
else {
len = sizeof(struct ip6_hdr) + datalen;
buffer = malloc(len);
struct ip6_hdr *ip6 = (struct ip6_hdr *) buffer;
if (datalen)
memcpy(buffer + sizeof(struct ip6_hdr), data, datalen);
// Build IP6 header
memset(ip6, 0, sizeof(struct ip6_hdr));
ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = 0;
ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len - sizeof(struct ip6_hdr));
ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_ICMPV6;
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = IPDEFTTL;
ip6->ip6_ctlun.ip6_un2_vfc = IPV6_VERSION;
memcpy(&(ip6->ip6_src), &cur->daddr.ip6, 16);
memcpy(&(ip6->ip6_dst), &cur->saddr.ip6, 16);
}
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
cur->version == 4 ? &cur->saddr.ip4 : &cur->saddr.ip6, source, sizeof(source));
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
cur->version == 4 ? &cur->daddr.ip4 : &cur->daddr.ip6, dest, sizeof(dest));
// Send raw ICMP message
log_android(ANDROID_LOG_DEBUG,
"ICMP sending to tun %d from %s to %s data %u type %d code %d id %x seq %d",
args->tun, dest, source, datalen,
icmp->icmp_type, icmp->icmp_code, icmp->icmp_id, icmp->icmp_seq);
ssize_t res = write(args->tun, buffer, len);
// Write PCAP record
if (res >= 0) {
if (pcap_file != NULL)
write_pcap_rec(buffer, (size_t) res);
}
else
log_android(ANDROID_LOG_WARN, "ICMP write error %d: %s", errno, strerror(errno));
free(buffer);
if (res != len) {
log_android(ANDROID_LOG_ERROR, "write %d wrote %d", res, len);
return -1;
}
return res;
}
2016-01-23 19:46:27 +00:00
ssize_t write_udp(const struct arguments *args, const struct udp_session *cur,
2016-01-24 11:50:40 +00:00
uint8_t *data, size_t datalen) {
size_t len;
u_int8_t *buffer;
struct udphdr *udp;
uint16_t csum;
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
2016-01-20 13:11:04 +00:00
// Build packet
if (cur->version == 4) {
len = sizeof(struct iphdr) + sizeof(struct udphdr) + datalen;
2016-01-25 17:57:05 +00:00
buffer = malloc(len);
struct iphdr *ip4 = (struct iphdr *) buffer;
udp = (struct udphdr *) (buffer + sizeof(struct iphdr));
if (datalen)
memcpy(buffer + sizeof(struct iphdr) + sizeof(struct udphdr), data, datalen);
2016-01-25 12:28:52 +00:00
// Build IP4 header
2016-01-25 17:57:05 +00:00
memset(ip4, 0, sizeof(struct iphdr));
ip4->version = 4;
ip4->ihl = sizeof(struct iphdr) >> 2;
ip4->tot_len = htons(len);
ip4->ttl = IPDEFTTL;
ip4->protocol = IPPROTO_UDP;
ip4->saddr = cur->daddr.ip4;
ip4->daddr = cur->saddr.ip4;
2016-01-25 12:28:52 +00:00
// Calculate IP4 checksum
ip4->check = ~calc_checksum(0, (uint8_t *) ip4, sizeof(struct iphdr));
2016-01-25 17:57:05 +00:00
// Calculate UDP4 checksum
struct ippseudo pseudo;
2016-01-25 17:57:05 +00:00
memset(&pseudo, 0, sizeof(struct ippseudo));
pseudo.ippseudo_src.s_addr = (__be32) ip4->saddr;
pseudo.ippseudo_dst.s_addr = (__be32) ip4->daddr;
pseudo.ippseudo_p = ip4->protocol;
pseudo.ippseudo_len = htons(sizeof(struct udphdr) + datalen);
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ippseudo));
}
else {
len = sizeof(struct ip6_hdr) + sizeof(struct udphdr) + datalen;
2016-01-25 17:57:05 +00:00
buffer = malloc(len);
struct ip6_hdr *ip6 = (struct ip6_hdr *) buffer;
udp = (struct udphdr *) (buffer + sizeof(struct ip6_hdr));
if (datalen)
memcpy(buffer + sizeof(struct ip6_hdr) + sizeof(struct udphdr), data, datalen);
2016-01-25 12:28:52 +00:00
// Build IP6 header
2016-01-25 17:57:05 +00:00
memset(ip6, 0, sizeof(struct ip6_hdr));
ip6->ip6_ctlun.ip6_un1.ip6_un1_flow = 0;
ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len - sizeof(struct ip6_hdr));
ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_UDP;
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = IPDEFTTL;
ip6->ip6_ctlun.ip6_un2_vfc = IPV6_VERSION;
2016-01-25 12:28:52 +00:00
memcpy(&(ip6->ip6_src), &cur->daddr.ip6, 16);
memcpy(&(ip6->ip6_dst), &cur->saddr.ip6, 16);
2016-01-25 17:57:05 +00:00
// Calculate UDP6 checksum
struct ip6_hdr_pseudo pseudo;
2016-01-25 17:57:05 +00:00
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo));
memcpy(&pseudo.ip6ph_src, &ip6->ip6_dst, 16);
memcpy(&pseudo.ip6ph_dst, &ip6->ip6_src, 16);
pseudo.ip6ph_len = ip6->ip6_ctlun.ip6_un1.ip6_un1_plen;
pseudo.ip6ph_nxt = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo));
}
2016-01-20 13:11:04 +00:00
// Build UDP header
2016-01-25 17:57:05 +00:00
memset(udp, 0, sizeof(struct udphdr));
2016-01-20 13:11:04 +00:00
udp->source = cur->dest;
udp->dest = cur->source;
udp->len = htons(sizeof(struct udphdr) + datalen);
2016-01-25 12:28:52 +00:00
// Continue checksum
csum = calc_checksum(csum, (uint8_t *) udp, sizeof(struct udphdr));
csum = calc_checksum(csum, data, datalen);
udp->check = ~csum;
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
cur->version == 4 ? &cur->saddr.ip4 : &cur->saddr.ip6, source, sizeof(source));
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
cur->version == 4 ? &cur->daddr.ip4 : &cur->daddr.ip6, dest, sizeof(dest));
2016-01-20 13:11:04 +00:00
// Send packet
log_android(ANDROID_LOG_DEBUG,
2016-01-25 17:57:05 +00:00
"UDP sending to tun %d from %s/%u to %s/%u data %u",
2016-02-02 13:31:44 +00:00
args->tun, dest, ntohs(cur->dest), source, ntohs(cur->source), len);
2016-01-20 13:11:04 +00:00
2016-01-24 11:50:40 +00:00
ssize_t res = write(args->tun, buffer, len);
2016-01-20 13:11:04 +00:00
2016-01-28 17:39:13 +00:00
// Write PCAP record
2016-01-25 12:28:52 +00:00
if (res >= 0) {
if (pcap_file != NULL)
2016-01-29 06:59:35 +00:00
write_pcap_rec(buffer, (size_t) res);
2016-01-25 12:28:52 +00:00
}
else
log_android(ANDROID_LOG_WARN, "UDP write error %d: %s", errno, strerror(errno));
2016-01-20 13:11:04 +00:00
free(buffer);
2016-01-26 17:53:17 +00:00
if (res != len) {
log_android(ANDROID_LOG_ERROR, "write %d wrote %d", res, len);
return -1;
}
2016-01-20 13:11:04 +00:00
return res;
}
2016-01-23 19:46:27 +00:00
ssize_t write_tcp(const struct arguments *args, const struct tcp_session *cur,
const uint8_t *data, size_t datalen, size_t confirm,
2016-01-24 11:50:40 +00:00
int syn, int ack, int fin, int rst) {
2016-01-25 17:57:05 +00:00
size_t len;
u_int8_t *buffer;
struct tcphdr *tcp;
uint16_t csum;
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
2016-01-14 14:02:32 +00:00
// Build packet
2016-01-25 17:57:05 +00:00
if (cur->version == 4) {
len = sizeof(struct iphdr) + sizeof(struct tcphdr) + datalen;
buffer = malloc(len);
struct iphdr *ip4 = (struct iphdr *) buffer;
tcp = (struct tcphdr *) (buffer + sizeof(struct iphdr));
if (datalen)
memcpy(buffer + sizeof(struct iphdr) + sizeof(struct tcphdr), data, datalen);
// Build IP4 header
memset(ip4, 0, sizeof(struct iphdr));
ip4->version = 4;
ip4->ihl = sizeof(struct iphdr) >> 2;
ip4->tot_len = htons(len);
ip4->ttl = IPDEFTTL;
ip4->protocol = IPPROTO_TCP;
ip4->saddr = cur->daddr.ip4;
ip4->daddr = cur->saddr.ip4;
// Calculate IP4 checksum
ip4->check = ~calc_checksum(0, (uint8_t *) ip4, sizeof(struct iphdr));
// Calculate TCP4 checksum
struct ippseudo pseudo;
memset(&pseudo, 0, sizeof(struct ippseudo));
pseudo.ippseudo_src.s_addr = (__be32) ip4->saddr;
pseudo.ippseudo_dst.s_addr = (__be32) ip4->daddr;
pseudo.ippseudo_p = ip4->protocol;
pseudo.ippseudo_len = htons(sizeof(struct tcphdr) + datalen);
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ippseudo));
}
else {
len = sizeof(struct ip6_hdr) + sizeof(struct tcphdr) + datalen;
buffer = malloc(len);
struct ip6_hdr *ip6 = (struct ip6_hdr *) buffer;
tcp = (struct tcphdr *) (buffer + sizeof(struct ip6_hdr));
if (datalen)
memcpy(buffer + sizeof(struct ip6_hdr) + sizeof(struct tcphdr), data, datalen);
// Build IP6 header
memset(ip6, 0, sizeof(struct ip6_hdr));
ip6->ip6_ctlun.ip6_un1.ip6_un1_plen = htons(len - sizeof(struct ip6_hdr));
ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt = IPPROTO_TCP;
ip6->ip6_ctlun.ip6_un1.ip6_un1_hlim = IPDEFTTL;
ip6->ip6_ctlun.ip6_un2_vfc = 0x60;
memcpy(&(ip6->ip6_src), &cur->daddr.ip6, 16);
memcpy(&(ip6->ip6_dst), &cur->saddr.ip6, 16);
// Calculate TCP6 checksum
struct ip6_hdr_pseudo pseudo;
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo));
memcpy(&pseudo.ip6ph_src, &ip6->ip6_dst, 16);
memcpy(&pseudo.ip6ph_dst, &ip6->ip6_src, 16);
pseudo.ip6ph_len = ip6->ip6_ctlun.ip6_un1.ip6_un1_plen;
pseudo.ip6ph_nxt = ip6->ip6_ctlun.ip6_un1.ip6_un1_nxt;
csum = calc_checksum(0, (uint8_t *) &pseudo, sizeof(struct ip6_hdr_pseudo));
}
2016-01-14 14:02:32 +00:00
// Build TCP header
2016-01-25 17:57:05 +00:00
memset(tcp, 0, sizeof(struct tcphdr));
2016-01-14 14:02:32 +00:00
tcp->source = cur->dest;
tcp->dest = cur->source;
2016-01-17 05:41:32 +00:00
tcp->seq = htonl(cur->local_seq);
2016-01-17 09:42:21 +00:00
tcp->ack_seq = htonl((uint32_t) (cur->remote_seq + confirm));
2016-01-14 14:02:32 +00:00
tcp->doff = sizeof(struct tcphdr) >> 2;
2016-01-23 19:46:27 +00:00
tcp->syn = (__u16) syn;
tcp->ack = (__u16) ack;
tcp->fin = (__u16) fin;
tcp->rst = (__u16) rst;
2016-01-22 18:03:32 +00:00
tcp->window = htons(TCP_RECV_WINDOW);
2016-01-25 17:57:05 +00:00
tcp->urg_ptr;
2016-01-14 14:02:32 +00:00
2016-01-17 09:42:21 +00:00
if (!tcp->ack)
tcp->ack_seq = 0;
2016-01-25 17:57:05 +00:00
// Continue checksum
2016-01-23 19:46:27 +00:00
csum = calc_checksum(csum, (uint8_t *) tcp, sizeof(struct tcphdr));
2016-01-25 17:57:05 +00:00
csum = calc_checksum(csum, data, datalen);
2016-01-22 16:00:50 +00:00
tcp->check = ~csum;
2016-01-14 14:02:32 +00:00
2016-01-25 17:57:05 +00:00
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
cur->version == 4 ? &cur->saddr.ip4 : &cur->saddr.ip6, source, sizeof(source));
inet_ntop(cur->version == 4 ? AF_INET : AF_INET6,
cur->version == 4 ? &cur->daddr.ip4 : &cur->daddr.ip6, dest, sizeof(dest));
2016-01-14 14:02:32 +00:00
// Send packet
log_android(ANDROID_LOG_DEBUG,
2016-01-25 17:57:05 +00:00
"TCP sending%s%s%s%s to tun %s/%u seq %u ack %u data %u confirm %u",
(tcp->syn ? " SYN" : ""),
(tcp->ack ? " ACK" : ""),
(tcp->fin ? " FIN" : ""),
(tcp->rst ? " RST" : ""),
2016-01-25 17:57:05 +00:00
dest, ntohs(tcp->dest),
ntohl(tcp->seq) - cur->local_start,
ntohl(tcp->ack_seq) - cur->remote_start,
datalen, confirm);
2016-01-18 20:18:23 +00:00
2016-01-24 11:50:40 +00:00
ssize_t res = write(args->tun, buffer, len);
2016-01-14 14:02:32 +00:00
2016-01-17 19:40:40 +00:00
// Write pcap record
if (res >= 0) {
if (pcap_file != NULL)
2016-01-29 06:59:35 +00:00
write_pcap_rec(buffer, (size_t) res);
} else
log_android(ANDROID_LOG_ERROR, "TCP write%s%s%s%s data %d confirm %d error %d: %s",
(tcp->syn ? " SYN" : ""),
(tcp->ack ? " ACK" : ""),
(tcp->fin ? " FIN" : ""),
(tcp->rst ? " RST" : ""),
datalen, confirm,
errno, strerror((errno)));
2016-01-17 09:42:21 +00:00
2016-01-14 14:02:32 +00:00
free(buffer);
2016-01-26 17:53:17 +00:00
if (res != len) {
log_android(ANDROID_LOG_ERROR, "TCP write %d wrote %d", res, len);
return -1;
}
2016-01-14 14:02:32 +00:00
return res;
}
2016-01-19 08:49:47 +00:00
uint8_t char2nible(const char c) {
2016-01-23 19:46:27 +00:00
if (c >= '0' && c <= '9') return (uint8_t) (c - '0');
if (c >= 'a' && c <= 'f') return (uint8_t) ((c - 'a') + 10);
if (c >= 'A' && c <= 'F') return (uint8_t) ((c - 'A') + 10);
2016-01-19 08:49:47 +00:00
return 255;
}
void hex2bytes(const char *hex, uint8_t *buffer) {
2016-01-23 19:46:27 +00:00
size_t len = strlen(hex);
2016-01-19 08:49:47 +00:00
for (int i = 0; i < len; i += 2)
buffer[i / 2] = (char2nible(hex[i]) << 4) | char2nible(hex[i + 1]);
}
jint get_uid(const int protocol, const int version,
const void *saddr, const uint16_t sport, int dump) {
2016-01-10 07:14:47 +00:00
char line[250];
2016-01-19 08:49:47 +00:00
char hex[16 * 2 + 1];
2016-01-10 07:14:47 +00:00
int fields;
2016-01-19 08:49:47 +00:00
uint8_t addr4[4];
uint8_t addr6[16];
2016-01-23 19:46:27 +00:00
int port;
jint uid = -1;
2016-01-10 07:14:47 +00:00
2016-01-28 17:39:13 +00:00
#ifdef PROFILE_UID
float mselapsed;
struct timeval start, end;
gettimeofday(&start, NULL);
#endif
2016-01-10 07:14:47 +00:00
// Get proc file name
char *fn = NULL;
2016-02-02 13:31:44 +00:00
if (protocol == IPPROTO_ICMP && version == 4)
fn = "/proc/net/icmp";
else if (protocol == IPPROTO_ICMPV6 && version == 6)
fn = "/proc/net/icmp6";
else if (protocol == IPPROTO_TCP)
2016-01-10 07:14:47 +00:00
fn = (version == 4 ? "/proc/net/tcp" : "/proc/net/tcp6");
else if (protocol == IPPROTO_UDP)
fn = (version == 4 ? "/proc/net/udp" : "/proc/net/udp6");
else
2016-01-15 11:13:12 +00:00
return uid;
2016-01-10 07:14:47 +00:00
2016-01-19 08:49:47 +00:00
if (dump) {
char source[INET6_ADDRSTRLEN + 1];
2016-01-19 08:49:47 +00:00
inet_ntop(version == 4 ? AF_INET : AF_INET6, saddr, source, sizeof(source));
log_android(ANDROID_LOG_INFO, "Searching %s/%u in %s", source, sport, fn);
}
2016-01-10 07:14:47 +00:00
// Open proc file
FILE *fd = fopen(fn, "r");
if (fd == NULL) {
log_android(ANDROID_LOG_ERROR, "fopen %s error %d: %s", fn, errno, strerror(errno));
2016-01-15 11:13:12 +00:00
return uid;
2016-01-10 07:14:47 +00:00
}
// Scan proc file
2016-01-15 11:13:12 +00:00
jint u;
2016-01-10 07:14:47 +00:00
int i = 0;
while (fgets(line, sizeof(line), fd) != NULL) {
if (i++) {
if (version == 4)
fields = sscanf(
line,
2016-01-19 08:49:47 +00:00
"%*d: %8s:%X %*X:%*X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld",
hex, &port, &u);
2016-01-10 07:14:47 +00:00
else
fields = sscanf(
line,
2016-01-19 08:49:47 +00:00
"%*d: %32s:%X %*X:%*X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld",
hex, &port, &u);
if (fields == 3) {
hex2bytes(hex, version == 4 ? addr4 : addr6);
if (version == 4)
((uint32_t *) addr4)[0] = htonl(((uint32_t *) addr4)[0]);
for (int w = 0; w < 4; w++)
((uint32_t *) addr6)[w] = htonl(((uint32_t *) addr6)[w]);
if (dump) {
char source[INET6_ADDRSTRLEN + 1];
2016-01-19 08:49:47 +00:00
inet_ntop(version == 4 ? AF_INET : AF_INET6,
version == 4 ? addr4 : addr6,
source, sizeof(source));
log_android(ANDROID_LOG_INFO, "%s/%u %d", source, port, u);
2016-01-19 08:49:47 +00:00
}
2016-01-15 11:13:12 +00:00
if (port == sport) {
uid = u;
if (memcmp(version == 4 ? addr4 : addr6, saddr, version == 4 ? 4 : 16) == 0)
2016-01-19 08:49:47 +00:00
break;
2016-01-10 11:51:02 +00:00
}
2016-01-15 11:13:12 +00:00
} else
log_android(ANDROID_LOG_ERROR, "Invalid field #%d: %s", fields, line);
2016-01-09 20:17:42 +00:00
}
}
2016-01-10 07:14:47 +00:00
2016-01-15 09:28:31 +00:00
if (fclose(fd))
log_android(ANDROID_LOG_ERROR, "fclose %s error %d: %s", fn, errno, strerror(errno));
2016-01-28 17:39:13 +00:00
#ifdef PROFILE_UID
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_UID)
log_android(ANDROID_LOG_WARN, "get uid ip %f", mselapsed);
#endif
2016-01-10 07:14:47 +00:00
2016-01-15 11:13:12 +00:00
return uid;
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-23 19:46:27 +00:00
uint16_t calc_checksum(uint16_t start, const uint8_t *buffer, size_t length) {
2016-01-22 16:00:50 +00:00
register uint32_t sum = start;
2016-01-23 19:46:27 +00:00
register uint16_t *buf = (uint16_t *) buffer;
register size_t len = length;
2016-01-12 12:15:21 +00:00
2016-01-15 11:57:32 +00:00
while (len > 1) {
sum += *buf++;
len -= 2;
2016-01-12 12:15:21 +00:00
}
2016-01-15 11:57:32 +00:00
if (len > 0)
sum += *((uint8_t *) buf);
while (sum >> 16)
sum = (sum & 0xFFFF) + (sum >> 16);
2016-01-22 16:00:50 +00:00
return (uint16_t) sum;
2016-01-12 12:15:21 +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
}
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;
}
int sdk_int(JNIEnv *env) {
jclass clsVersion = jniFindClass(env, "android/os/Build$VERSION");
jfieldID fid = (*env)->GetStaticFieldID(env, clsVersion, "SDK_INT", "I");
return (*env)->GetStaticIntField(env, clsVersion, fid);
}
2016-01-25 12:58:44 +00:00
typedef int (*PFN_SYS_PROP_GET)(const char *, char *);
int __system_property_get(JNIEnv *env, const char *name, char *value) {
2016-01-25 12:58:44 +00:00
static PFN_SYS_PROP_GET __real_system_property_get = NULL;
if (!__real_system_property_get) {
void *handle = dlopen("libc.so", sdk_int(env) >= 21 ? RTLD_NOLOAD : 0);
2016-01-25 12:58:44 +00:00
if (!handle)
log_android(ANDROID_LOG_ERROR, "dlopen(libc.so): %s", dlerror());
else {
2016-01-29 18:10:23 +00:00
__real_system_property_get = (PFN_SYS_PROP_GET) dlsym(
handle, "__system_property_get");
2016-01-25 12:58:44 +00:00
if (!__real_system_property_get)
log_android(ANDROID_LOG_ERROR, "dlsym(__system_property_get()): %s", dlerror());
}
}
return (*__real_system_property_get)(name, value);
}
void log_android(int prio, const char *fmt, ...) {
2016-01-17 15:08:40 +00:00
if (prio >= loglevel) {
char line[1024];
2016-01-17 13:20:07 +00:00
va_list argptr;
va_start(argptr, fmt);
2016-01-17 15:08:40 +00:00
vsprintf(line, fmt, argptr);
__android_log_print(prio, TAG, line);
2016-01-17 13:20:07 +00:00
va_end(argptr);
}
}
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
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);
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, jpacket);
(*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, "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;
jboolean 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);
const char *signature = "(Leu/faircode/netguard/Packet;)Z";
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
jboolean jallowed = (*args->env)->CallBooleanMethod(
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);
(*args->env)->DeleteLocalRef(args->env, jpacket);
(*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_address_allowed %f", mselapsed);
2016-01-28 10:58:39 +00:00
#endif
return jallowed;
}
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-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);
*/
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-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);
(*env)->DeleteLocalRef(env, jdata);
(*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-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 20:37:51 +00:00
void write_pcap_hdr() {
struct pcap_hdr_s pcap_hdr;
pcap_hdr.magic_number = 0xa1b2c3d4;
pcap_hdr.version_major = 2;
pcap_hdr.version_minor = 4;
pcap_hdr.thiszone = 0;
pcap_hdr.sigfigs = 0;
2016-01-22 12:06:50 +00:00
pcap_hdr.snaplen = MAX_PCAP_RECORD;
2016-01-18 20:37:51 +00:00
pcap_hdr.network = LINKTYPE_RAW;
write_pcap(&pcap_hdr, sizeof(struct pcap_hdr_s));
}
2016-01-23 19:46:27 +00:00
void write_pcap_rec(const uint8_t *buffer, size_t length) {
2016-01-18 20:37:51 +00:00
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts))
log_android(ANDROID_LOG_ERROR, "clock_gettime error %d: %s", errno, strerror(errno));
2016-01-23 19:46:27 +00:00
size_t plen = (length < MAX_PCAP_RECORD ? length : MAX_PCAP_RECORD);
2016-01-22 16:00:50 +00:00
struct pcaprec_hdr_s pcap_rec;
2016-01-18 20:37:51 +00:00
2016-01-23 19:46:27 +00:00
pcap_rec.ts_sec = (guint32_t) ts.tv_sec;
pcap_rec.ts_usec = (guint32_t) (ts.tv_nsec / 1000);
pcap_rec.incl_len = (guint32_t) plen;
pcap_rec.orig_len = (guint32_t) length;
2016-01-18 20:37:51 +00:00
2016-01-22 16:00:50 +00:00
write_pcap(&pcap_rec, sizeof(struct pcaprec_hdr_s));
write_pcap(buffer, plen);
2016-01-18 20:37:51 +00:00
}
2016-01-22 12:06:50 +00:00
void write_pcap(const void *ptr, size_t len) {
if (fwrite(ptr, len, 1, pcap_file) < 1)
log_android(ANDROID_LOG_ERROR, "PCAP fwrite error %d: %s", errno, strerror(errno));
else {
long fsize = ftell(pcap_file);
2016-02-05 09:43:28 +00:00
log_android(ANDROID_LOG_VERBOSE, "PCAP wrote %d @%ld", len, fsize);
2016-01-22 12:06:50 +00:00
if (fsize > MAX_PCAP_FILE) {
log_android(ANDROID_LOG_WARN, "PCAP truncate @%ld", fsize);
2016-01-22 12:06:50 +00:00
if (ftruncate(fileno(pcap_file), sizeof(struct pcap_hdr_s)))
log_android(ANDROID_LOG_ERROR, "PCAP ftruncate error %d: %s",
errno, strerror(errno));
else {
if (!lseek(fileno(pcap_file), sizeof(struct pcap_hdr_s), SEEK_SET))
log_android(ANDROID_LOG_ERROR, "PCAP ftruncate error %d: %s",
errno, strerror(errno));
}
}
}
}
2016-01-23 11:48:17 +00:00
char *trim(char *str) {
while (isspace(*str))
str++;
if (*str == 0)
return str;
char *end = str + strlen(str) - 1;
while (end > str && isspace(*end))
end--;
*(end + 1) = 0;
return str;
}
2016-01-16 11:32:55 +00:00
const char *strstate(const int state) {
switch (state) {
case TCP_ESTABLISHED:
return "ESTABLISHED";
2016-01-16 11:32:55 +00:00
case TCP_SYN_SENT:
return "SYN_SENT";
2016-01-16 11:32:55 +00:00
case TCP_SYN_RECV:
return "SYN_RECV";
2016-01-16 11:32:55 +00:00
case TCP_FIN_WAIT1:
return "FIN_WAIT1";
2016-01-16 11:32:55 +00:00
case TCP_FIN_WAIT2:
return "FIN_WAIT2";
2016-01-16 11:32:55 +00:00
case TCP_TIME_WAIT:
return "TIME_WAIT";
2016-01-16 11:32:55 +00:00
case TCP_CLOSE:
return "CLOSE";
2016-01-16 11:32:55 +00:00
case TCP_CLOSE_WAIT:
return "CLOSE_WAIT";
2016-01-16 11:32:55 +00:00
case TCP_LAST_ACK:
return "LAST_ACK";
2016-01-16 11:32:55 +00:00
case TCP_LISTEN:
return "LISTEN";
2016-01-16 11:32:55 +00:00
case TCP_CLOSING:
return "CLOSING";
2016-01-16 11:32:55 +00:00
default:
2016-01-23 19:46:27 +00:00
return "UNKNOWN";
2016-01-16 11:32:55 +00:00
}
}
2016-01-23 19:46:27 +00:00
char *hex(const u_int8_t *data, const size_t len) {
2016-01-12 20:15:25 +00:00
char hex_str[] = "0123456789ABCDEF";
2016-01-12 12:15:21 +00:00
2016-01-15 11:13:12 +00:00
char *hexout;
hexout = (char *) malloc(len * 3 + 1); // TODO free
2016-01-12 12:15:21 +00:00
for (size_t i = 0; i < len; i++) {
2016-01-13 19:17:21 +00:00
hexout[i * 3 + 0] = hex_str[(data[i] >> 4) & 0x0F];
hexout[i * 3 + 1] = hex_str[(data[i]) & 0x0F];
hexout[i * 3 + 2] = ' ';
2016-01-12 12:15:21 +00:00
}
2016-01-23 14:50:18 +00:00
hexout[len * 3] = 0;
2016-01-13 19:17:21 +00:00
return hexout;
2016-01-12 16:19:27 +00:00
}