mirror of https://github.com/M66B/NetGuard.git
parent
1a1f4494dc
commit
11c79001b5
|
@ -182,7 +182,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
|
||||
private native void jni_start(int tun, boolean fwd53, int loglevel);
|
||||
|
||||
private native void jni_stop(int tun, boolean datagram, boolean stream);
|
||||
private native void jni_stop(int tun, boolean clr);
|
||||
|
||||
private native int jni_get_mtu();
|
||||
|
||||
|
@ -442,7 +442,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
Log.i(TAG, "Legacy restart");
|
||||
|
||||
if (vpn != null) {
|
||||
stopNative(vpn, false, false);
|
||||
stopNative(vpn, false);
|
||||
stopVPN(vpn);
|
||||
vpn = null;
|
||||
try {
|
||||
|
@ -455,7 +455,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
} else {
|
||||
if (vpn != null && prefs.getBoolean("filter", false) && builder.equals(last_builder)) {
|
||||
Log.i(TAG, "Native restart");
|
||||
stopNative(vpn, false, false);
|
||||
stopNative(vpn, false);
|
||||
|
||||
} else {
|
||||
last_builder = builder;
|
||||
|
@ -467,7 +467,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
|
||||
if (prev != null && vpn == null) {
|
||||
Log.w(TAG, "Handover failed");
|
||||
stopNative(prev, false, false);
|
||||
stopNative(prev, false);
|
||||
stopVPN(prev);
|
||||
prev = null;
|
||||
try {
|
||||
|
@ -480,7 +480,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
}
|
||||
|
||||
if (prev != null) {
|
||||
stopNative(prev, false, false);
|
||||
stopNative(prev, false);
|
||||
stopVPN(prev);
|
||||
}
|
||||
}
|
||||
|
@ -497,7 +497,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
|
||||
private void stop() {
|
||||
if (vpn != null) {
|
||||
stopNative(vpn, true, true);
|
||||
stopNative(vpn, true);
|
||||
stopVPN(vpn);
|
||||
vpn = null;
|
||||
unprepare();
|
||||
|
@ -1145,9 +1145,9 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
}
|
||||
}
|
||||
|
||||
private void stopNative(ParcelFileDescriptor vpn, boolean datagram, boolean stream) {
|
||||
Log.i(TAG, "Stop native clear=" + datagram + "/" + stream);
|
||||
jni_stop(vpn.getFd(), datagram, stream);
|
||||
private void stopNative(ParcelFileDescriptor vpn, boolean clear) {
|
||||
Log.i(TAG, "Stop native clear=" + clear);
|
||||
jni_stop(vpn.getFd(), clear);
|
||||
}
|
||||
|
||||
private void unprepare() {
|
||||
|
@ -1925,7 +1925,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
|
||||
try {
|
||||
if (vpn != null) {
|
||||
stopNative(vpn, true, true);
|
||||
stopNative(vpn, true);
|
||||
stopVPN(vpn);
|
||||
vpn = null;
|
||||
unprepare();
|
||||
|
|
|
@ -19,33 +19,16 @@
|
|||
|
||||
#include "netguard.h"
|
||||
|
||||
struct icmp_session *icmp_session = NULL;
|
||||
extern struct ng_session *ng_session;
|
||||
extern FILE *pcap_file;
|
||||
|
||||
void init_icmp(const struct arguments *args) {
|
||||
icmp_session = NULL;
|
||||
}
|
||||
|
||||
void clear_icmp() {
|
||||
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;
|
||||
}
|
||||
|
||||
int get_icmp_sessions() {
|
||||
int count = 0;
|
||||
struct icmp_session *ic = icmp_session;
|
||||
while (ic != NULL) {
|
||||
if (!ic->stop)
|
||||
struct ng_session *s = ng_session;
|
||||
while (s != NULL) {
|
||||
if ((s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) && !s->icmp.stop)
|
||||
count++;
|
||||
ic = ic->next;
|
||||
s = s->next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
@ -62,137 +45,139 @@ int get_icmp_timeout(const struct icmp_session *u, int sessions, int maxsessions
|
|||
void check_icmp_sessions(const struct arguments *args, int sessions, int maxsessions) {
|
||||
time_t now = time(NULL);
|
||||
|
||||
struct icmp_session *il = NULL;
|
||||
struct icmp_session *i = icmp_session;
|
||||
while (i != NULL) {
|
||||
int timeout = get_icmp_timeout(i, sessions, maxsessions);
|
||||
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));
|
||||
struct ng_session *sl = NULL;
|
||||
struct ng_session *s = ng_session;
|
||||
while (s != NULL) {
|
||||
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) {
|
||||
int timeout = get_icmp_timeout(&s->icmp, sessions, maxsessions);
|
||||
if (s->icmp.stop || s->icmp.time + timeout < now) {
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (s->icmp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->icmp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->icmp.daddr.ip4, dest, sizeof(dest));
|
||||
}
|
||||
else {
|
||||
inet_ntop(AF_INET6, &s->icmp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->icmp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
log_android(ANDROID_LOG_WARN, "ICMP idle %d/%d sec stop %d from %s to %s",
|
||||
now - s->icmp.time, timeout, s->icmp.stop, dest, source);
|
||||
|
||||
if (close(s->socket))
|
||||
log_android(ANDROID_LOG_ERROR, "ICMP close %d error %d: %s",
|
||||
s->socket, errno, strerror(errno));
|
||||
s->socket = -1;
|
||||
|
||||
if (sl == NULL)
|
||||
ng_session = s->next;
|
||||
else
|
||||
sl->next = s->next;
|
||||
|
||||
struct ng_session *c = s;
|
||||
s = s->next;
|
||||
free(c);
|
||||
}
|
||||
else {
|
||||
inet_ntop(AF_INET6, &i->saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &i->daddr.ip6, dest, sizeof(dest));
|
||||
sl = s;
|
||||
s = s->next;
|
||||
}
|
||||
log_android(ANDROID_LOG_WARN, "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 {
|
||||
il = i;
|
||||
i = i->next;
|
||||
} else {
|
||||
sl = s;
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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);
|
||||
void check_icmp_socket(const struct arguments *args, const struct epoll_event *ev) {
|
||||
struct ng_session *s = (struct ng_session *) ev->data.ptr;
|
||||
|
||||
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));
|
||||
// Check socket error
|
||||
if (ev->events & EPOLLERR) {
|
||||
s->icmp.time = time(NULL);
|
||||
|
||||
cur->stop = 1;
|
||||
int serr = 0;
|
||||
socklen_t optlen = sizeof(int);
|
||||
int err = getsockopt(s->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));
|
||||
|
||||
s->icmp.stop = 1;
|
||||
}
|
||||
else {
|
||||
// Check socket read
|
||||
if (ev->events & EPOLLIN) {
|
||||
s->icmp.time = time(NULL);
|
||||
|
||||
uint16_t blen = (uint16_t) (s->icmp.version == 4 ? ICMP4_MAXMSG : ICMP6_MAXMSG);
|
||||
uint8_t *buffer = malloc(blen);
|
||||
ssize_t bytes = recv(s->socket, buffer, blen, 0);
|
||||
if (bytes < 0) {
|
||||
// Socket error
|
||||
log_android(ANDROID_LOG_WARN, "ICMP recv error %d: %s",
|
||||
errno, strerror(errno));
|
||||
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
s->icmp.stop = 1;
|
||||
}
|
||||
else {
|
||||
// Check socket read
|
||||
if (FD_ISSET(cur->socket, rfds)) {
|
||||
cur->time = time(NULL);
|
||||
else if (bytes == 0) {
|
||||
log_android(ANDROID_LOG_WARN, "ICMP recv eof");
|
||||
s->icmp.stop = 1;
|
||||
|
||||
uint16_t blen = (uint16_t) (cur->version == 4 ? ICMP4_MAXMSG : ICMP6_MAXMSG);
|
||||
uint8_t *buffer = malloc(blen);
|
||||
ssize_t bytes = recv(cur->socket, buffer, blen, 0);
|
||||
if (bytes < 0) {
|
||||
// Socket error
|
||||
log_android(ANDROID_LOG_WARN, "ICMP recv error %d: %s",
|
||||
errno, strerror(errno));
|
||||
} else {
|
||||
// Socket read data
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (s->icmp.version == 4)
|
||||
inet_ntop(AF_INET, &s->icmp.daddr.ip4, dest, sizeof(dest));
|
||||
else
|
||||
inet_ntop(AF_INET6, &s->icmp.daddr.ip6, dest, sizeof(dest));
|
||||
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
cur->stop = 1;
|
||||
}
|
||||
else if (bytes == 0) {
|
||||
log_android(ANDROID_LOG_WARN, "ICMP recv eof");
|
||||
cur->stop = 1;
|
||||
// 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
|
||||
struct icmp *icmp = (struct icmp *) buffer;
|
||||
log_android(
|
||||
s->icmp.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,
|
||||
s->icmp.id, icmp->icmp_id, icmp->icmp_seq);
|
||||
|
||||
} 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));
|
||||
|
||||
// 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
|
||||
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;
|
||||
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));
|
||||
}
|
||||
icmp->icmp_cksum = 0;
|
||||
icmp->icmp_cksum = ~calc_checksum(csum, buffer, (size_t) bytes);
|
||||
|
||||
// Forward to tun
|
||||
if (write_icmp(args, cur, buffer, (size_t) bytes) < 0)
|
||||
cur->stop = 1;
|
||||
}
|
||||
free(buffer);
|
||||
// restore original ID
|
||||
icmp->icmp_id = s->icmp.id;
|
||||
uint16_t csum = 0;
|
||||
if (s->icmp.version == 6) {
|
||||
// Untested
|
||||
struct ip6_hdr_pseudo pseudo;
|
||||
memset(&pseudo, 0, sizeof(struct ip6_hdr_pseudo));
|
||||
memcpy(&pseudo.ip6ph_src, &s->icmp.daddr.ip6, 16);
|
||||
memcpy(&pseudo.ip6ph_dst, &s->icmp.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));
|
||||
}
|
||||
icmp->icmp_cksum = 0;
|
||||
icmp->icmp_cksum = ~calc_checksum(csum, buffer, (size_t) bytes);
|
||||
|
||||
// Forward to tun
|
||||
if (write_icmp(args, &s->icmp, buffer, (size_t) bytes) < 0)
|
||||
s->icmp.stop = 1;
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
jboolean handle_icmp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid) {
|
||||
int uid,
|
||||
const int epoll_fd) {
|
||||
// Get headers
|
||||
const uint8_t version = (*pkt) >> 4;
|
||||
const struct iphdr *ip4 = (struct iphdr *) pkt;
|
||||
|
@ -207,13 +192,14 @@ jboolean handle_icmp(const struct arguments *args,
|
|||
}
|
||||
|
||||
// Search session
|
||||
struct icmp_session *cur = icmp_session;
|
||||
struct ng_session *cur = ng_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 &&
|
||||
memcmp(&cur->daddr.ip6, &ip6->ip6_dst, 16) == 0)))
|
||||
!((cur->protocol == IPPROTO_ICMP || cur->protocol == IPPROTO_ICMPV6) &&
|
||||
!cur->icmp.stop && cur->icmp.version == version &&
|
||||
(version == 4 ? cur->icmp.saddr.ip4 == ip4->saddr &&
|
||||
cur->icmp.daddr.ip4 == ip4->daddr
|
||||
: memcmp(&cur->icmp.saddr.ip6, &ip6->ip6_src, 16) == 0 &&
|
||||
memcmp(&cur->icmp.daddr.ip6, &ip6->ip6_dst, 16) == 0)))
|
||||
cur = cur->next;
|
||||
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
|
@ -231,37 +217,46 @@ jboolean handle_icmp(const struct arguments *args,
|
|||
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;
|
||||
struct ng_session *s = malloc(sizeof(struct ng_session));
|
||||
s->protocol = (uint8_t) (version == 4 ? IPPROTO_ICMP : IPPROTO_ICMPV6);
|
||||
|
||||
s->icmp.time = time(NULL);
|
||||
s->icmp.uid = uid;
|
||||
s->icmp.version = version;
|
||||
|
||||
if (version == 4) {
|
||||
i->saddr.ip4 = (__be32) ip4->saddr;
|
||||
i->daddr.ip4 = (__be32) ip4->daddr;
|
||||
s->icmp.saddr.ip4 = (__be32) ip4->saddr;
|
||||
s->icmp.daddr.ip4 = (__be32) ip4->daddr;
|
||||
} else {
|
||||
memcpy(&i->saddr.ip6, &ip6->ip6_src, 16);
|
||||
memcpy(&i->daddr.ip6, &ip6->ip6_dst, 16);
|
||||
memcpy(&s->icmp.saddr.ip6, &ip6->ip6_src, 16);
|
||||
memcpy(&s->icmp.daddr.ip6, &ip6->ip6_dst, 16);
|
||||
}
|
||||
|
||||
i->id = icmp->icmp_id; // store original ID
|
||||
s->icmp.id = icmp->icmp_id; // store original ID
|
||||
|
||||
i->stop = 0;
|
||||
i->next = NULL;
|
||||
s->icmp.stop = 0;
|
||||
s->next = NULL;
|
||||
|
||||
// Open UDP socket
|
||||
i->socket = open_icmp_socket(args, i);
|
||||
if (i->socket < 0) {
|
||||
free(i);
|
||||
s->socket = open_icmp_socket(args, &s->icmp);
|
||||
if (s->socket < 0) {
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "ICMP socket %d id %x", i->socket, i->id);
|
||||
log_android(ANDROID_LOG_DEBUG, "ICMP socket %d id %x", s->socket, s->icmp.id);
|
||||
|
||||
i->next = icmp_session;
|
||||
icmp_session = i;
|
||||
// Monitor events
|
||||
memset(&s->ev, 0, sizeof(struct epoll_event));
|
||||
s->ev.events = EPOLLIN | EPOLLERR;
|
||||
s->ev.data.ptr = s;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s->socket, &s->ev))
|
||||
log_android(ANDROID_LOG_ERROR, "epoll add icmp error %d: %s", errno, strerror(errno));
|
||||
|
||||
cur = i;
|
||||
s->next = ng_session;
|
||||
ng_session = s;
|
||||
|
||||
cur = s;
|
||||
}
|
||||
|
||||
// Modify ID
|
||||
|
@ -286,7 +281,7 @@ jboolean handle_icmp(const struct arguments *args,
|
|||
source, dest,
|
||||
icmp->icmp_type, icmp->icmp_code, icmp->icmp_id, icmp->icmp_seq, icmplen);
|
||||
|
||||
cur->time = time(NULL);
|
||||
cur->icmp.time = time(NULL);
|
||||
|
||||
struct sockaddr_in server4;
|
||||
struct sockaddr_in6 server6;
|
||||
|
@ -306,7 +301,7 @@ jboolean handle_icmp(const struct arguments *args,
|
|||
(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;
|
||||
cur->icmp.stop = 1;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -35,10 +35,11 @@ uint16_t get_default_mss(int version) {
|
|||
}
|
||||
|
||||
int check_tun(const struct arguments *args,
|
||||
fd_set *rfds, fd_set *wfds, fd_set *efds,
|
||||
const struct epoll_event *ev,
|
||||
const int epoll_fd,
|
||||
int sessions, int maxsessions) {
|
||||
// Check tun error
|
||||
if (FD_ISSET(args->tun, efds)) {
|
||||
if (ev->events & EPOLLERR) {
|
||||
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",
|
||||
|
@ -51,7 +52,7 @@ int check_tun(const struct arguments *args,
|
|||
}
|
||||
|
||||
// Check tun read
|
||||
if (FD_ISSET(args->tun, rfds)) {
|
||||
if (ev->events & EPOLLIN) {
|
||||
uint8_t *buffer = malloc(get_mtu());
|
||||
ssize_t length = read(args->tun, buffer, get_mtu());
|
||||
if (length < 0) {
|
||||
|
@ -79,7 +80,7 @@ int check_tun(const struct arguments *args,
|
|||
}
|
||||
|
||||
// Handle IP from tun
|
||||
handle_ip(args, buffer, (size_t) length, sessions, maxsessions);
|
||||
handle_ip(args, buffer, (size_t) length, epoll_fd, sessions, maxsessions);
|
||||
|
||||
free(buffer);
|
||||
}
|
||||
|
@ -119,6 +120,7 @@ int is_upper_layer(int protocol) {
|
|||
|
||||
void handle_ip(const struct arguments *args,
|
||||
const uint8_t *pkt, const size_t length,
|
||||
const int epoll_fd,
|
||||
int sessions, int maxsessions) {
|
||||
uint8_t protocol;
|
||||
void *saddr;
|
||||
|
@ -328,11 +330,11 @@ void handle_ip(const struct arguments *args,
|
|||
// Handle allowed traffic
|
||||
if (allowed) {
|
||||
if (protocol == IPPROTO_ICMP || protocol == IPPROTO_ICMPV6)
|
||||
handle_icmp(args, pkt, length, payload, uid);
|
||||
handle_icmp(args, pkt, length, payload, uid, epoll_fd);
|
||||
else if (protocol == IPPROTO_UDP)
|
||||
handle_udp(args, pkt, length, payload, uid, redirect);
|
||||
handle_udp(args, pkt, length, payload, uid, redirect, epoll_fd);
|
||||
else if (protocol == IPPROTO_TCP)
|
||||
handle_tcp(args, pkt, length, payload, uid, redirect);
|
||||
handle_tcp(args, pkt, length, payload, uid, redirect, epoll_fd);
|
||||
}
|
||||
else {
|
||||
if (protocol == IPPROTO_UDP)
|
||||
|
|
|
@ -101,9 +101,7 @@ Java_eu_faircode_netguard_ServiceSinkhole_jni_1init(JNIEnv *env, jobject instanc
|
|||
struct arguments args;
|
||||
args.env = env;
|
||||
args.instance = instance;
|
||||
init_icmp(&args);
|
||||
init_udp(&args);
|
||||
init_tcp(&args);
|
||||
init(&args);
|
||||
|
||||
pcap_file = NULL;
|
||||
|
||||
|
@ -152,11 +150,9 @@ Java_eu_faircode_netguard_ServiceSinkhole_jni_1start(
|
|||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_eu_faircode_netguard_ServiceSinkhole_jni_1stop(
|
||||
JNIEnv *env, jobject instance, jint tun,
|
||||
jboolean datagram, jboolean stream) {
|
||||
JNIEnv *env, jobject instance, jint tun, jboolean clr) {
|
||||
pthread_t t = thread_id;
|
||||
log_android(ANDROID_LOG_WARN, "Stop tun %d clear %d/%d thread %x",
|
||||
tun, (int) datagram, (int) stream, t);
|
||||
log_android(ANDROID_LOG_WARN, "Stop tun %d thread %x", tun, t);
|
||||
if (t && pthread_kill(t, 0) == 0) {
|
||||
stopping = 1;
|
||||
log_android(ANDROID_LOG_WARN, "Kill thread %x", t);
|
||||
|
@ -170,12 +166,8 @@ Java_eu_faircode_netguard_ServiceSinkhole_jni_1stop(
|
|||
log_android(ANDROID_LOG_WARN, "pthread_join error %d: %s", err, strerror(err));
|
||||
}
|
||||
|
||||
if (datagram) {
|
||||
clear_icmp();
|
||||
clear_udp();
|
||||
}
|
||||
if (stream)
|
||||
clear_tcp();
|
||||
if (clr)
|
||||
clear();
|
||||
|
||||
log_android(ANDROID_LOG_WARN, "Stopped thread %x", t);
|
||||
} else
|
||||
|
@ -283,9 +275,7 @@ JNIEXPORT void JNICALL
|
|||
Java_eu_faircode_netguard_ServiceSinkhole_jni_1done(JNIEnv *env, jobject instance) {
|
||||
log_android(ANDROID_LOG_INFO, "Done");
|
||||
|
||||
clear_icmp();
|
||||
clear_udp();
|
||||
clear_tcp();
|
||||
clear();
|
||||
|
||||
if (pthread_mutex_destroy(&lock))
|
||||
log_android(ANDROID_LOG_ERROR, "pthread_mutex_destroy failed");
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include <sys/types.h>
|
||||
#include <sys/ioctl.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/epoll.h>
|
||||
#include <dlfcn.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/resource.h>
|
||||
|
@ -95,9 +96,6 @@ struct icmp_session {
|
|||
uint16_t id;
|
||||
|
||||
uint8_t stop;
|
||||
jint socket;
|
||||
|
||||
struct icmp_session *next;
|
||||
};
|
||||
|
||||
#define UDP_ACTIVE 0
|
||||
|
@ -127,9 +125,6 @@ struct udp_session {
|
|||
__be16 dest; // network notation
|
||||
|
||||
uint8_t state;
|
||||
jint socket;
|
||||
|
||||
struct udp_session *next;
|
||||
};
|
||||
|
||||
struct tcp_session {
|
||||
|
@ -165,10 +160,19 @@ struct tcp_session {
|
|||
__be16 dest; // network notation
|
||||
|
||||
uint8_t state;
|
||||
jint socket;
|
||||
struct segment *forward;
|
||||
};
|
||||
|
||||
struct tcp_session *next;
|
||||
struct ng_session {
|
||||
uint8_t protocol;
|
||||
union {
|
||||
struct icmp_session icmp;
|
||||
struct udp_session udp;
|
||||
struct tcp_session tcp;
|
||||
};
|
||||
jint socket;
|
||||
struct epoll_event ev;
|
||||
struct ng_session *next;
|
||||
};
|
||||
|
||||
// IPv6
|
||||
|
@ -296,6 +300,10 @@ void report_error(const struct arguments *args, jint error, const char *fmt, ...
|
|||
|
||||
void check_allowed(const struct arguments *args);
|
||||
|
||||
void init(const struct arguments *args);
|
||||
|
||||
void clear();
|
||||
|
||||
void check_icmp_sessions(const struct arguments *args, int sessions, int maxsessions);
|
||||
|
||||
void check_udp_sessions(const struct arguments *args, int sessions, int maxsessions);
|
||||
|
@ -314,15 +322,14 @@ uint16_t get_mtu();
|
|||
|
||||
uint16_t get_default_mss(int version);
|
||||
|
||||
int get_selects(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds);
|
||||
|
||||
int check_tun(const struct arguments *args,
|
||||
fd_set *rfds, fd_set *wfds, fd_set *efds,
|
||||
const struct epoll_event *ev,
|
||||
const int epoll_fd,
|
||||
int sessions, int maxsessions);
|
||||
|
||||
void check_icmp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds);
|
||||
void check_icmp_socket(const struct arguments *args, const struct epoll_event *ev);
|
||||
|
||||
void check_udp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds);
|
||||
void check_udp_socket(const struct arguments *args, const struct epoll_event *ev);
|
||||
|
||||
int32_t get_qname(const uint8_t *data, const size_t datalen, uint16_t off, char *qname);
|
||||
|
||||
|
@ -330,11 +337,13 @@ void parse_dns_response(const struct arguments *args, const uint8_t *data, const
|
|||
|
||||
uint32_t get_send_window(const struct tcp_session *cur);
|
||||
|
||||
int get_receive_buffer(const struct tcp_session *cur);
|
||||
int get_receive_buffer(const struct ng_session *cur);
|
||||
|
||||
uint32_t get_receive_window(const struct tcp_session *cur);
|
||||
uint32_t get_receive_window(const struct ng_session *cur);
|
||||
|
||||
void check_tcp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds);
|
||||
void check_tcp_socket(const struct arguments *args,
|
||||
const struct epoll_event *ev,
|
||||
const int epoll_fd);
|
||||
|
||||
int is_lower_layer(int protocol);
|
||||
|
||||
|
@ -342,22 +351,16 @@ int is_upper_layer(int protocol);
|
|||
|
||||
void handle_ip(const struct arguments *args,
|
||||
const uint8_t *buffer, size_t length,
|
||||
const int epoll_fd,
|
||||
int sessions, int maxsessions);
|
||||
|
||||
void init_icmp(const struct arguments *args);
|
||||
|
||||
void clear_icmp();
|
||||
|
||||
int get_icmp_sessions();
|
||||
|
||||
jboolean handle_icmp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid);
|
||||
|
||||
void init_udp(const struct arguments *args);
|
||||
|
||||
void clear_udp();
|
||||
int uid,
|
||||
const int epoll_fd);
|
||||
|
||||
int get_udp_sessions();
|
||||
|
||||
|
@ -371,7 +374,8 @@ void block_udp(const struct arguments *args,
|
|||
jboolean handle_udp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid, struct allowed *redirect);
|
||||
int uid, struct allowed *redirect,
|
||||
const int epoll_fd);
|
||||
|
||||
int get_dns_query(const struct arguments *args, const struct udp_session *u,
|
||||
const uint8_t *data, const size_t datalen,
|
||||
|
@ -384,10 +388,6 @@ int check_domain(const struct arguments *args, const struct udp_session *u,
|
|||
int check_dhcp(const struct arguments *args, const struct udp_session *u,
|
||||
const uint8_t *data, const size_t datalen);
|
||||
|
||||
void init_tcp(const struct arguments *args);
|
||||
|
||||
void clear_tcp();
|
||||
|
||||
void clear_tcp_data(struct tcp_session *cur);
|
||||
|
||||
int get_tcp_sessions();
|
||||
|
@ -395,7 +395,8 @@ int get_tcp_sessions();
|
|||
jboolean handle_tcp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid, struct allowed *redirect);
|
||||
int uid, struct allowed *redirect,
|
||||
const int epoll_fd);
|
||||
|
||||
void queue_tcp(const struct arguments *args,
|
||||
const struct tcphdr *tcphdr,
|
||||
|
|
|
@ -25,9 +25,26 @@ extern pthread_mutex_t lock;
|
|||
extern jboolean stopping;
|
||||
extern jboolean signaled;
|
||||
|
||||
extern struct icmp_session *icmp_session;
|
||||
extern struct udp_session *udp_session;
|
||||
extern struct tcp_session *tcp_session;
|
||||
struct ng_session *ng_session = NULL;
|
||||
|
||||
void init(const struct arguments *args) {
|
||||
ng_session = NULL;
|
||||
}
|
||||
|
||||
void clear() {
|
||||
struct ng_session *s = ng_session;
|
||||
while (s != NULL) {
|
||||
if (s->socket >= 0 && close(s->socket))
|
||||
log_android(ANDROID_LOG_ERROR, "close %d error %d: %s",
|
||||
s->socket, errno, strerror(errno));
|
||||
if (s->protocol == IPPROTO_TCP)
|
||||
clear_tcp_data(&s->tcp);
|
||||
struct ng_session *p = s;
|
||||
s = s->next;
|
||||
free(p);
|
||||
}
|
||||
ng_session = NULL;
|
||||
}
|
||||
|
||||
void handle_signal(int sig, siginfo_t *info, void *context) {
|
||||
log_android(ANDROID_LOG_DEBUG, "Signal %d", sig);
|
||||
|
@ -36,9 +53,7 @@ void handle_signal(int sig, siginfo_t *info, void *context) {
|
|||
|
||||
void *handle_events(void *a) {
|
||||
int sdk;
|
||||
fd_set rfds;
|
||||
fd_set wfds;
|
||||
fd_set efds;
|
||||
int epoll_fd;
|
||||
struct timespec ts;
|
||||
sigset_t blockset;
|
||||
sigset_t emptyset;
|
||||
|
@ -68,8 +83,6 @@ void *handle_events(void *a) {
|
|||
log_android(ANDROID_LOG_WARN, "getrlimit soft %d hard %d max sessions %d",
|
||||
rlim.rlim_cur, rlim.rlim_max, maxsessions);
|
||||
}
|
||||
if (maxsessions > FD_SETSIZE)
|
||||
maxsessions = FD_SETSIZE;
|
||||
|
||||
// Block SIGUSR1
|
||||
sigemptyset(&blockset);
|
||||
|
@ -88,6 +101,27 @@ void *handle_events(void *a) {
|
|||
stopping = 0;
|
||||
signaled = 0;
|
||||
|
||||
// Open epoll fd
|
||||
epoll_fd = epoll_create(1);
|
||||
if (epoll_fd < 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "epoll create error %d: %s", errno, strerror(errno));
|
||||
report_exit(args, "epoll create error %d: %s", errno, strerror(errno));
|
||||
stopping = 1;
|
||||
}
|
||||
|
||||
// Monitor tun events
|
||||
struct epoll_event ev_tun;
|
||||
memset(&ev_tun, 0, sizeof(struct epoll_event));
|
||||
ev_tun.events = EPOLLIN | EPOLLERR;
|
||||
ev_tun.data.ptr = NULL;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, args->tun, &ev_tun)) {
|
||||
log_android(ANDROID_LOG_ERROR, "epoll add tun error %d: %s", errno, strerror(errno));
|
||||
report_exit(args, "epoll add tun error %d: %s", errno, strerror(errno));
|
||||
stopping = 1;
|
||||
}
|
||||
|
||||
sigemptyset(&emptyset);
|
||||
|
||||
// Loop
|
||||
while (!stopping) {
|
||||
log_android(ANDROID_LOG_DEBUG, "Loop thread %x", thread_id);
|
||||
|
@ -111,62 +145,62 @@ void *handle_events(void *a) {
|
|||
// Next event time
|
||||
ts.tv_sec = (sdk < 16 ? 5 : get_select_timeout(sessions, maxsessions));
|
||||
ts.tv_nsec = 0;
|
||||
sigemptyset(&emptyset);
|
||||
|
||||
// Check if tun is writable
|
||||
FD_ZERO(&rfds);
|
||||
FD_ZERO(&wfds);
|
||||
FD_ZERO(&efds);
|
||||
FD_SET(args->tun, &wfds);
|
||||
if (pselect(args->tun + 1, &rfds, &wfds, &efds, &ts, &emptyset) == 0) {
|
||||
log_android(ANDROID_LOG_WARN, "tun not writable");
|
||||
continue;
|
||||
}
|
||||
// TODO: check if tun is writable?
|
||||
|
||||
// Select
|
||||
int max = get_selects(args, &rfds, &wfds, &efds);
|
||||
int ready = pselect(max + 1, &rfds, &wfds, &efds, idle ? NULL : &ts, &emptyset);
|
||||
// Poll
|
||||
struct epoll_event ev;
|
||||
sigset_t origmask;
|
||||
pthread_sigmask(SIG_SETMASK, &emptyset, &origmask);
|
||||
int ready = epoll_wait(epoll_fd, &ev, 1, ts.tv_sec * 1000);
|
||||
pthread_sigmask(SIG_SETMASK, &origmask, NULL);
|
||||
|
||||
if (ready < 0) {
|
||||
if (errno == EINTR) {
|
||||
if (stopping && signaled) { ;
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"pselect signaled tun %d thread %x", args->tun, thread_id);
|
||||
"epoll signaled tun %d thread %x", args->tun, thread_id);
|
||||
report_exit(args, NULL);
|
||||
break;
|
||||
} else {
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"pselect interrupted tun %d thread %x", args->tun, thread_id);
|
||||
"epoll interrupted tun %d thread %x", args->tun, thread_id);
|
||||
continue;
|
||||
}
|
||||
} else if (errno == EBADF) {
|
||||
if (is_valid_fd(args->tun)) {
|
||||
log_android(ANDROID_LOG_ERROR, "pselect error %d: %s", errno, strerror(errno));
|
||||
report_exit(args, "pselect error %d: %s", errno, strerror(errno));
|
||||
log_android(ANDROID_LOG_ERROR, "epoll error %d: %s", errno, strerror(errno));
|
||||
report_exit(args, "epoll error %d: %s", errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
else {
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"tun socket %d select error %d: %s",
|
||||
"tun socket %d epoll error %d: %s",
|
||||
args->tun, errno, strerror(errno));
|
||||
report_exit(args, "tun socket %d select error %d: %s",
|
||||
report_exit(args, "tun socket %d epoll error %d: %s",
|
||||
args->tun, errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
} else {
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"pselect tun %d thread %x error %d: %s",
|
||||
"epoll 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",
|
||||
report_exit(args, "epoll tun %d thread %x error %d: %s",
|
||||
args->tun, thread_id, errno, strerror(errno));
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (ready == 0)
|
||||
log_android(ANDROID_LOG_DEBUG, "pselect timeout");
|
||||
log_android(ANDROID_LOG_DEBUG, "epoll timeout");
|
||||
else {
|
||||
log_android(ANDROID_LOG_DEBUG, "pselect ready %d", ready);
|
||||
log_android(ANDROID_LOG_DEBUG, "epoll ready %d in %d out %d err %d prot %d sock %d",
|
||||
ready,
|
||||
(ev.events & EPOLLIN) != 0,
|
||||
(ev.events & EPOLLOUT) != 0,
|
||||
(ev.events & EPOLLERR) != 0,
|
||||
(ev.data.ptr == NULL ? -1 : ((struct ng_session *) ev.data.ptr)->protocol),
|
||||
(ev.data.ptr == NULL ? -1 : ((struct ng_session *) ev.data.ptr)->socket));
|
||||
|
||||
if (pthread_mutex_lock(&lock))
|
||||
log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed");
|
||||
|
@ -179,9 +213,10 @@ void *handle_events(void *a) {
|
|||
|
||||
// Check upstream
|
||||
int error = 0;
|
||||
if (check_tun(args, &rfds, &wfds, &efds, sessions, maxsessions) < 0)
|
||||
error = 1;
|
||||
else {
|
||||
if (ev.data.ptr == NULL) {
|
||||
if (check_tun(args, &ev, epoll_fd, sessions, maxsessions) < 0)
|
||||
error = 1;
|
||||
} else {
|
||||
#ifdef PROFILE_EVENTS
|
||||
gettimeofday(&end, NULL);
|
||||
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
|
||||
|
@ -192,14 +227,14 @@ void *handle_events(void *a) {
|
|||
gettimeofday(&start, NULL);
|
||||
#endif
|
||||
|
||||
// Check ICMP downstream
|
||||
check_icmp_sockets(args, &rfds, &wfds, &efds);
|
||||
|
||||
// Check UDP downstream
|
||||
check_udp_sockets(args, &rfds, &wfds, &efds);
|
||||
|
||||
// Check TCP downstream
|
||||
check_tcp_sockets(args, &rfds, &wfds, &efds);
|
||||
// Check downstream
|
||||
struct ng_session *session = (struct ng_session *) ev.data.ptr;
|
||||
if (session->protocol == IPPROTO_ICMP || session->protocol == IPPROTO_ICMPV6)
|
||||
check_icmp_socket(args, &ev);
|
||||
else if (session->protocol == IPPROTO_UDP)
|
||||
check_udp_socket(args, &ev);
|
||||
else if (session->protocol == IPPROTO_TCP)
|
||||
check_tcp_socket(args, &ev, epoll_fd);
|
||||
}
|
||||
|
||||
if (pthread_mutex_unlock(&lock))
|
||||
|
@ -218,6 +253,11 @@ void *handle_events(void *a) {
|
|||
}
|
||||
}
|
||||
|
||||
// Close epoll fd
|
||||
if (epoll_fd >= 0 && close(epoll_fd))
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"epoll close error %d: %s", errno, strerror(errno));
|
||||
|
||||
(*env)->DeleteGlobalRef(env, args->instance);
|
||||
|
||||
// Detach from Java
|
||||
|
@ -237,220 +277,127 @@ int get_select_timeout(int sessions, int maxsessions) {
|
|||
time_t now = time(NULL);
|
||||
int timeout = SELECT_TIMEOUT;
|
||||
|
||||
struct icmp_session *i = icmp_session;
|
||||
while (i != NULL) {
|
||||
if (!i->stop) {
|
||||
int stimeout = i->time + get_icmp_timeout(i, sessions, maxsessions) - now + 1;
|
||||
if (stimeout > 0 && stimeout < timeout)
|
||||
timeout = stimeout;
|
||||
}
|
||||
i = i->next;
|
||||
}
|
||||
struct ng_session *s = ng_session;
|
||||
while (s != NULL) {
|
||||
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) {
|
||||
if (!s->icmp.stop) {
|
||||
int stimeout =
|
||||
s->icmp.time + get_icmp_timeout(&s->icmp, sessions, maxsessions) - now + 1;
|
||||
if (stimeout > 0 && stimeout < timeout)
|
||||
timeout = stimeout;
|
||||
}
|
||||
|
||||
struct udp_session *u = udp_session;
|
||||
while (u != NULL) {
|
||||
if (u->state == UDP_ACTIVE) {
|
||||
int stimeout = u->time + get_udp_timeout(u, sessions, maxsessions) - now + 1;
|
||||
if (stimeout > 0 && stimeout < timeout)
|
||||
timeout = stimeout;
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
} else if (s->protocol == IPPROTO_UDP) {
|
||||
if (s->udp.state == UDP_ACTIVE) {
|
||||
int stimeout =
|
||||
s->udp.time + get_udp_timeout(&s->udp, sessions, maxsessions) - now + 1;
|
||||
if (stimeout > 0 && stimeout < timeout)
|
||||
timeout = stimeout;
|
||||
}
|
||||
|
||||
} else if (s->protocol == IPPROTO_TCP) {
|
||||
if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE) {
|
||||
int stimeout =
|
||||
s->tcp.time + get_tcp_timeout(&s->tcp, sessions, maxsessions) - now + 1;
|
||||
if (stimeout > 0 && stimeout < timeout)
|
||||
timeout = stimeout;
|
||||
}
|
||||
|
||||
struct tcp_session *t = tcp_session;
|
||||
while (t != NULL) {
|
||||
if (t->state != TCP_CLOSING && t->state != TCP_CLOSE) {
|
||||
int stimeout = t->time + get_tcp_timeout(t, sessions, maxsessions) - now + 1;
|
||||
if (stimeout > 0 && stimeout < timeout)
|
||||
timeout = stimeout;
|
||||
}
|
||||
t = t->next;
|
||||
|
||||
s = s->next;
|
||||
}
|
||||
|
||||
return timeout;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
// Always select tun
|
||||
FD_SET(args->tun, rfds);
|
||||
FD_SET(args->tun, efds);
|
||||
int max = args->tun;
|
||||
|
||||
// Select ICMP sockets
|
||||
struct icmp_session *i = icmp_session;
|
||||
while (i != NULL) {
|
||||
if (!i->stop) {
|
||||
if (is_valid_fd(i->socket)) {
|
||||
FD_SET(i->socket, efds);
|
||||
FD_SET(i->socket, rfds);
|
||||
if (i->socket > max)
|
||||
max = i->socket;
|
||||
} else {
|
||||
log_android(ANDROID_LOG_WARN, "ICMP socket %d select error %d: %s",
|
||||
i->socket, errno, strerror(errno));
|
||||
i->stop = 1;
|
||||
}
|
||||
}
|
||||
i = i->next;
|
||||
}
|
||||
|
||||
// Select UDP sockets
|
||||
struct udp_session *u = udp_session;
|
||||
while (u != NULL) {
|
||||
if (u->state == UDP_ACTIVE) {
|
||||
if (is_valid_fd(u->socket)) {
|
||||
FD_SET(u->socket, efds);
|
||||
FD_SET(u->socket, rfds);
|
||||
if (u->socket > max)
|
||||
max = u->socket;
|
||||
}
|
||||
else {
|
||||
log_android(ANDROID_LOG_WARN, "UDP socket %d select error %d: %s",
|
||||
u->socket, errno, strerror(errno));
|
||||
u->state = UDP_FINISHING;
|
||||
}
|
||||
}
|
||||
u = u->next;
|
||||
}
|
||||
|
||||
// Select TCP sockets
|
||||
struct tcp_session *t = tcp_session;
|
||||
while (t != NULL) {
|
||||
// Select sockets
|
||||
if (t->socket >= 0) {
|
||||
if (is_valid_fd(t->socket)) {
|
||||
if (t->state == TCP_LISTEN) {
|
||||
// Check for errors
|
||||
FD_SET(t->socket, efds);
|
||||
|
||||
// Check for connected = writable
|
||||
FD_SET(t->socket, wfds);
|
||||
|
||||
if (t->socket > max)
|
||||
max = t->socket;
|
||||
}
|
||||
else if (t->state == TCP_ESTABLISHED || t->state == TCP_CLOSE_WAIT) {
|
||||
// Check errors
|
||||
FD_SET(t->socket, efds);
|
||||
|
||||
// Check for incoming data
|
||||
if (get_send_window(t) > 0)
|
||||
FD_SET(t->socket, rfds);
|
||||
|
||||
// Check for outgoing data
|
||||
if (t->forward != NULL)
|
||||
FD_SET(t->socket, wfds);
|
||||
|
||||
if (t->socket > max)
|
||||
max = t->socket;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_android(ANDROID_LOG_WARN, "TCP socket %d select error %d: %s",
|
||||
t->socket, errno, strerror(errno));
|
||||
write_rst(args, t);
|
||||
}
|
||||
}
|
||||
|
||||
t = t->next;
|
||||
}
|
||||
|
||||
return max;
|
||||
}
|
||||
|
||||
void check_allowed(const struct arguments *args) {
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
|
||||
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));
|
||||
struct ng_session *l = NULL;
|
||||
struct ng_session *s = ng_session;
|
||||
while (s != NULL) {
|
||||
if (s->protocol == IPPROTO_ICMP || s->protocol == IPPROTO_ICMPV6) {
|
||||
if (!s->icmp.stop) {
|
||||
if (s->icmp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->icmp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->icmp.daddr.ip4, dest, sizeof(dest));
|
||||
}
|
||||
else {
|
||||
inet_ntop(AF_INET6, &s->icmp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->icmp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
jobject objPacket = create_packet(
|
||||
args, s->icmp.version, IPPROTO_ICMP, "",
|
||||
source, 0, dest, 0, "", s->icmp.uid, 0);
|
||||
if (is_address_allowed(args, objPacket) == NULL) {
|
||||
s->icmp.stop = 1;
|
||||
log_android(ANDROID_LOG_WARN, "ICMP terminate %d uid %d",
|
||||
s->socket, s->icmp.uid);
|
||||
}
|
||||
}
|
||||
|
||||
jobject objPacket = create_packet(
|
||||
args, i->version, IPPROTO_ICMP, "",
|
||||
source, 0, dest, 0, "", i->uid, 0);
|
||||
if (is_address_allowed(args, objPacket) == NULL) {
|
||||
i->stop = 1;
|
||||
log_android(ANDROID_LOG_WARN, "ICMP terminate %d uid %d", i->socket, i->uid);
|
||||
} else if (s->protocol == IPPROTO_UDP) {
|
||||
if (s->udp.state == UDP_ACTIVE) {
|
||||
if (s->udp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->udp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest));
|
||||
}
|
||||
else {
|
||||
inet_ntop(AF_INET6, &s->udp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
jobject objPacket = create_packet(
|
||||
args, s->udp.version, IPPROTO_UDP, "",
|
||||
source, ntohs(s->udp.source), dest, ntohs(s->udp.dest), "", s->udp.uid, 0);
|
||||
if (is_address_allowed(args, objPacket) == NULL) {
|
||||
s->udp.state = UDP_FINISHING;
|
||||
log_android(ANDROID_LOG_WARN, "UDP terminate session socket %d uid %d",
|
||||
s->socket, s->udp.uid);
|
||||
}
|
||||
}
|
||||
else if (s->udp.state == UDP_BLOCKED) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP remove blocked session uid %d", s->udp.uid);
|
||||
|
||||
if (l == NULL)
|
||||
ng_session = s->next;
|
||||
else
|
||||
l->next = s->next;
|
||||
|
||||
struct ng_session *c = s;
|
||||
s = s->next;
|
||||
free(c);
|
||||
continue;
|
||||
}
|
||||
|
||||
} else if (s->protocol == IPPROTO_TCP) {
|
||||
if (s->tcp.state != TCP_CLOSING && s->tcp.state != TCP_CLOSE) {
|
||||
if (s->tcp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->tcp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->tcp.daddr.ip4, dest, sizeof(dest));
|
||||
}
|
||||
else {
|
||||
inet_ntop(AF_INET6, &s->tcp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->tcp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
jobject objPacket = create_packet(
|
||||
args, s->tcp.version, IPPROTO_TCP, "",
|
||||
source, ntohs(s->tcp.source), dest, ntohs(s->tcp.dest), "", s->tcp.uid, 0);
|
||||
if (is_address_allowed(args, objPacket) == NULL) {
|
||||
write_rst(args, &s->tcp);
|
||||
log_android(ANDROID_LOG_WARN, "TCP terminate socket %d uid %d",
|
||||
s->socket, s->tcp.uid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
i = i->next;
|
||||
}
|
||||
|
||||
struct udp_session *l = NULL;
|
||||
struct udp_session *u = udp_session;
|
||||
while (u != NULL) {
|
||||
if (u->state == UDP_ACTIVE) {
|
||||
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, "",
|
||||
source, ntohs(u->source), dest, ntohs(u->dest), "", u->uid, 0);
|
||||
if (is_address_allowed(args, objPacket) == NULL) {
|
||||
u->state = UDP_FINISHING;
|
||||
log_android(ANDROID_LOG_WARN, "UDP terminate session socket %d uid %d",
|
||||
u->socket, u->uid);
|
||||
}
|
||||
}
|
||||
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;
|
||||
u = u->next;
|
||||
}
|
||||
|
||||
struct tcp_session *t = tcp_session;
|
||||
while (t != NULL) {
|
||||
if (t->state != TCP_CLOSING && 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, "",
|
||||
source, ntohs(t->source), dest, ntohs(t->dest), "", t->uid, 0);
|
||||
if (is_address_allowed(args, objPacket) == NULL) {
|
||||
write_rst(args, t);
|
||||
log_android(ANDROID_LOG_WARN, "TCP terminate socket %d uid %d",
|
||||
t->socket, t->uid);
|
||||
}
|
||||
}
|
||||
t = t->next;
|
||||
l = s;
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
File diff suppressed because it is too large
Load Diff
|
@ -19,33 +19,16 @@
|
|||
|
||||
#include "netguard.h"
|
||||
|
||||
struct udp_session *udp_session = NULL;
|
||||
extern struct ng_session *ng_session;
|
||||
extern FILE *pcap_file;
|
||||
|
||||
void init_udp(const struct arguments *args) {
|
||||
udp_session = NULL;
|
||||
}
|
||||
|
||||
void clear_udp() {
|
||||
struct udp_session *u = udp_session;
|
||||
while (u != NULL) {
|
||||
if (u->socket >= 0 && close(u->socket))
|
||||
log_android(ANDROID_LOG_ERROR, "UDP close %d error %d: %s",
|
||||
-u->socket, errno, strerror(errno));
|
||||
struct udp_session *p = u;
|
||||
u = u->next;
|
||||
free(p);
|
||||
}
|
||||
udp_session = NULL;
|
||||
}
|
||||
|
||||
int get_udp_sessions() {
|
||||
int count = 0;
|
||||
struct udp_session *uc = udp_session;
|
||||
while (uc != NULL) {
|
||||
if (uc->state == UDP_ACTIVE)
|
||||
struct ng_session *s = ng_session;
|
||||
while (s != NULL) {
|
||||
if (s->protocol == IPPROTO_UDP && s->udp.state == UDP_ACTIVE)
|
||||
count++;
|
||||
uc = uc->next;
|
||||
s = s->next;
|
||||
}
|
||||
return count;
|
||||
}
|
||||
|
@ -62,137 +45,138 @@ int get_udp_timeout(const struct udp_session *u, int sessions, int maxsessions)
|
|||
void check_udp_sessions(const struct arguments *args, int sessions, int maxsessions) {
|
||||
time_t now = time(NULL);
|
||||
|
||||
struct udp_session *ul = NULL;
|
||||
struct udp_session *u = udp_session;
|
||||
while (u != NULL) {
|
||||
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));
|
||||
}
|
||||
struct ng_session *sl = NULL;
|
||||
struct ng_session *s = ng_session;
|
||||
while (s != NULL) {
|
||||
if (s->protocol == IPPROTO_UDP) {
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (s->udp.version == 4) {
|
||||
inet_ntop(AF_INET, &s->udp.saddr.ip4, source, sizeof(source));
|
||||
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest));
|
||||
}
|
||||
else {
|
||||
inet_ntop(AF_INET6, &s->udp.saddr.ip6, source, sizeof(source));
|
||||
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
// Check session timeout
|
||||
int timeout = get_udp_timeout(u, sessions, maxsessions);
|
||||
if (u->state == UDP_ACTIVE && u->time + timeout < now) {
|
||||
log_android(ANDROID_LOG_WARN, "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 session timeout
|
||||
int timeout = get_udp_timeout(&s->udp, sessions, maxsessions);
|
||||
if (s->udp.state == UDP_ACTIVE && s->udp.time + timeout < now) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP idle %d/%d sec state %d from %s/%u to %s/%u",
|
||||
now - s->udp.time, timeout, s->udp.state,
|
||||
source, ntohs(s->udp.source), dest, ntohs(s->udp.dest));
|
||||
s->udp.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);
|
||||
// Check finished sessions
|
||||
if (s->udp.state == UDP_FINISHING) {
|
||||
log_android(ANDROID_LOG_INFO, "UDP close from %s/%u to %s/%u socket %d",
|
||||
source, ntohs(s->udp.source), dest, ntohs(s->udp.dest), s->socket);
|
||||
|
||||
if (close(u->socket))
|
||||
log_android(ANDROID_LOG_ERROR, "UDP close %d error %d: %s",
|
||||
u->socket, errno, strerror(errno));
|
||||
u->socket = -1;
|
||||
if (close(s->socket))
|
||||
log_android(ANDROID_LOG_ERROR, "UDP close %d error %d: %s",
|
||||
s->socket, errno, strerror(errno));
|
||||
s->socket = -1;
|
||||
|
||||
u->time = time(NULL);
|
||||
u->state = UDP_CLOSED;
|
||||
}
|
||||
s->udp.time = time(NULL);
|
||||
s->udp.state = UDP_CLOSED;
|
||||
}
|
||||
|
||||
if (u->state == UDP_CLOSED && (u->sent || u->received)) {
|
||||
account_usage(args, u->version, IPPROTO_UDP,
|
||||
dest, ntohs(u->dest), u->uid, u->sent, u->received);
|
||||
u->sent = 0;
|
||||
u->received = 0;
|
||||
}
|
||||
if (s->udp.state == UDP_CLOSED && (s->udp.sent || s->udp.received)) {
|
||||
account_usage(args, s->udp.version, IPPROTO_UDP,
|
||||
dest, ntohs(s->udp.dest), s->udp.uid, s->udp.sent, s->udp.received);
|
||||
s->udp.sent = 0;
|
||||
s->udp.received = 0;
|
||||
}
|
||||
|
||||
// Cleanup lingering sessions
|
||||
if ((u->state == UDP_CLOSED || u->state == UDP_BLOCKED) &&
|
||||
u->time + UDP_KEEP_TIMEOUT < now) {
|
||||
if (ul == NULL)
|
||||
udp_session = u->next;
|
||||
else
|
||||
ul->next = u->next;
|
||||
// Cleanup lingering sessions
|
||||
if ((s->udp.state == UDP_CLOSED || s->udp.state == UDP_BLOCKED) &&
|
||||
s->udp.time + UDP_KEEP_TIMEOUT < now) {
|
||||
if (sl == NULL)
|
||||
ng_session = s->next;
|
||||
else
|
||||
sl->next = s->next;
|
||||
|
||||
struct udp_session *c = u;
|
||||
u = u->next;
|
||||
free(c);
|
||||
}
|
||||
else {
|
||||
ul = u;
|
||||
u = u->next;
|
||||
struct ng_session *c = s;
|
||||
s = s->next;
|
||||
free(c);
|
||||
}
|
||||
else {
|
||||
sl = s;
|
||||
s = s->next;
|
||||
}
|
||||
} else {
|
||||
sl = s;
|
||||
s = s->next;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
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) {
|
||||
if (cur->socket >= 0) {
|
||||
// Check socket error
|
||||
if (FD_ISSET(cur->socket, efds)) {
|
||||
cur->time = time(NULL);
|
||||
void check_udp_socket(const struct arguments *args, const struct epoll_event *ev) {
|
||||
struct ng_session *s = (struct ng_session *) ev->data.ptr;
|
||||
|
||||
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));
|
||||
// Check socket error
|
||||
if (ev->events & EPOLLERR) {
|
||||
s->udp.time = time(NULL);
|
||||
|
||||
cur->state = UDP_FINISHING;
|
||||
int serr = 0;
|
||||
socklen_t optlen = sizeof(int);
|
||||
int err = getsockopt(s->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));
|
||||
|
||||
s->udp.state = UDP_FINISHING;
|
||||
}
|
||||
else {
|
||||
// Check socket read
|
||||
if (ev->events & EPOLLIN) {
|
||||
s->udp.time = time(NULL);
|
||||
|
||||
uint8_t *buffer = malloc(s->udp.mss);
|
||||
ssize_t bytes = recv(s->socket, buffer, s->udp.mss, 0);
|
||||
if (bytes < 0) {
|
||||
// Socket error
|
||||
log_android(ANDROID_LOG_WARN, "UDP recv error %d: %s",
|
||||
errno, strerror(errno));
|
||||
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
s->udp.state = UDP_FINISHING;
|
||||
}
|
||||
else {
|
||||
// Check socket read
|
||||
if (FD_ISSET(cur->socket, rfds)) {
|
||||
cur->time = time(NULL);
|
||||
else if (bytes == 0) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP recv eof");
|
||||
s->udp.state = UDP_FINISHING;
|
||||
|
||||
uint8_t *buffer = malloc(cur->mss);
|
||||
ssize_t bytes = recv(cur->socket, buffer, cur->mss, 0);
|
||||
if (bytes < 0) {
|
||||
// Socket error
|
||||
log_android(ANDROID_LOG_WARN, "UDP recv error %d: %s",
|
||||
errno, strerror(errno));
|
||||
} else {
|
||||
// Socket read data
|
||||
char dest[INET6_ADDRSTRLEN + 1];
|
||||
if (s->udp.version == 4)
|
||||
inet_ntop(AF_INET, &s->udp.daddr.ip4, dest, sizeof(dest));
|
||||
else
|
||||
inet_ntop(AF_INET6, &s->udp.daddr.ip6, dest, sizeof(dest));
|
||||
log_android(ANDROID_LOG_INFO, "UDP recv bytes %d from %s/%u for tun",
|
||||
bytes, dest, ntohs(s->udp.dest));
|
||||
|
||||
if (errno != EINTR && errno != EAGAIN)
|
||||
cur->state = UDP_FINISHING;
|
||||
}
|
||||
else if (bytes == 0) {
|
||||
log_android(ANDROID_LOG_WARN, "UDP recv eof");
|
||||
cur->state = UDP_FINISHING;
|
||||
s->udp.received += bytes;
|
||||
|
||||
} 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));
|
||||
log_android(ANDROID_LOG_INFO, "UDP recv bytes %d from %s/%u for tun",
|
||||
bytes, dest, ntohs(cur->dest));
|
||||
// Process DNS response
|
||||
if (ntohs(s->udp.dest) == 53)
|
||||
parse_dns_response(args, buffer, (size_t) bytes);
|
||||
|
||||
cur->received += bytes;
|
||||
|
||||
// Process DNS response
|
||||
if (ntohs(cur->dest) == 53)
|
||||
parse_dns_response(args, buffer, (size_t) bytes);
|
||||
|
||||
// Forward to tun
|
||||
if (write_udp(args, cur, buffer, (size_t) bytes) < 0)
|
||||
cur->state = UDP_FINISHING;
|
||||
else {
|
||||
// Prevent too many open files
|
||||
if (ntohs(cur->dest) == 53)
|
||||
cur->state = UDP_FINISHING;
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
// Forward to tun
|
||||
if (write_udp(args, &s->udp, buffer, (size_t) bytes) < 0)
|
||||
s->udp.state = UDP_FINISHING;
|
||||
else {
|
||||
// Prevent too many open files
|
||||
if (ntohs(s->udp.dest) == 53)
|
||||
s->udp.state = UDP_FINISHING;
|
||||
}
|
||||
}
|
||||
free(buffer);
|
||||
}
|
||||
cur = cur->next;
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -207,14 +191,15 @@ int has_udp_session(const struct arguments *args, const uint8_t *pkt, const uint
|
|||
return 1;
|
||||
|
||||
// Search session
|
||||
struct udp_session *cur = udp_session;
|
||||
struct ng_session *cur = ng_session;
|
||||
while (cur != NULL &&
|
||||
!(cur->version == version &&
|
||||
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 &&
|
||||
memcmp(&cur->daddr.ip6, &ip6->ip6_dst, 16) == 0)))
|
||||
!(cur->protocol == IPPROTO_UDP &&
|
||||
cur->udp.version == version &&
|
||||
cur->udp.source == udphdr->source && cur->udp.dest == udphdr->dest &&
|
||||
(version == 4 ? cur->udp.saddr.ip4 == ip4->saddr &&
|
||||
cur->udp.daddr.ip4 == ip4->daddr
|
||||
: memcmp(&cur->udp.saddr.ip6, &ip6->ip6_src, 16) == 0 &&
|
||||
memcmp(&cur->udp.daddr.ip6, &ip6->ip6_dst, 16) == 0)))
|
||||
cur = cur->next;
|
||||
|
||||
return (cur != NULL);
|
||||
|
@ -244,32 +229,35 @@ void block_udp(const struct arguments *args,
|
|||
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;
|
||||
struct ng_session *s = malloc(sizeof(struct ng_session));
|
||||
s->protocol = IPPROTO_UDP;
|
||||
|
||||
s->udp.time = time(NULL);
|
||||
s->udp.uid = uid;
|
||||
s->udp.version = version;
|
||||
|
||||
if (version == 4) {
|
||||
u->saddr.ip4 = (__be32) ip4->saddr;
|
||||
u->daddr.ip4 = (__be32) ip4->daddr;
|
||||
s->udp.saddr.ip4 = (__be32) ip4->saddr;
|
||||
s->udp.daddr.ip4 = (__be32) ip4->daddr;
|
||||
} else {
|
||||
memcpy(&u->saddr.ip6, &ip6->ip6_src, 16);
|
||||
memcpy(&u->daddr.ip6, &ip6->ip6_dst, 16);
|
||||
memcpy(&s->udp.saddr.ip6, &ip6->ip6_src, 16);
|
||||
memcpy(&s->udp.daddr.ip6, &ip6->ip6_dst, 16);
|
||||
}
|
||||
|
||||
u->source = udphdr->source;
|
||||
u->dest = udphdr->dest;
|
||||
u->state = UDP_BLOCKED;
|
||||
u->socket = -1;
|
||||
s->udp.source = udphdr->source;
|
||||
s->udp.dest = udphdr->dest;
|
||||
s->udp.state = UDP_BLOCKED;
|
||||
s->socket = -1;
|
||||
|
||||
u->next = udp_session;
|
||||
udp_session = u;
|
||||
s->next = ng_session;
|
||||
ng_session = s;
|
||||
}
|
||||
|
||||
jboolean handle_udp(const struct arguments *args,
|
||||
const uint8_t *pkt, size_t length,
|
||||
const uint8_t *payload,
|
||||
int uid, struct allowed *redirect) {
|
||||
int uid, struct allowed *redirect,
|
||||
const int epoll_fd) {
|
||||
// Get headers
|
||||
const uint8_t version = (*pkt) >> 4;
|
||||
const struct iphdr *ip4 = (struct iphdr *) pkt;
|
||||
|
@ -279,14 +267,15 @@ jboolean handle_udp(const struct arguments *args,
|
|||
const size_t datalen = length - (data - pkt);
|
||||
|
||||
// Search session
|
||||
struct udp_session *cur = udp_session;
|
||||
struct ng_session *cur = ng_session;
|
||||
while (cur != NULL &&
|
||||
!(cur->version == version &&
|
||||
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 &&
|
||||
memcmp(&cur->daddr.ip6, &ip6->ip6_dst, 16) == 0)))
|
||||
!(cur->protocol == IPPROTO_UDP &&
|
||||
cur->udp.version == version &&
|
||||
cur->udp.source == udphdr->source && cur->udp.dest == udphdr->dest &&
|
||||
(version == 4 ? cur->udp.saddr.ip4 == ip4->saddr &&
|
||||
cur->udp.daddr.ip4 == ip4->daddr
|
||||
: memcmp(&cur->udp.saddr.ip6, &ip6->ip6_src, 16) == 0 &&
|
||||
memcmp(&cur->udp.daddr.ip6, &ip6->ip6_dst, 16) == 0)))
|
||||
cur = cur->next;
|
||||
|
||||
char source[INET6_ADDRSTRLEN + 1];
|
||||
|
@ -299,9 +288,9 @@ jboolean handle_udp(const struct arguments *args,
|
|||
inet_ntop(AF_INET6, &ip6->ip6_dst, dest, sizeof(dest));
|
||||
}
|
||||
|
||||
if (cur != NULL && cur->state != UDP_ACTIVE) {
|
||||
if (cur != NULL && cur->udp.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);
|
||||
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest), cur->udp.state);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -311,47 +300,56 @@ jboolean handle_udp(const struct arguments *args,
|
|||
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;
|
||||
struct ng_session *s = malloc(sizeof(struct ng_session));
|
||||
s->protocol = IPPROTO_UDP;
|
||||
|
||||
s->udp.time = time(NULL);
|
||||
s->udp.uid = uid;
|
||||
s->udp.version = version;
|
||||
|
||||
int rversion;
|
||||
if (redirect == NULL)
|
||||
rversion = u->version;
|
||||
rversion = s->udp.version;
|
||||
else
|
||||
rversion = (strstr(redirect->raddr, ":") == NULL ? 4 : 6);
|
||||
u->mss = (uint16_t) (rversion == 4 ? UDP4_MAXMSG : UDP6_MAXMSG);
|
||||
s->udp.mss = (uint16_t) (rversion == 4 ? UDP4_MAXMSG : UDP6_MAXMSG);
|
||||
|
||||
u->sent = 0;
|
||||
u->received = 0;
|
||||
s->udp.sent = 0;
|
||||
s->udp.received = 0;
|
||||
|
||||
if (version == 4) {
|
||||
u->saddr.ip4 = (__be32) ip4->saddr;
|
||||
u->daddr.ip4 = (__be32) ip4->daddr;
|
||||
s->udp.saddr.ip4 = (__be32) ip4->saddr;
|
||||
s->udp.daddr.ip4 = (__be32) ip4->daddr;
|
||||
} else {
|
||||
memcpy(&u->saddr.ip6, &ip6->ip6_src, 16);
|
||||
memcpy(&u->daddr.ip6, &ip6->ip6_dst, 16);
|
||||
memcpy(&s->udp.saddr.ip6, &ip6->ip6_src, 16);
|
||||
memcpy(&s->udp.daddr.ip6, &ip6->ip6_dst, 16);
|
||||
}
|
||||
|
||||
u->source = udphdr->source;
|
||||
u->dest = udphdr->dest;
|
||||
u->state = UDP_ACTIVE;
|
||||
u->next = NULL;
|
||||
s->udp.source = udphdr->source;
|
||||
s->udp.dest = udphdr->dest;
|
||||
s->udp.state = UDP_ACTIVE;
|
||||
s->next = NULL;
|
||||
|
||||
// Open UDP socket
|
||||
u->socket = open_udp_socket(args, u, redirect);
|
||||
if (u->socket < 0) {
|
||||
free(u);
|
||||
s->socket = open_udp_socket(args, &s->udp, redirect);
|
||||
if (s->socket < 0) {
|
||||
free(s);
|
||||
return 0;
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "UDP socket %d", u->socket);
|
||||
log_android(ANDROID_LOG_DEBUG, "UDP socket %d", s->socket);
|
||||
|
||||
u->next = udp_session;
|
||||
udp_session = u;
|
||||
// Monitor events
|
||||
memset(&s->ev, 0, sizeof(struct epoll_event));
|
||||
s->ev.events = EPOLLIN | EPOLLERR;
|
||||
s->ev.data.ptr = s;
|
||||
if (epoll_ctl(epoll_fd, EPOLL_CTL_ADD, s->socket, &s->ev))
|
||||
log_android(ANDROID_LOG_ERROR, "epoll add udp error %d: %s", errno, strerror(errno));
|
||||
|
||||
cur = u;
|
||||
s->next = ng_session;
|
||||
ng_session = s;
|
||||
|
||||
cur = s;
|
||||
}
|
||||
|
||||
// Check for DNS
|
||||
|
@ -359,23 +357,23 @@ jboolean handle_udp(const struct arguments *args,
|
|||
char qname[DNS_QNAME_MAX + 1];
|
||||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
if (get_dns_query(args, cur, data, datalen, &qtype, &qclass, qname) >= 0) {
|
||||
if (get_dns_query(args, &cur->udp, data, datalen, &qtype, &qclass, qname) >= 0) {
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"DNS query qtype %d qclass %d name %s",
|
||||
qtype, qclass, qname);
|
||||
|
||||
if (check_domain(args, cur, data, datalen, qclass, qtype, qname)) {
|
||||
if (check_domain(args, &cur->udp, data, datalen, qclass, qtype, qname)) {
|
||||
// Log qname
|
||||
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),
|
||||
source, ntohs(cur->udp.source), dest, ntohs(cur->udp.dest),
|
||||
name, 0, 0);
|
||||
log_packet(args, objPacket);
|
||||
|
||||
// Session done
|
||||
cur->state = UDP_FINISHING;
|
||||
cur->udp.state = UDP_FINISHING;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
@ -383,28 +381,28 @@ jboolean handle_udp(const struct arguments *args,
|
|||
|
||||
// Check for DHCP (tethering)
|
||||
if (ntohs(udphdr->source) == 68 || ntohs(udphdr->dest) == 67) {
|
||||
if (check_dhcp(args, cur, data, datalen) >= 0)
|
||||
if (check_dhcp(args, &cur->udp, data, datalen) >= 0)
|
||||
return 1;
|
||||
}
|
||||
|
||||
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);
|
||||
|
||||
cur->time = time(NULL);
|
||||
cur->udp.time = time(NULL);
|
||||
|
||||
int rversion;
|
||||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
if (redirect == NULL) {
|
||||
rversion = cur->version;
|
||||
if (cur->version == 4) {
|
||||
rversion = cur->udp.version;
|
||||
if (cur->udp.version == 4) {
|
||||
addr4.sin_family = AF_INET;
|
||||
addr4.sin_addr.s_addr = (__be32) cur->daddr.ip4;
|
||||
addr4.sin_port = cur->dest;
|
||||
addr4.sin_addr.s_addr = (__be32) cur->udp.daddr.ip4;
|
||||
addr4.sin_port = cur->udp.dest;
|
||||
} else {
|
||||
addr6.sin6_family = AF_INET6;
|
||||
memcpy(&addr6.sin6_addr, &cur->daddr.ip6, 16);
|
||||
addr6.sin6_port = cur->dest;
|
||||
memcpy(&addr6.sin6_addr, &cur->udp.daddr.ip6, 16);
|
||||
addr6.sin6_port = cur->udp.dest;
|
||||
}
|
||||
} else {
|
||||
rversion = (strstr(redirect->raddr, ":") == NULL ? 4 : 6);
|
||||
|
@ -428,12 +426,12 @@ jboolean handle_udp(const struct arguments *args,
|
|||
(socklen_t) (rversion == 4 ? sizeof(addr4) : sizeof(addr6))) != datalen) {
|
||||
log_android(ANDROID_LOG_ERROR, "UDP sendto error %d: %s", errno, strerror(errno));
|
||||
if (errno != EINTR && errno != EAGAIN) {
|
||||
cur->state = UDP_FINISHING;
|
||||
cur->udp.state = UDP_FINISHING;
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
cur->sent += datalen;
|
||||
cur->udp.sent += datalen;
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue