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

1297 lines
44 KiB
C
Raw Normal View History

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-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>
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-01-09 11:10:11 +00:00
2016-01-12 20:15:25 +00:00
// TODO TCP fragmentation
// TODO TCP push
2016-01-17 13:20:07 +00:00
// TODO TCPv6
// TODO UDPv4
// TODO UDPv6
// TODO DHCP
2016-01-15 09:28:31 +00:00
// TODO log allowed traffic
2016-01-12 20:15:25 +00:00
// TODO header file
2016-01-15 11:57:32 +00:00
// TODO fix warnings
// Window size < 2^31: x <= y: (uint32_t)(y-x) < 0x80000000
2016-01-12 20:15:25 +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-09 11:10:11 +00:00
#define TAG "NetGuard.JNI"
#define MAXPKT 32768
// TODO TCP parameters (net.inet.tcp.keepinit, etc)
2016-01-15 11:13:12 +00:00
#define SELECTWAIT 10 // seconds
#define TCPTIMEOUT 300 // seconds ~net.inet.tcp.keepidle
2016-01-15 07:48:18 +00:00
#define TCPTTL 64
#define TCPWINDOW 32768
2016-01-15 11:13:12 +00:00
#define UIDDELAY 10 // milliseconds
2016-01-17 09:42:21 +00:00
#define MAXPCAP 80
2016-01-11 22:06:35 +00:00
2016-01-13 08:23:21 +00:00
struct arguments {
jobject instance;
int tun;
};
2016-01-16 08:07:04 +00:00
struct session {
2016-01-12 08:45:58 +00:00
time_t time;
2016-01-15 09:28:31 +00:00
int uid;
2016-01-12 20:15:25 +00:00
uint32_t remote_seq; // confirmed bytes received, host notation
uint32_t local_seq; // confirmed bytes sent, host notation
2016-01-17 05:48:33 +00:00
uint32_t remote_start;
uint32_t local_start;
2016-01-12 12:15:21 +00:00
int32_t saddr; // network notation
__be16 source; // network notation
int32_t daddr; // network notation
__be16 dest; // network notation
uint8_t state;
jint socket;
uint32_t lport; // host notation
2016-01-16 08:07:04 +00:00
struct session *next;
2016-01-11 22:06:35 +00:00
};
2016-01-17 09:42:21 +00:00
// https://wiki.wireshark.org/Development/LibpcapFileFormat
typedef unsigned short guint16_t;
typedef unsigned int guint32_t;
typedef signed int gint32_t;
typedef struct pcap_hdr_s {
guint32_t magic_number;
guint16_t version_major;
guint16_t version_minor;
gint32_t thiszone;
guint32_t sigfigs;
guint32_t snaplen;
guint32_t network;
} pcap_hdr_t;
typedef struct pcaprec_hdr_s {
guint32_t ts_sec;
guint32_t ts_usec;
guint32_t incl_len;
guint32_t orig_len;
} pcaprec_hdr_t;
#define LINKTYPE_RAW 101
void pcap_write(const void *, size_t);
2016-01-13 08:23:21 +00:00
void *handle_events(void *);
2016-01-12 12:15:21 +00:00
2016-01-15 18:51:07 +00:00
void handle_ip(JNIEnv *, jobject, const struct arguments *, const uint8_t *, const uint16_t);
2016-01-15 09:28:31 +00:00
void handle_tcp(JNIEnv *, jobject, const struct arguments *args,
const uint8_t *, const uint16_t, int uid);
2016-01-12 20:15:25 +00:00
2016-01-13 09:26:06 +00:00
int openSocket(JNIEnv *, jobject, const struct sockaddr_in *);
2016-01-11 22:06:35 +00:00
2016-01-13 18:25:15 +00:00
int getLocalPort(const int);
2016-01-16 14:12:42 +00:00
int writeTCP(const struct session *, uint8_t *, uint16_t, uint16_t, int, int, int, int);
2016-01-13 09:26:06 +00:00
2016-01-12 20:15:25 +00:00
jint getUid(const int, const int, const void *, const uint16_t);
2016-01-09 20:17:42 +00:00
2016-01-15 11:57:32 +00:00
uint16_t checksum(uint8_t *, uint16_t);
2016-01-11 22:06:35 +00:00
2016-01-17 13:20:07 +00:00
void ng_log(int, const char *, ...);
2016-01-16 11:32:55 +00:00
const char *strstate(const int state);
2016-01-12 20:15:25 +00:00
char *hex(const u_int8_t *, const u_int16_t);
// Global variables
2016-01-12 12:15:21 +00:00
2016-01-13 08:23:21 +00:00
static JavaVM *jvm;
pthread_t thread_id;
2016-01-14 06:46:23 +00:00
int signaled = 0;
2016-01-16 08:07:04 +00:00
struct session *session = NULL;
2016-01-17 13:20:07 +00:00
int loglevel = ANDROID_LOG_WARN;
2016-01-17 09:42:21 +00:00
char *pcap_fn = 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
2016-01-14 14:02:32 +00:00
JNIEXPORT void JNICALL
2016-01-17 09:42:21 +00:00
Java_eu_faircode_netguard_SinkholeService_jni_1init(JNIEnv *env, jobject instance,
2016-01-17 13:20:07 +00:00
jint loglevel_, jstring pcap_) {
loglevel = loglevel_;
2016-01-17 09:42:21 +00:00
const char *pcap = (*env)->GetStringUTFChars(env, pcap_, 0);
if (pcap == NULL)
pcap_fn = NULL;
else {
pcap_fn = malloc(strlen(pcap) + 1);
strcpy(pcap_fn, pcap);
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_INFO, "PCAP %s", pcap_fn);
2016-01-17 09:42:21 +00:00
session = NULL;
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;
pcap_hdr.snaplen = MAXPCAP;
pcap_hdr.network = LINKTYPE_RAW;
pcap_write(&pcap_hdr, sizeof(struct pcap_hdr_s));
}
(*env)->ReleaseStringUTFChars(env, pcap_, pcap);
}
void pcap_write(const void *ptr, size_t len) {
FILE *fd = fopen(pcap_fn, "ab");
if (fd == NULL)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "fopen %s error %d: %s",
pcap_fn, errno, strerror(errno));
2016-01-17 09:42:21 +00:00
else {
if (fwrite(ptr, len, 1, fd) < 1)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "fwrite %s error %d: %s",
pcap_fn, errno, strerror(errno));
2016-01-17 09:42:21 +00:00
else
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "PCAP write %d", len);
2016-01-17 09:42:21 +00:00
if (fclose(fd))
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "fclose %s error %d: %s",
pcap_fn, errno, strerror(errno));
2016-01-17 09:42:21 +00:00
}
2016-01-14 14:02:32 +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_1start(JNIEnv *env, jobject instance,
jint tun) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_INFO, "Starting tun=%d", tun);
2016-01-12 16:19:27 +00:00
2016-01-14 06:46:23 +00:00
if (pthread_kill(thread_id, 0) == 0)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_WARN, "Already running thread %u", thread_id);
2016-01-14 06:46:23 +00:00
else {
jint rs = (*env)->GetJavaVM(env, &jvm);
if (rs != JNI_OK)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "GetJavaVM failed");
2016-01-14 06:46:23 +00:00
struct arguments *args = malloc(sizeof(struct arguments));
args->instance = (*env)->NewGlobalRef(env, instance);
args->tun = tun;
int err = pthread_create(&thread_id, NULL, handle_events, args);
2016-01-17 13:20:07 +00:00
if (err == 0)
ng_log(ANDROID_LOG_INFO, "Started thread %u", thread_id);
else
ng_log(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) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_INFO, "Stop tun %d clear %d", tun, clear);
2016-01-14 06:46:23 +00:00
if (pthread_kill(thread_id, 0) == 0) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "Kill thread %u", thread_id);
int err = pthread_kill(thread_id, SIGUSR1);
if (err != 0)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_WARN, "pthread_kill error %d: %s",
err, strerror(err));
2016-01-14 06:46:23 +00:00
else {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "Join thread %u", thread_id);
2016-01-14 06:46:23 +00:00
pthread_join(thread_id, NULL);
if (err != 0)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_WARN, "pthread_join error %d: %s",
err, strerror(err));
2016-01-14 06:46:23 +00:00
}
2016-01-17 05:35:26 +00:00
if (clear) {
struct session *s = session;
while (s != NULL) {
struct session *p = s;
s = s->next;
free(p);
}
session = NULL;
}
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_INFO, "Stopped thread %u", thread_id);
} else
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_WARN, "Not running");
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) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_INFO, "Done");
2016-01-17 09:42:21 +00:00
free(pcap_fn);
}
2016-01-11 22:06:35 +00:00
// Private functions
void sig_handler(int sig, siginfo_t *info, void *context) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "Signal %d", sig);
2016-01-14 06:46:23 +00:00
signaled = 1;
}
2016-01-13 08:23:21 +00:00
void *handle_events(void *a) {
struct arguments *args = (struct arguments *) a;
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_INFO, "Start events tun=%d thread %u", args->tun,
thread_id);
2016-01-12 16:19:27 +00:00
2016-01-13 08:23:21 +00:00
JNIEnv *env;
jint rs = (*jvm)->AttachCurrentThread(jvm, &env, NULL);
2016-01-13 09:26:06 +00:00
if (rs != JNI_OK)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "AttachCurrentThread failed");
2016-01-13 08:23:21 +00:00
int max;
fd_set rfds;
fd_set wfds;
fd_set efds;
struct timespec ts;
2016-01-13 08:23:21 +00:00
char dest[20];
sigset_t blockset;
sigset_t emptyset;
struct sigaction sa;
// Block SIGUSR1
sigemptyset(&blockset);
sigaddset(&blockset, SIGUSR1);
sigprocmask(SIG_BLOCK, &blockset, NULL);
/// Handle SIGUSR1
sa.sa_sigaction = sig_handler;
sigemptyset(&sa.sa_mask);
sa.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
2016-01-14 06:46:23 +00:00
signaled = 0;
// Loop
while (1) {
2016-01-13 08:23:21 +00:00
time_t now = time(NULL);
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "Loop thread %u", thread_id);
2016-01-13 08:23:21 +00:00
// Select
FD_ZERO(&rfds);
FD_ZERO(&wfds);
FD_ZERO(&efds);
2016-01-15 20:40:37 +00:00
// Always read tun
2016-01-13 08:23:21 +00:00
FD_SET(args->tun, &rfds);
FD_SET(args->tun, &efds);
max = args->tun;
2016-01-16 08:07:04 +00:00
struct session *last = NULL;
struct session *cur = session;
2016-01-13 08:23:21 +00:00
while (cur != NULL) {
// TODO differentiate timeouts
if (cur->time + TCPTIMEOUT < now) {
2016-01-17 15:08:40 +00:00
// TODO send keep alives?
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_WARN, "Idle lport %u",
cur->lport);
if (cur->state == TCP_SYN_RECV ||
cur->state == TCP_ESTABLISHED ||
cur->state == TCP_CLOSE_WAIT) {
2016-01-16 14:12:42 +00:00
if (writeTCP(cur, NULL, 0, 0, 0, 1, 0, args->tun) < 0) { // FIN
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR,
"write FIN lport %u error %d: %s",
cur->lport, errno, strerror((errno)));
cur->state = TCP_TIME_WAIT; // Will close socket
}
else {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG,
"Half close initiated");
cur->local_seq++;
if (cur->state == TCP_SYN_RECV || cur->state == TCP_ESTABLISHED)
cur->state = TCP_FIN_WAIT1;
else // close wait
cur->state = TCP_LAST_ACK;
}
} else
cur->state = TCP_TIME_WAIT; // Will close socket
}
if (cur->state == TCP_TIME_WAIT) {
2016-01-13 08:23:21 +00:00
// Log
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "Close lport %u",
cur->lport);
2016-01-14 14:02:32 +00:00
// TODO keep for some time
// TODO non blocking?
2016-01-15 09:28:31 +00:00
if (close(cur->socket))
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "close error %d: %s",
errno, strerror(errno));
2016-01-11 22:06:35 +00:00
2016-01-14 14:02:32 +00:00
if (last == NULL)
2016-01-16 08:07:04 +00:00
session = cur->next;
2016-01-14 14:02:32 +00:00
else
last->next = cur->next;
2016-01-16 08:07:04 +00:00
struct session *c = cur;
2016-01-14 14:02:32 +00:00
cur = cur->next;
free(c);
continue;
2016-01-12 18:44:56 +00:00
2016-01-15 20:40:37 +00:00
} else if (cur->state != TCP_TIME_WAIT) {
2016-01-16 11:32:55 +00:00
if (cur->state == TCP_LISTEN) {
2016-01-17 15:26:37 +00:00
FD_SET(cur->socket, &efds);
2016-01-13 08:23:21 +00:00
FD_SET(cur->socket, &wfds);
if (cur->socket > max)
max = cur->socket;
}
2016-01-15 20:40:37 +00:00
else if (cur->state == TCP_ESTABLISHED ||
2016-01-16 11:32:55 +00:00
cur->state == TCP_SYN_RECV ||
2016-01-15 20:40:37 +00:00
cur->state == TCP_CLOSE_WAIT) {
2016-01-17 15:26:37 +00:00
FD_SET(cur->socket, &efds);
2016-01-13 10:11:11 +00:00
FD_SET(cur->socket, &rfds);
if (cur->socket > max)
max = cur->socket;
}
2016-01-12 18:44:56 +00:00
}
2016-01-13 08:23:21 +00:00
last = cur;
cur = cur->next;
}
2016-01-12 08:45:58 +00:00
2016-01-15 09:28:31 +00:00
ts.tv_sec = SELECTWAIT;
ts.tv_nsec = 0;
// TODO let timeout depend on session timeouts
sigemptyset(&emptyset);
2016-01-16 18:31:44 +00:00
int ready = pselect(max + 1, &rfds, &wfds, &efds, session == NULL ? NULL : &ts, &emptyset);
2016-01-13 08:23:21 +00:00
if (ready < 0) {
if (errno == EINTR) {
2016-01-14 06:46:23 +00:00
if (signaled) { ;
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "pselect signaled");
2016-01-14 06:46:23 +00:00
break;
} else {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_WARN, "pselect interrupted");
2016-01-14 06:46:23 +00:00
continue;
}
} else {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "pselect error %d: %s",
errno, strerror(errno));
2016-01-14 06:46:23 +00:00
break;
}
2016-01-13 08:23:21 +00:00
}
int sessions = 0;
struct session *s = session;
while (s != NULL) {
sessions++;
s = s->next;
}
2016-01-13 08:23:21 +00:00
if (ready == 0)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "pselect timeout sessions %d", sessions);
2016-01-13 08:23:21 +00:00
else {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "pselect sessions %d ready %d",
sessions, ready);
2016-01-14 14:02:32 +00:00
// Check tun exception
2016-01-13 08:23:21 +00:00
if (FD_ISSET(args->tun, &efds)) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "tun exception");
2016-01-13 08:23:21 +00:00
break;
}
2016-01-12 08:45:58 +00:00
2016-01-14 14:02:32 +00:00
// Check tun read
2016-01-13 08:23:21 +00:00
if (FD_ISSET(args->tun, &rfds)) {
uint8_t buffer[MAXPKT];
ssize_t length = read(args->tun, buffer, MAXPKT);
if (length < 0) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "tun read error %d: %s",
errno, strerror(errno));
2016-01-16 11:32:55 +00:00
if (errno == EINTR)
continue;
else
break;
2016-01-12 08:45:58 +00:00
}
2016-01-17 09:42:21 +00:00
else if (length > 0) {
if (pcap_fn != NULL) {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts))
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR,
"clock_gettime error %d: %s",
errno, strerror(errno));
2016-01-17 09:42:21 +00:00
int plen = (length < MAXPCAP ? length : MAXPCAP);
struct pcaprec_hdr_s *pcap_rec = malloc(
sizeof(struct pcaprec_hdr_s) + plen);
pcap_rec->ts_sec = ts.tv_sec;
pcap_rec->ts_usec = ts.tv_nsec / 1000;
pcap_rec->incl_len = plen;
pcap_rec->orig_len = length;
memcpy(((uint8_t *) pcap_rec) + sizeof(struct pcaprec_hdr_s), buffer, plen);
pcap_write(pcap_rec, sizeof(struct pcaprec_hdr_s) + plen);
free(pcap_rec);
}
2016-01-15 09:28:31 +00:00
handle_ip(env, args->instance, args, buffer, length);
2016-01-17 09:42:21 +00:00
}
else {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "tun empty read");
break;
}
2016-01-13 08:23:21 +00:00
}
2016-01-12 08:45:58 +00:00
2016-01-13 08:23:21 +00:00
// Check sockets
2016-01-16 08:07:04 +00:00
struct session *cur = session;
2016-01-13 08:23:21 +00:00
while (cur != NULL) {
if (FD_ISSET(cur->socket, &efds)) {
2016-01-17 15:26:37 +00:00
// Socket exception
2016-01-12 08:45:58 +00:00
int serr;
2016-01-17 15:26:37 +00:00
socklen_t optlen = sizeof(int);
int err = getsockopt(cur->socket, SOL_SOCKET, SO_ERROR, &serr, &optlen);
if (err < 0)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR,
"getsockopt lport %u error %d: %s",
cur->lport, errno, strerror(errno));
2016-01-17 15:26:37 +00:00
if (err < 0 || serr) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "lport %u SO_ERROR %d: %s",
cur->lport, serr, strerror(serr));
2016-01-17 15:26:37 +00:00
if (err < 0 || (serr && serr != EINTR)) {
if (writeTCP(cur, NULL, 0, 0, 0, 0, 1, args->tun) < 0) // RST
ng_log(ANDROID_LOG_ERROR,
"write RST error %d: %s",
errno, strerror((errno)));
}
2016-01-12 08:45:58 +00:00
}
2016-01-13 08:23:21 +00:00
}
2016-01-17 15:26:37 +00:00
else {
// Assume socket okay
if (cur->state == TCP_LISTEN) {
// Check socket connect
if (FD_ISSET(cur->socket, &wfds)) {
// Log
char dest[20];
inet_ntop(AF_INET, &(cur->daddr), dest, sizeof(dest));
ng_log(ANDROID_LOG_DEBUG, "Connected lport %u %s/%u",
cur->lport, dest, ntohs(cur->dest));
if (writeTCP(cur, NULL, 0, 1, 1, 0, 0, args->tun) < 0) { // SYN+ACK
ng_log(ANDROID_LOG_ERROR,
"write SYN+ACK error %d: %s",
errno, strerror((errno)));
// Remote will retry
cur->state = TCP_TIME_WAIT; // will close socket
cur = cur->next;
continue;
} else {
cur->local_seq++; // local SYN
cur->remote_seq++; // remote SYN
cur->state = TCP_SYN_RECV;
}
2016-01-16 11:32:55 +00:00
}
2016-01-13 08:23:21 +00:00
}
2016-01-13 10:11:11 +00:00
2016-01-17 15:26:37 +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)) {
// TODO window size
uint8_t buffer[MAXPKT];
ssize_t bytes = recv(cur->socket, buffer, MAXPKT, 0);
2016-01-16 11:32:55 +00:00
if (bytes < 0) {
2016-01-17 15:26:37 +00:00
// Socket error
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR,
"recv lport %u error %d: %s",
cur->lport, errno, strerror(errno));
2016-01-17 15:26:37 +00:00
if (errno != EINTR) {
if (writeTCP(cur, NULL, 0, 0, 0, 0, 1, args->tun) < 0) // RST
ng_log(ANDROID_LOG_ERROR,
"write RST error %d: %s",
errno, strerror((errno)));
2016-01-15 20:40:37 +00:00
}
2016-01-15 07:48:18 +00:00
}
2016-01-17 15:26:37 +00:00
else if (bytes == 0) {
// Socket peer closed
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG,
"recv empty lport %u state %s",
cur->lport, strstate(cur->state));
2016-01-16 11:32:55 +00:00
2016-01-17 15:26:37 +00:00
if (writeTCP(cur, NULL, 0, 0, 0, 1, 0, args->tun) < 0) // FIN
ng_log(ANDROID_LOG_ERROR,
"write FIN lport %u error %d: %s",
cur->lport, errno, strerror((errno)));
else {
cur->local_seq++; // local FIN
if (cur->state == TCP_SYN_RECV || cur->state == TCP_ESTABLISHED)
cur->state = TCP_FIN_WAIT1;
else // close wait
cur->state = TCP_LAST_ACK;
ng_log(ANDROID_LOG_DEBUG, "Half close state %s",
strstate(cur->state));
}
} else {
// Socket read
ng_log(ANDROID_LOG_DEBUG,
"recv lport %u bytes %d state %s",
cur->lport, bytes, strstate(cur->state));
if (writeTCP(cur, buffer, bytes, 0, 0, 0, 0, args->tun) < 0) // ACK
ng_log(ANDROID_LOG_ERROR,
"write ACK lport %u error %d: %s",
cur->lport, errno, strerror((errno)));
else
cur->local_seq += bytes; // received from socket
2016-01-15 07:48:18 +00:00
}
2016-01-13 10:11:11 +00:00
}
}
}
2016-01-13 08:23:21 +00:00
cur = cur->next;
2016-01-11 22:06:35 +00:00
}
}
}
2016-01-12 16:19:27 +00:00
2016-01-13 08:23:21 +00:00
(*env)->DeleteGlobalRef(env, args->instance);
rs = (*jvm)->DetachCurrentThread(jvm);
2016-01-13 09:26:06 +00:00
if (rs != JNI_OK)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "DetachCurrentThread failed");
2016-01-13 08:23:21 +00:00
free(args);
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_INFO, "Stopped events tun=%d thread %u",
args->tun, thread_id);
2016-01-16 11:32:55 +00:00
// TODO conditionally report to Java
2016-01-11 22:06:35 +00:00
}
2016-01-15 18:51:07 +00:00
void handle_ip(JNIEnv *env, jobject instance, const struct arguments *args,
const uint8_t *buffer, const uint16_t length) {
uint8_t protocol;
void *saddr;
void *daddr;
char source[40];
char dest[40];
char flags[10];
int flen = 0;
uint8_t *payload;
// Get protocol, addresses & payload
uint8_t version = (*buffer) >> 4;
if (version == 4) {
struct iphdr *ip4hdr = buffer;
protocol = ip4hdr->protocol;
saddr = &ip4hdr->saddr;
daddr = &ip4hdr->daddr;
if (ip4hdr->frag_off & IP_MF)
flags[flen++] = '+';
uint8_t optlen = (ip4hdr->ihl - 5) * 4;
payload = buffer + 20 + optlen;
if (ntohs(ip4hdr->tot_len) != length) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "Invalid length %u header length %u",
length, ntohs(ip4hdr->tot_len));
2016-01-15 18:51:07 +00:00
return;
}
uint16_t csum = checksum(ip4hdr, sizeof(struct iphdr));
if (csum != 0) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "Invalid IP checksum");
2016-01-15 18:51:07 +00:00
return;
}
}
else if (version == 6) {
struct ip6_hdr *ip6hdr = buffer;
protocol = ip6hdr->ip6_nxt;
saddr = &ip6hdr->ip6_src;
daddr = &ip6hdr->ip6_dst;
payload = buffer + 40;
// TODO check length
// TODO checksum
}
else {
2016-01-17 13:20:07 +00:00
ng_log(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
int syn = 0;
uint16_t sport = -1;
uint16_t dport = -1;
if (protocol == IPPROTO_TCP) {
struct tcphdr *tcp = payload;
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
} else if (protocol == IPPROTO_UDP) {
struct udphdr *udp = payload;
sport = ntohs(udp->source);
dport = ntohs(udp->dest);
// TODO checksum
}
flags[flen] = 0;
// Get uid
jint uid = -1;
if ((protocol == IPPROTO_TCP && syn) || protocol == IPPROTO_UDP) {
// Sleep 10 ms
2016-01-16 08:07:04 +00:00
// TODO uid retry
2016-01-15 18:51:07 +00:00
usleep(1000 * UIDDELAY);
// Lookup uid
uid = getUid(protocol, version, saddr, sport);
if (uid < 0 && version == 4) {
int8_t saddr128[16];
memset(saddr128, 0, 10);
saddr128[10] = 0xFF;
saddr128[11] = 0xFF;
memcpy(saddr128 + 12, saddr, 4);
uid = getUid(protocol, 6, saddr128, sport);
}
}
2016-01-17 13:20:07 +00:00
ng_log(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-15 18:51:07 +00:00
if (protocol == IPPROTO_TCP)
handle_tcp(env, instance, args, buffer, length, uid);
// Call back
if ((protocol == IPPROTO_TCP && syn) || protocol == IPPROTO_UDP) {
jclass cls = (*env)->GetObjectClass(env, instance);
jmethodID mid = (*env)->GetMethodID(env, cls, "logPacket",
"(ILjava/lang/String;ILjava/lang/String;IILjava/lang/String;IZ)V");
if (mid == 0)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "logPacket not found");
2016-01-15 18:51:07 +00:00
else {
jboolean allowed = 0;
jstring jsource = (*env)->NewStringUTF(env, source);
jstring jdest = (*env)->NewStringUTF(env, dest);
jstring jflags = (*env)->NewStringUTF(env, flags);
(*env)->CallVoidMethod(env, instance, mid,
version,
jsource, sport,
jdest, dport,
protocol, jflags,
uid, allowed);
(*env)->DeleteLocalRef(env, jsource);
(*env)->DeleteLocalRef(env, jdest);
(*env)->DeleteLocalRef(env, jflags);
jthrowable ex = (*env)->ExceptionOccurred(env);
if (ex) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
(*env)->DeleteLocalRef(env, ex);
}
}
(*env)->DeleteLocalRef(env, cls);
}
}
2016-01-14 14:02:32 +00:00
void handle_tcp(JNIEnv *env, jobject instance, const struct arguments *args,
2016-01-15 09:28:31 +00:00
const uint8_t *buffer, uint16_t length, int uid) {
2016-01-12 08:45:58 +00:00
// Check version
2016-01-14 14:02:32 +00:00
uint8_t version = (*buffer) >> 4;
2016-01-12 08:45:58 +00:00
if (version != 4)
return;
2016-01-11 22:06:35 +00:00
// Get headers
2016-01-12 18:44:56 +00:00
struct iphdr *iphdr = buffer;
uint8_t optlen = (iphdr->ihl - 5) * 4;
2016-01-12 18:44:56 +00:00
struct tcphdr *tcphdr = buffer + sizeof(struct iphdr) + optlen;
2016-01-15 07:48:18 +00:00
if (optlen)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_INFO, "optlen %d", optlen);
2016-01-12 18:44:56 +00:00
// Get data
uint16_t dataoff = sizeof(struct iphdr) + optlen + sizeof(struct tcphdr);
uint16_t datalen = length - dataoff;
2016-01-11 22:06:35 +00:00
2016-01-16 08:07:04 +00:00
// Search session
struct session *last = NULL;
struct session *cur = session;
while (cur != NULL && !(cur->saddr == iphdr->saddr && cur->source == tcphdr->source &&
cur->daddr == iphdr->daddr && cur->dest == tcphdr->dest)) {
2016-01-11 22:06:35 +00:00
last = cur;
cur = cur->next;
}
// Log
char dest[20];
inet_ntop(AF_INET, &(iphdr->daddr), dest, sizeof(dest));
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "Received %s/%u seq %u ack %u window %u data %d",
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-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "New SYN");
2016-01-11 22:06:35 +00:00
2016-01-16 08:07:04 +00:00
// Register session
struct session *syn = malloc(sizeof(struct 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-14 14:02:32 +00:00
syn->remote_seq = ntohl(tcphdr->seq); // ISN remote
2016-01-15 09:08:18 +00:00
syn->local_seq = 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-11 22:06:35 +00:00
syn->saddr = iphdr->saddr;
syn->source = tcphdr->source;
syn->daddr = iphdr->daddr;
syn->dest = tcphdr->dest;
2016-01-16 11:32:55 +00:00
syn->state = TCP_LISTEN;
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-11 22:06:35 +00:00
2016-01-13 09:26:06 +00:00
// Build target address
struct sockaddr_in daddr;
memset(&daddr, 0, sizeof(struct sockaddr_in));
daddr.sin_family = AF_INET;
daddr.sin_port = tcphdr->dest;
daddr.sin_addr.s_addr = iphdr->daddr;
// Open socket
syn->socket = openSocket(env, instance, &daddr);
2016-01-15 07:48:18 +00:00
if (syn->socket < 0) {
2016-01-15 20:40:37 +00:00
syn->state = TCP_TIME_WAIT;
2016-01-15 07:48:18 +00:00
// Remote will retry
free(syn);
}
2016-01-11 22:06:35 +00:00
else {
2016-01-13 09:26:06 +00:00
syn->lport = getLocalPort(syn->socket);
2016-01-11 22:06:35 +00:00
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "Connecting to %s/%u lport %u",
dest, ntohs(tcphdr->dest), syn->lport);
2016-01-15 07:48:18 +00:00
if (last == NULL)
2016-01-16 08:07:04 +00:00
session = syn;
2016-01-15 07:48:18 +00:00
else
last->next = syn;
2016-01-11 22:06:35 +00:00
}
}
2016-01-15 09:28:31 +00:00
else {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_WARN, "Unknown session");
2016-01-17 09:42:21 +00:00
struct session rst;
memset(&rst, 0, sizeof(struct session));
rst.saddr = iphdr->saddr;
rst.source = tcphdr->source;
rst.daddr = iphdr->daddr;
rst.dest = tcphdr->dest;
2016-01-15 09:28:31 +00:00
2016-01-17 09:42:21 +00:00
if (writeTCP(&rst, NULL, 0, 0, 0, 0, 1, args->tun) < 0)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR,
"write RST error %d: %s",
errno, strerror((errno)));
2016-01-15 09:28:31 +00:00
}
2016-01-11 22:06:35 +00:00
}
else {
2016-01-16 11:32:55 +00:00
int oldstate = cur->state;
uint32_t oldlocal = cur->local_seq;
uint32_t oldremote = cur->remote_seq;
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG,
"Session lport %u state %s local %u remote %u",
cur->lport, strstate(cur->state),
cur->local_seq - cur->local_start,
cur->remote_seq - cur->remote_start);
2016-01-12 08:45:58 +00:00
2016-01-17 13:30:21 +00:00
// Do not change order of conditions
2016-01-17 13:30:21 +00:00
if (tcphdr->rst) {
cur->time = time(NULL);
cur->state = TCP_TIME_WAIT; // will close socket
2016-01-12 12:15:21 +00:00
}
2016-01-14 21:48:05 +00:00
2016-01-17 13:30:21 +00:00
else if (tcphdr->syn)
ng_log(ANDROID_LOG_DEBUG, "Ignoring repeated SYN");
2016-01-17 09:42:21 +00:00
else if (tcphdr->fin /* ACK */) {
2016-01-16 11:32:55 +00:00
if (ntohl(tcphdr->ack_seq) == cur->local_seq &&
ntohl(tcphdr->seq) == cur->remote_seq) {
cur->time = time(NULL);
2016-01-16 11:32:55 +00:00
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "socket RD shutdown");
2016-01-16 11:32:55 +00:00
if (shutdown(cur->socket, SHUT_RD)) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "shutdown RD error %d: %s",
errno, strerror(errno));
2016-01-17 09:42:21 +00:00
// Socket could be closed by remote
2016-01-16 11:32:55 +00:00
}
2016-01-17 09:42:21 +00:00
int ok = 1;
if (tcphdr->ack && datalen) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "send socket data %u",
datalen);
2016-01-17 09:42:21 +00:00
// TODO non blocking
if (send(cur->socket, buffer + dataoff, datalen, 0) < 0) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "send error %d: %s",
errno, strerror(errno));
2016-01-17 09:42:21 +00:00
ok = 0;
}
else {
if (shutdown(cur->socket, SHUT_WR)) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR,
"shutdown WR error %d: %s",
errno, strerror(errno));
2016-01-16 11:32:55 +00:00
ok = 0;
2016-01-17 09:42:21 +00:00
// Data might be lost
2016-01-16 11:32:55 +00:00
}
2016-01-17 09:42:21 +00:00
else
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG, "socket WR shutdown");
2016-01-16 11:32:55 +00:00
}
2016-01-17 09:42:21 +00:00
}
2016-01-16 11:32:55 +00:00
2016-01-17 09:42:21 +00:00
if (ok) {
if (writeTCP(cur, NULL, 0, 1 + datalen, 0, 0, 0, args->tun) < 0) // ACK
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR,
"write ACK error %d: %s",
errno, strerror((errno)));
2016-01-17 09:42:21 +00:00
else {
cur->remote_seq += (1 + datalen); // FIN + received from tun
2016-01-17 15:08:40 +00:00
if (cur->state == TCP_ESTABLISHED /* && !tcphdr->ack */)
2016-01-17 09:42:21 +00:00
cur->state = TCP_CLOSE_WAIT;
2016-01-17 15:08:40 +00:00
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 */)
2016-01-17 09:42:21 +00:00
cur->state = TCP_TIME_WAIT;
else
2016-01-17 15:08:40 +00:00
ng_log(ANDROID_LOG_ERROR, "Invalid FIN state %s ACK %d",
strstate(cur->state), tcphdr->ack);
2016-01-16 11:32:55 +00:00
}
2016-01-15 20:40:37 +00:00
}
}
else {
2016-01-17 15:08:40 +00:00
// TODO proper wrap around
if (cur->state == TCP_FIN_WAIT1 &&
ntohl(tcphdr->seq) == cur->remote_seq &&
ntohl(tcphdr->ack_seq) < cur->local_seq) {
int confirm = cur->local_seq - ntohl(tcphdr->ack_seq);
ng_log(ANDROID_LOG_INFO, "Simultaneous close %d", confirm);
if (writeTCP(cur, NULL, 0, confirm, 0, 0, 0, args->tun) < 0) // ACK
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR,
"write ACK error %d: %s",
errno, strerror((errno)));
2016-01-17 12:45:34 +00:00
}
2016-01-17 15:08:40 +00:00
else
ng_log(ANDROID_LOG_WARN, "Invalid FIN seq %u/%u ack %u/%u",
ntohl(tcphdr->seq) - cur->remote_start,
cur->remote_seq - cur->remote_start,
ntohl(tcphdr->ack_seq) - cur->local_start,
cur->local_seq - cur->local_start);
}
2016-01-14 21:48:05 +00:00
}
2016-01-17 13:30:21 +00:00
else if (tcphdr->ack) {
if (((uint32_t) ntohl(tcphdr->seq) + 1) == cur->remote_seq) {
// TODO respond to keep alive?
ng_log(ANDROID_LOG_DEBUG, "Keep alive");
cur->time = time(NULL);
} else if (ntohl(tcphdr->ack_seq) == cur->local_seq &&
ntohl(tcphdr->seq) == cur->remote_seq) {
cur->time = time(NULL);
if (cur->state == TCP_SYN_RECV) {
// TODO process data?
cur->state = TCP_ESTABLISHED;
}
else if (cur->state == TCP_ESTABLISHED) {
ng_log(ANDROID_LOG_DEBUG, "New ack data %u", datalen);
if (datalen) {
ng_log(ANDROID_LOG_DEBUG, "send socket data %u",
datalen);
// TODO non blocking
if (send(cur->socket, buffer + dataoff, datalen, 0) < 0) {
ng_log(ANDROID_LOG_ERROR, "send error %d: %s",
errno, strerror(errno));
// Remote will retry
} else {
if (writeTCP(cur, NULL, 0, datalen, 0, 0, 0, args->tun) < 0) // ACK
ng_log(ANDROID_LOG_ERROR,
"write data error %d: %s",
errno, strerror((errno)));
else
cur->remote_seq += datalen; // received from tun
}
}
}
else if (cur->state == TCP_LAST_ACK) {
// socket has been shutdown already
cur->state = TCP_TIME_WAIT; // Will close socket
}
else if (cur->state == TCP_FIN_WAIT1)
cur->state = TCP_FIN_WAIT2;
else if (cur->state == TCP_CLOSING)
cur->state = TCP_TIME_WAIT;
else
ng_log(ANDROID_LOG_ERROR, "Invalid ACK state %s", strstate(cur->state));
}
else {
2016-01-17 15:08:40 +00:00
// TODO proper wrap around
if (ntohl(tcphdr->seq) == cur->remote_seq &&
ntohl(tcphdr->ack_seq) < cur->local_seq)
ng_log(ANDROID_LOG_INFO, "Previous ACK seq %u/%u ack %u/%u",
ntohl(tcphdr->seq) - cur->remote_start,
cur->remote_seq - cur->remote_start,
ntohl(tcphdr->ack_seq) - cur->local_start,
cur->local_seq - cur->local_start);
else
ng_log(ANDROID_LOG_WARN, "Invalid ACK seq %u/%u ack %u/%u",
ntohl(tcphdr->seq) - cur->remote_start,
cur->remote_seq - cur->remote_start,
ntohl(tcphdr->ack_seq) - cur->local_start,
cur->local_seq - cur->local_start);
2016-01-17 13:30:21 +00:00
}
2016-01-17 09:42:21 +00:00
}
2016-01-16 11:32:55 +00:00
else
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "Unknown packet");
2016-01-16 11:32:55 +00:00
if (cur->state != oldstate || cur->local_seq != oldlocal || cur->remote_seq != oldremote)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG,
"Session lport %u new state %s local %u remote %u",
cur->lport, strstate(cur->state),
cur->local_seq - cur->local_start,
cur->remote_seq - cur->remote_start);
2016-01-13 09:26:06 +00:00
}
}
int openSocket(JNIEnv *env, jobject instance, const struct sockaddr_in *daddr) {
int sock = -1;
// Get TCP socket
// TODO socket options (SO_REUSEADDR, etc)
2016-01-13 09:26:06 +00:00
if ((sock = socket(AF_INET, SOCK_STREAM, 0)) < 0) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "socket error %d: %s",
errno, strerror(errno));
2016-01-13 09:26:06 +00:00
return -1;
}
// Protect
jclass cls = (*env)->GetObjectClass(env, instance);
jmethodID mid = (*env)->GetMethodID(env, cls, "protect", "(I)Z");
if (mid == 0) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "protect not found");
2016-01-13 09:26:06 +00:00
return -1;
}
else {
jboolean isProtected = (*env)->CallBooleanMethod(env, instance, mid, sock);
if (!isProtected)
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "protect failed");
2016-01-13 09:26:06 +00:00
jthrowable ex = (*env)->ExceptionOccurred(env);
if (ex) {
(*env)->ExceptionDescribe(env);
(*env)->ExceptionClear(env);
(*env)->DeleteLocalRef(env, ex);
}
}
2016-01-12 12:15:21 +00:00
2016-01-13 10:11:11 +00:00
// Set non blocking
uint8_t flags = fcntl(sock, F_GETFL, 0);
if (flags < 0 || fcntl(sock, F_SETFL, flags | O_NONBLOCK) < 0) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "fcntl O_NONBLOCK error %d: %s",
errno, strerror(errno));
2016-01-13 10:11:11 +00:00
return -1;
}
2016-01-13 09:26:06 +00:00
// Initiate connect
int err = connect(sock, daddr, sizeof(struct sockaddr_in));
if (err < 0 && errno != EINPROGRESS) {
2016-01-17 13:20:07 +00:00
ng_log(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-01-13 18:25:15 +00:00
// Set blocking
2016-01-14 14:02:32 +00:00
if (fcntl(sock, F_SETFL, flags & ~O_NONBLOCK) < 0) {
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "fcntl error %d: %s",
errno, strerror(errno));
2016-01-13 18:25:15 +00:00
return -1;
}
2016-01-13 09:26:06 +00:00
return sock;
2016-01-11 22:06:35 +00:00
}
2016-01-13 09:26:06 +00:00
int getLocalPort(const int sock) {
struct sockaddr_in sin;
int len = sizeof(sin);
if (getsockname(sock, &sin, &len) < 0) {
2016-01-17 13:20:07 +00:00
ng_log(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-16 08:07:04 +00:00
int writeTCP(const struct session *cur,
2016-01-16 14:12:42 +00:00
uint8_t *data, uint16_t datalen, uint16_t confirm,
2016-01-15 09:04:44 +00:00
int syn, int fin, int rst, int tun) {
2016-01-14 14:02:32 +00:00
// Build packet
2016-01-15 09:28:31 +00:00
uint16_t len = sizeof(struct iphdr) + sizeof(struct tcphdr) + datalen;
2016-01-14 14:02:32 +00:00
u_int8_t *buffer = calloc(len, 1);
struct iphdr *ip = buffer;
struct tcphdr *tcp = buffer + sizeof(struct iphdr);
2016-01-14 18:30:38 +00:00
if (datalen)
2016-01-16 14:12:42 +00:00
memcpy(buffer + sizeof(struct iphdr) + sizeof(struct tcphdr), data, datalen);
2016-01-14 14:02:32 +00:00
// Build IP header
ip->version = 4;
ip->ihl = sizeof(struct iphdr) >> 2;
ip->tot_len = htons(len);
2016-01-15 07:48:18 +00:00
ip->ttl = TCPTTL;
2016-01-14 14:02:32 +00:00
ip->protocol = IPPROTO_TCP;
ip->saddr = cur->daddr;
ip->daddr = cur->saddr;
// Calculate IP checksum
ip->check = checksum(ip, sizeof(struct iphdr));
// Build TCP header
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-15 09:04:44 +00:00
tcp->syn = syn;
2016-01-17 09:42:21 +00:00
tcp->ack = (datalen > 0 || confirm > 0 || syn);
2016-01-14 21:48:05 +00:00
tcp->fin = fin;
2016-01-15 09:04:44 +00:00
tcp->rst = rst;
2016-01-15 07:48:18 +00:00
tcp->window = htons(TCPWINDOW);
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-14 14:02:32 +00:00
// Calculate TCP checksum
2016-01-16 14:12:42 +00:00
// TODO optimize memory usage
2016-01-14 18:30:38 +00:00
uint16_t clen = sizeof(struct ippseudo) + sizeof(struct tcphdr) + datalen;
2016-01-14 14:02:32 +00:00
uint8_t csum[clen];
// Build pseudo header
struct ippseudo *pseudo = csum;
pseudo->ippseudo_src.s_addr = ip->saddr;
pseudo->ippseudo_dst.s_addr = ip->daddr;
pseudo->ippseudo_pad = 0;
pseudo->ippseudo_p = ip->protocol;
2016-01-15 09:28:31 +00:00
pseudo->ippseudo_len = htons(sizeof(struct tcphdr) + datalen);
2016-01-14 14:02:32 +00:00
2016-01-14 18:30:38 +00:00
// Copy TCP header + data
2016-01-14 14:02:32 +00:00
memcpy(csum + sizeof(struct ippseudo), tcp, sizeof(struct tcphdr));
2016-01-14 18:30:38 +00:00
if (datalen)
2016-01-16 14:12:42 +00:00
memcpy(csum + sizeof(struct ippseudo) + sizeof(struct tcphdr), data, datalen);
2016-01-14 14:02:32 +00:00
tcp->check = checksum(csum, clen);
char to[20];
inet_ntop(AF_INET, &(ip->daddr), to, sizeof(to));
// Send packet
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_DEBUG,
"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" : ""),
to, ntohs(tcp->dest),
ntohl(tcp->seq) - cur->local_start,
ntohl(tcp->ack_seq) - cur->remote_start,
datalen, confirm);
2016-01-14 14:02:32 +00:00
int res = write(tun, buffer, len);
2016-01-17 09:42:21 +00:00
if (pcap_fn != NULL) {
struct timespec ts;
if (clock_gettime(CLOCK_REALTIME, &ts))
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR,
"clock_gettime error %d: %s",
errno, strerror(errno));
2016-01-17 09:42:21 +00:00
int plen = (len < MAXPCAP ? len : MAXPCAP);
struct pcaprec_hdr_s *pcap_rec = malloc(sizeof(struct pcaprec_hdr_s) + plen);
pcap_rec->ts_sec = ts.tv_sec;
pcap_rec->ts_usec = ts.tv_nsec / 1000;
pcap_rec->incl_len = plen;
pcap_rec->orig_len = len;
memcpy(((uint8_t *) pcap_rec) + sizeof(struct pcaprec_hdr_s), buffer, plen);
pcap_write(pcap_rec, sizeof(struct pcaprec_hdr_s) + plen);
free(pcap_rec);
}
2016-01-14 14:02:32 +00:00
free(buffer);
return res;
}
2016-01-12 20:15:25 +00:00
jint getUid(const int protocol, const int version, const void *saddr, const uint16_t sport) {
2016-01-10 07:14:47 +00:00
char line[250];
int fields;
int32_t addr32;
int8_t addr128[16];
uint16_t port;
jint uid = -1;
2016-01-10 07:14:47 +00:00
// Get proc file name
char *fn = NULL;
if (protocol == IPPROTO_TCP)
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
// Open proc file
FILE *fd = fopen(fn, "r");
if (fd == NULL) {
2016-01-17 13:20:07 +00:00
ng_log(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,
"%*d: %X:%X %*X:%*X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld ",
2016-01-15 11:13:12 +00:00
&addr32, &port, &u);
2016-01-10 07:14:47 +00:00
else
fields = sscanf(line,
"%*d: %8X%8X%8X%8X:%X %*X:%*X %*X %*lX:%*lX %*X:%*X %*X %d %*d %*ld ",
2016-01-15 11:13:12 +00:00
addr128, addr128 + 4, addr128 + 8, addr128 + 12, &port, &u);
if (fields == (version == 4 ? 3 : 6)) {
if (port == sport) {
if (version == 4) {
if (addr32 == *((int32_t *) saddr)) {
uid = u;
break;
}
}
else {
if (memcmp(addr128, saddr, (size_t) 16) == 0) {
uid = u;
break;
}
}
2016-01-10 11:51:02 +00:00
}
2016-01-15 11:13:12 +00:00
} else
2016-01-17 13:20:07 +00:00
ng_log(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))
2016-01-17 13:20:07 +00:00
ng_log(ANDROID_LOG_ERROR, "fclose %s error %d: %s",
fn, errno, strerror(errno));
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-15 11:57:32 +00:00
uint16_t checksum(uint8_t *buffer, uint16_t length) {
register uint32_t sum = 0;
register uint16_t *buf = buffer;
register int 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);
return (uint16_t) (~sum);
2016-01-12 12:15:21 +00:00
}
2016-01-17 13:20:07 +00:00
void ng_log(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-16 11:32:55 +00:00
const char *strstate(const int state) {
char buf[20];
switch (state) {
case TCP_ESTABLISHED:
return "TCP_ESTABLISHED";
2016-01-16 11:32:55 +00:00
case TCP_SYN_SENT:
return "TCP_SYN_SENT";
2016-01-16 11:32:55 +00:00
case TCP_SYN_RECV:
return "TCP_SYN_RECV";
2016-01-16 11:32:55 +00:00
case TCP_FIN_WAIT1:
return "TCP_FIN_WAIT1";
2016-01-16 11:32:55 +00:00
case TCP_FIN_WAIT2:
return "TCP_FIN_WAIT2";
2016-01-16 11:32:55 +00:00
case TCP_TIME_WAIT:
return "TCP_TIME_WAIT";
2016-01-16 11:32:55 +00:00
case TCP_CLOSE:
return "TCP_CLOSE";
2016-01-16 11:32:55 +00:00
case TCP_CLOSE_WAIT:
return "TCP_CLOSE_WAIT";
2016-01-16 11:32:55 +00:00
case TCP_LAST_ACK:
return "TCP_LAST_ACK";
2016-01-16 11:32:55 +00:00
case TCP_LISTEN:
return "TCP_LISTEN";
2016-01-16 11:32:55 +00:00
case TCP_CLOSING:
return "TCP_CLOSING";
2016-01-16 11:32:55 +00:00
default:
sprintf(buf, "TCP_%d", state);
2016-01-16 11:32:55 +00:00
return buf;
}
}
2016-01-12 20:15:25 +00:00
char *hex(const u_int8_t *data, const u_int16_t len) {
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-13 19:17:21 +00:00
return hexout;
2016-01-12 16:19:27 +00:00
}