Native rewrite address/domain check

This commit is contained in:
M66B 2016-01-28 11:58:39 +01:00
parent db5f58aa70
commit e16c4c4af2
6 changed files with 353 additions and 297 deletions

View File

@ -23,11 +23,13 @@
#NetGuard
-keepnames class eu.faircode.netguard.** { *; }
#JNI callback
#JNI callbacks
-keep class eu.faircode.netguard.Packet { *; }
-keep class eu.faircode.netguard.SinkholeService {
void nativeExit(java.lang.String);
void logPacket(eu.faircode.netguard.Packet);
boolean isDomainBlocked(java.lang.String);
boolean isAddressAllowed(eu.faircode.netguard.Packet);
}
#Support library

View File

@ -211,10 +211,7 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
// Development
if (!(Util.isDebuggable(this) || Util.getSelfVersionName(this).contains("beta"))) {
screen.removePreference(screen.findPreference("category_development"));
SharedPreferences.Editor edit = prefs.edit();
edit.remove("debug");
edit.remove("loglevel");
edit.apply();
prefs.edit().remove("loglevel").apply();
}
// Handle technical info
@ -507,7 +504,7 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
} else if ("stats_samples".equals(name)) {
getPreferenceScreen().findPreference(name).setTitle(getString(R.string.setting_stats_samples, prefs.getString(name, "90")));
} else if ("debug".equals(name) || "loglevel".equals(name))
} else if ("loglevel".equals(name))
SinkholeService.reload(null, "changed " + name, this);
}

View File

@ -62,7 +62,9 @@ import android.util.Log;
import android.util.TypedValue;
import android.widget.RemoteViews;
import java.io.BufferedReader;
import java.io.File;
import java.io.FileReader;
import java.io.IOException;
import java.net.InetAddress;
import java.util.ArrayList;
@ -90,6 +92,9 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
private Object subscriptionsChangedListener = null;
private ParcelFileDescriptor vpn = null;
private HashMap<String, Boolean> mapDomainBlocked = new HashMap<>();
private HashMap<Integer, Boolean> mapUidAllowed = new HashMap<>();
private volatile Looper mServiceLooper;
private volatile ServiceHandler mServiceHandler;
@ -122,7 +127,7 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
private native void jni_init();
private native void jni_start(int tun, int[] uids, String hosts, boolean log, boolean filter, boolean debug, int loglevel);
private native void jni_start(int tun, int loglevel);
private native void jni_stop(int tun, boolean clear);
@ -764,18 +769,56 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
}
private void startNative(ParcelFileDescriptor vpn, List<Rule> listAllowed) {
prepareAllowed(listAllowed);
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SinkholeService.this);
boolean log = prefs.getBoolean("log", false);
boolean filter = prefs.getBoolean("filter", false);
boolean use_hosts = prefs.getBoolean("use_hosts", false);
if (log || filter) {
boolean debug = prefs.getBoolean("debug", false);
int prio = Integer.parseInt(prefs.getString("loglevel", Integer.toString(Log.INFO)));
if (!debug)
prio = Log.WARN;
jni_start(vpn.getFd(), prio);
}
}
private void prepareAllowed(List<Rule> listAllowed) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(SinkholeService.this);
boolean use_hosts = prefs.getBoolean("use_hosts", false);
mapUidAllowed.clear();
for (Rule rule : listAllowed)
mapUidAllowed.put(rule.info.applicationInfo.uid, true);
mapDomainBlocked.clear();
if (use_hosts) {
File hosts = new File(getFilesDir(), "hosts.txt");
String hname = (use_hosts && hosts.exists() ? hosts.getAbsolutePath() : null);
jni_start(vpn.getFd(), getAllowedUids(listAllowed), hname, log, filter, debug, prio);
BufferedReader br = null;
try {
br = new BufferedReader(new FileReader(hosts));
String line;
while ((line = br.readLine()) != null) {
int hash = line.indexOf('#');
if (hash >= 0)
line = line.substring(0, hash);
line = line.trim();
if (line.length() > 0) {
String[] words = line.split("\\s+");
if (words.length == 2)
mapDomainBlocked.put(words[1], true);
else
Log.i(TAG, "Invalid hosts file line: " + line);
}
}
br.close();
} catch (IOException ex) {
Log.e(TAG, ex.toString() + "\n" + Log.getStackTraceString(ex));
} finally {
if (br != null)
try {
br.close();
} catch (IOException exex) {
Log.e(TAG, exex.toString() + "\n" + Log.getStackTraceString(exex));
}
}
}
}
@ -843,15 +886,6 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
return listAllowed;
}
private int[] getAllowedUids(List<Rule> listAllowed) {
int[] uid = new int[listAllowed.size() + 2];
uid[0] = -1; // Allow unknown
uid[1] = 0; // Allow root (DNS, etc)
for (int i = 0; i < listAllowed.size(); i++)
uid[i + 2] = listAllowed.get(i).info.applicationInfo.uid;
return uid;
}
private void stopVPN(ParcelFileDescriptor pfd) {
Log.i(TAG, "Stopping");
try {
@ -880,6 +914,30 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
mServiceHandler.sendMessage(msg);
}
// Called from native code
private boolean isDomainBlocked(String name) {
boolean blocked = (mapDomainBlocked.containsKey(name) && mapDomainBlocked.get(name));
return blocked;
}
// Called from native code
private boolean isAddressAllowed(Packet packet) {
SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this);
if (prefs.getBoolean("filter", false)) {
if (packet.uid <= 0) // unknown, root
packet.allowed = true;
else
packet.allowed = (mapUidAllowed.containsKey(packet.uid) && mapUidAllowed.get(packet.uid));
} else
packet.allowed = false;
if (prefs.getBoolean("log", false))
logPacket(packet);
return packet.allowed;
}
private BroadcastReceiver interactiveStateReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {

View File

@ -54,7 +54,6 @@ jboolean signaled = 0;
struct udp_session *udp_session = NULL;
struct tcp_session *tcp_session = NULL;
int debug = 0;
int loglevel = 0;
FILE *pcap_file = NULL;
@ -100,18 +99,10 @@ Java_eu_faircode_netguard_SinkholeService_jni_1init(JNIEnv *env) {
}
JNIEXPORT void JNICALL
Java_eu_faircode_netguard_SinkholeService_jni_1start(
JNIEnv *env, jobject instance,
jint tun, jintArray uids_,
jstring hosts_,
jboolean log, jboolean filter,
jboolean debug_, jint loglevel_) {
Java_eu_faircode_netguard_SinkholeService_jni_1start(JNIEnv *env, jobject instance,
jint tun, jint loglevel) {
debug = debug_;
loglevel = loglevel_;
log_android(ANDROID_LOG_WARN, "Starting tun=%d log %d filter %d debug %d level %d",
tun, log, filter, debug, loglevel);
log_android(ANDROID_LOG_WARN, "Starting tun=%d level %d", tun, loglevel);
// Set blocking
int flags = fcntl(tun, F_GETFL, 0);
@ -132,32 +123,6 @@ Java_eu_faircode_netguard_SinkholeService_jni_1start(
args->instance = (*env)->NewGlobalRef(env, instance);
args->tun = tun;
args->ucount = (*env)->GetArrayLength(env, uids_);
args->uids = malloc(args->ucount * sizeof(jint));
jint *uids = (*env)->GetIntArrayElements(env, uids_, NULL);
memcpy(args->uids, uids, args->ucount * sizeof(jint));
(*env)->ReleaseIntArrayElements(env, uids_, uids, 0);
args->log = log;
args->filter = filter;
if (hosts_ == NULL) {
args->hcount = 0;
args->hosts = NULL;
log_android(ANDROID_LOG_WARN, "No hosts file");
} else {
const char *hosts = (*env)->GetStringUTFChars(env, hosts_, 0);
log_android(ANDROID_LOG_WARN, "hosts file %s", hosts);
read_hosts(hosts, args);
(*env)->ReleaseStringUTFChars(env, hosts_, hosts);
}
for (int i = 0; i < args->ucount; i++)
log_android(ANDROID_LOG_VERBOSE, "Allowed uid %d", args->uids[i]);
// Terminate sessions not allowed anymore
check_allowed(args);
// Start native thread
int err = pthread_create(&thread_id, NULL, handle_events, (void *) args);
if (err == 0)
@ -263,42 +228,6 @@ Java_eu_faircode_netguard_Util_jni_1getprop(JNIEnv *env, jclass type, jstring na
// Private functions
void check_allowed(const struct arguments *args) {
struct udp_session *u = udp_session;
while (u != NULL) {
if (!u->stop) {
int found = 0;
for (int i = 0; i < args->ucount; i++)
if (u->uid == args->uids[i]) {
found = 1;
break;
}
if (!found) {
u->stop = 1;
log_android(ANDROID_LOG_WARN, "UDP terminate %d uid %d", u->socket, u->uid);
}
}
u = u->next;
}
struct tcp_session *t = tcp_session;
while (t != NULL) {
if (t->state != TCP_TIME_WAIT && t->state != TCP_CLOSE) {
int found = 0;
for (int i = 0; i < args->ucount; i++)
if (t->uid == args->uids[i]) {
found = 1;
break;
}
if (!found) {
t->state = TCP_TIME_WAIT;
log_android(ANDROID_LOG_WARN, "TCP terminate socket %d uid %d", t->socket, t->uid);
}
}
t = t->next;
}
}
void clear_sessions() {
struct udp_session *u = udp_session;
while (u != NULL) {
@ -360,6 +289,9 @@ void *handle_events(void *a) {
sa.sa_flags = SA_RESTART;
sigaction(SIGUSR1, &sa, NULL);
// Terminate existing sessions not allowed anymore
check_allowed(args);
stopping = 0;
signaled = 0;
@ -474,10 +406,6 @@ void *handle_events(void *a) {
log_android(ANDROID_LOG_ERROR, "DetachCurrentThread failed");
// Cleanup
free(args->uids);
for (int i = 0; i < args->hcount; i++)
free(args->hosts[i]);
free(args->hosts);
free(args);
log_android(ANDROID_LOG_WARN, "Stopped events tun=%d thread %lu", args->tun, thread_id);
@ -506,6 +434,57 @@ void report_exit(const struct arguments *args, const char *fmt, ...) {
(*args->env)->DeleteLocalRef(args->env, cls);
}
void check_allowed(const struct arguments *args) {
char source[INET6_ADDRSTRLEN + 1];
char dest[INET6_ADDRSTRLEN + 1];
struct udp_session *u = udp_session;
while (u != NULL) {
if (!u->stop) {
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), 1, "", u->uid, 0);
if (!is_address_allowed(args, objPacket)) {
u->stop = 1;
log_android(ANDROID_LOG_WARN, "UDP terminate %d uid %d", u->socket, u->uid);
}
}
u = u->next;
}
struct tcp_session *t = tcp_session;
while (t != NULL) {
if (t->state != TCP_TIME_WAIT && t->state != TCP_CLOSE) {
if (t->version == 4) {
inet_ntop(AF_INET, &t->saddr.ip4, source, sizeof(source));
inet_ntop(AF_INET, &t->daddr.ip4, dest, sizeof(dest));
}
else {
inet_ntop(AF_INET6, &t->saddr.ip6, source, sizeof(source));
inet_ntop(AF_INET6, &t->daddr.ip6, dest, sizeof(dest));
}
jobject objPacket = create_packet(
args, t->version, IPPROTO_TCP, "",
source, ntohs(t->source), dest, ntohs(t->dest), 1, "", t->uid, 0);
if (!is_address_allowed(args, objPacket)) {
t->state = TCP_TIME_WAIT;
log_android(ANDROID_LOG_WARN, "TCP terminate socket %d uid %d", t->socket, t->uid);
}
}
t = t->next;
}
}
void check_sessions(const struct arguments *args) {
time_t now = time(NULL);
@ -1038,7 +1017,7 @@ void handle_ip(const struct arguments *args, const uint8_t *pkt, const size_t le
// Get uid
jint uid = -1;
if ((protocol == IPPROTO_TCP && (!args->filter || syn)) || protocol == IPPROTO_UDP) {
if ((protocol == IPPROTO_TCP && syn) || protocol == IPPROTO_UDP) {
log_android(ANDROID_LOG_INFO, "get uid %s/%u syn %d", dest, dport, syn);
int tries = 0;
usleep(1000 * UID_DELAY);
@ -1082,48 +1061,25 @@ void handle_ip(const struct arguments *args, const uint8_t *pkt, const size_t le
#endif
// Check if allowed
jboolean allowed = 0;
if (args->filter) {
allowed = (jboolean) !syn;
if (syn) {
for (int i = 0; i < args->ucount; i++)
if (uid == args->uids[i]) {
allowed = 1;
break;
}
}
jboolean allowed = 1;
if (protocol != IPPROTO_TCP || syn) {
jobject objPacket = create_packet(args, version, protocol, flags,
source, sport, dest, dport, 1, "", uid, 0);
allowed = is_address_allowed(args, objPacket);
}
// Handle allowed traffic
int log = 0;
char extra[200];
*extra = 0;
if (allowed) {
if (protocol == IPPROTO_UDP) {
allowed = handle_udp(args, pkt, length, payload, uid, extra);
log = (!allowed || dport != 53);
} else if (protocol == IPPROTO_TCP) {
allowed = handle_tcp(args, pkt, length, payload, uid, extra) || !debug;
log = (!allowed || syn);
}
else {
allowed = 0;
log = 1;
}
if (protocol == IPPROTO_UDP)
handle_udp(args, pkt, length, payload, uid);
else if (protocol == IPPROTO_TCP)
handle_tcp(args, pkt, length, payload, uid);
}
else
log = 1;
// Log traffic
if (args->log && log)
log_packet(args, version, protocol, flags,
source, sport, dest, dport, 1, extra, uid, allowed);
}
jboolean handle_udp(const struct arguments *args,
const uint8_t *pkt, size_t length,
const uint8_t *payload,
int uid, char *extra) {
const uint8_t *pkt, size_t length, const uint8_t *payload,
int uid) {
// Get headers
const uint8_t version = (*pkt) >> 4;
const struct iphdr *ip4 = (struct iphdr *) pkt;
@ -1203,7 +1159,6 @@ jboolean handle_udp(const struct arguments *args,
uint16_t qclass;
if (get_dns(args, cur, data, datalen, &qtype, &qclass, qname) >= 0) {
log_android(ANDROID_LOG_INFO, "DNS type %d class %d name %s", qtype, qclass, qname);
sprintf(extra, "qtype %d qname %s", qtype, qname);
if (check_domain(args, cur, data, datalen, qclass, qtype, qname)) {
cur->stop = 1;
@ -1300,60 +1255,61 @@ int get_dns(const struct arguments *args, const struct udp_session *u,
int check_domain(const struct arguments *args, const struct udp_session *u,
const uint8_t *data, const size_t datalen,
uint16_t qclass, uint16_t qtype, const char *name) {
if (qclass == DNS_QCLASS_IN && (qtype == DNS_QTYPE_A || qtype == DNS_QTYPE_AAAA)) {
for (int i = 0; i < args->hcount; i++)
if (!strcmp(name, args->hosts[i])) {
log_android(ANDROID_LOG_WARN, "DNS type %d name %s blocked", qtype, name);
// Build response
size_t rlen = datalen + sizeof(struct dns_rr) + (qtype == DNS_QTYPE_A ? 4 : 16);
uint8_t *response = malloc(rlen);
if (qclass == DNS_QCLASS_IN &&
(qtype == DNS_QTYPE_A || qtype == DNS_QTYPE_AAAA) &&
is_domain_blocked(args, name)) {
// Copy header & query
memcpy(response, data, datalen);
log_android(ANDROID_LOG_WARN, "DNS type %d name %s blocked", qtype, name);
// Modify copied header
struct dns_header *rh = (struct dns_header *) response;
rh->qr = 1;
rh->aa = 0;
rh->tc = 0;
rh->rd = 0;
rh->ra = 0;
rh->z = 0;
rh->ad = 0;
rh->cd = 0;
rh->rcode = 0;
rh->ans_count = htons(1);
rh->auth_count = 0;
rh->add_count = 0;
// Build response
size_t rlen = datalen + sizeof(struct dns_rr) + (qtype == DNS_QTYPE_A ? 4 : 16);
uint8_t *response = malloc(rlen);
// Build answer
struct dns_rr *answer = (struct dns_rr *) (response + datalen);
answer->qname_ptr = htons(sizeof(struct dns_header) | 0xC000);
answer->qtype = htons(qtype);
answer->qclass = htons(qclass);
answer->ttl = htonl(DNS_TTL);
answer->rdlength = htons(qtype == DNS_QTYPE_A ? 4 : 16);
// Copy header & query
memcpy(response, data, datalen);
// Add answer address
uint8_t *addr = response + datalen + sizeof(struct dns_rr);
if (qtype == DNS_QTYPE_A)
inet_pton(AF_INET, "127.0.0.1", addr);
else
inet_pton(AF_INET6, "::1", addr);
// Modify copied header
struct dns_header *rh = (struct dns_header *) response;
rh->qr = 1;
rh->aa = 0;
rh->tc = 0;
rh->rd = 0;
rh->ra = 0;
rh->z = 0;
rh->ad = 0;
rh->cd = 0;
rh->rcode = 0;
rh->ans_count = htons(1);
rh->auth_count = 0;
rh->add_count = 0;
// Experiment
rlen = datalen;
rh->rcode = 3; // NXDOMAIN
rh->ans_count = 0;
// Build answer
struct dns_rr *answer = (struct dns_rr *) (response + datalen);
answer->qname_ptr = htons(sizeof(struct dns_header) | 0xC000);
answer->qtype = htons(qtype);
answer->qclass = htons(qclass);
answer->ttl = htonl(DNS_TTL);
answer->rdlength = htons(qtype == DNS_QTYPE_A ? 4 : 16);
// Send response
ssize_t res = write_udp(args, u, response, rlen);
// Add answer address
uint8_t *addr = response + datalen + sizeof(struct dns_rr);
if (qtype == DNS_QTYPE_A)
inet_pton(AF_INET, "127.0.0.1", addr);
else
inet_pton(AF_INET6, "::1", addr);
free(response);
// Experiment
rlen = datalen;
rh->rcode = 3; // NXDOMAIN
rh->ans_count = 0;
return 1;
}
// Send response
ssize_t res = write_udp(args, u, response, rlen);
free(response);
return 1;
}
return 0;
@ -1483,7 +1439,7 @@ int check_dhcp(const struct arguments *args, const struct udp_session *u,
jboolean handle_tcp(const struct arguments *args,
const uint8_t *pkt, size_t length,
const uint8_t *payload,
int uid, char *extra) {
int uid) {
#ifdef PROFILE
float mselapsed;
struct timeval start, end;
@ -2086,9 +2042,13 @@ ssize_t write_udp(const struct arguments *args, const struct udp_session *cur,
#endif
if (res >= 0) {
if (args->log && ntohs(cur->dest) != 53)
log_packet(args, cur->version, IPPROTO_UDP, "",
dest, ntohs(udp->dest), source, ntohs(udp->source), 0, "", cur->uid, 1);
if (ntohs(cur->dest) != 53) {
jobject objPacket = create_packet(
args, cur->version, IPPROTO_UDP, "",
dest, ntohs(udp->dest), source, ntohs(udp->source), 0, "", cur->uid, 1);
log_packet(args, objPacket);
}
// Write pcap record
if (pcap_file != NULL)
@ -2419,17 +2379,27 @@ jclass jniFindClass(JNIEnv *env, const char *name) {
jmethodID method_protect = NULL;
jmethodID method_logPacket = NULL;
jmethodID method_isDomainBlocked = NULL;
jmethodID method_isAddressAllowed = NULL;
jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *signature) {
if (strcmp(name, "protect") == 0 && method_protect != NULL)
return method_protect;
if (strcmp(name, "logPacket") == 0 && method_logPacket != NULL)
return method_logPacket;
if (strcmp(name, "isDomainBlocked") == 0 && method_isDomainBlocked != NULL)
return method_isDomainBlocked;
if (strcmp(name, "isAddressAllowed") == 0 && method_isAddressAllowed != NULL)
return method_isAddressAllowed;
jmethodID method = (*env)->GetMethodID(env, cls, name, signature);
if (method == NULL)
log_android(ANDROID_LOG_ERROR, "Method %s%s", name, signature);
else {
if (method == NULL) {
log_android(ANDROID_LOG_ERROR, "Method %s %s not found", name, signature);
jniCheckException(env);
} else {
if (strcmp(name, "protect") == 0) {
method_protect = method;
log_android(ANDROID_LOG_INFO, "Cached method ID protect");
@ -2438,11 +2408,20 @@ jmethodID jniGetMethodID(JNIEnv *env, jclass cls, const char *name, const char *
method_logPacket = method;
log_android(ANDROID_LOG_INFO, "Cached method ID logPacket");
}
else if (strcmp(name, "isDomainBlocked") == 0) {
method_isDomainBlocked = method;
log_android(ANDROID_LOG_INFO, "Cached method ID isDomainBlocked");
}
else if (strcmp(name, "isAddressAllowed") == 0) {
method_isAddressAllowed = method;
log_android(ANDROID_LOG_INFO, "Cached method ID isAddressAllowed");
}
}
return method;
}
jfieldID jniGetFieldID(JNIEnv *env, jclass cls, const char *name, const char *type) {
// TODO cache field IDs
jfieldID field = (*env)->GetFieldID(env, cls, name, type);
if (field == NULL)
log_android(ANDROID_LOG_ERROR, "Field %s type %s not found", name, type);
@ -2497,34 +2476,110 @@ void log_android(int prio, const char *fmt, ...) {
}
}
void log_packet(
const struct arguments *args,
jint version,
jint protocol,
const char *flags,
const char *source,
jint sport,
const char *dest,
jint dport,
jboolean outbound,
const char *data,
jint uid,
jboolean allowed) {
void log_packet(const struct arguments *args, jobject jpacket) {
#ifdef PROFILE
float mselapsed;
struct timeval start, end;
gettimeofday(&start, NULL);
#endif
JNIEnv *env = args->env;
jclass clsService = (*env)->GetObjectClass(env, args->instance);
jclass clsService = (*args->env)->GetObjectClass(args->env, args->instance);
const char *signature = "(Leu/faircode/netguard/Packet;)V";
jmethodID logPacket = jniGetMethodID(env, clsService, "logPacket", signature);
jmethodID method = jniGetMethodID(args->env, clsService, "logPacket", signature);
(*args->env)->CallVoidMethod(args->env, args->instance, method, jpacket);
jniCheckException(args->env);
(*args->env)->DeleteLocalRef(args->env, jpacket);
(*args->env)->DeleteLocalRef(args->env, clsService);
#ifdef PROFILE
gettimeofday(&end, NULL);
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
(end.tv_usec - start.tv_usec) / 1000.0;
if (mselapsed > 1)
log_android(ANDROID_LOG_INFO, "log_packet %f", mselapsed);
#endif
}
jboolean is_domain_blocked(const struct arguments *args, const char *name) {
#ifdef PROFILE
float mselapsed;
struct timeval start, end;
gettimeofday(&start, NULL);
#endif
jclass clsService = (*args->env)->GetObjectClass(args->env, args->instance);
const char *signature = "(Ljava/lang/String;)Z";
jmethodID method = jniGetMethodID(args->env, clsService, "isDomainBlocked", signature);
jstring jname = (*args->env)->NewStringUTF(args->env, name);
jboolean jallowed = (*args->env)->CallBooleanMethod(args->env, args->instance, method, jname);
jniCheckException(args->env);
(*args->env)->DeleteLocalRef(args->env, jname);
(*args->env)->DeleteLocalRef(args->env, clsService);
#ifdef PROFILE
gettimeofday(&end, NULL);
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
(end.tv_usec - start.tv_usec) / 1000.0;
if (mselapsed > 1)
log_android(ANDROID_LOG_INFO, "is_domain_blocked %f", mselapsed);
#endif
return jallowed;
}
jboolean is_address_allowed(const struct arguments *args, jobject jpacket) {
#ifdef PROFILE
float mselapsed;
struct timeval start, end;
gettimeofday(&start, NULL);
#endif
jclass clsService = (*args->env)->GetObjectClass(args->env, args->instance);
const char *signature = "(Leu/faircode/netguard/Packet;)Z";
jmethodID method = jniGetMethodID(args->env, clsService, "isAddressAllowed", signature);
jboolean jallowed = (*args->env)->CallBooleanMethod(args->env, args->instance, method, jpacket);
jniCheckException(args->env);
(*args->env)->DeleteLocalRef(args->env, jpacket);
(*args->env)->DeleteLocalRef(args->env, clsService);
#ifdef PROFILE
gettimeofday(&end, NULL);
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
(end.tv_usec - start.tv_usec) / 1000.0;
if (mselapsed > 1)
log_android(ANDROID_LOG_INFO, "is_address_allowed %f", mselapsed);
#endif
return jallowed;
}
jobject create_packet(const struct arguments *args,
jint version,
jint protocol,
const char *flags,
const char *source,
jint sport,
const char *dest,
jint dport,
jboolean outbound,
const char *data,
jint uid,
jboolean allowed) {
JNIEnv *env = args->env;
const char *packet = "eu/faircode/netguard/Packet";
jmethodID initPacket = jniGetMethodID(env, clsPacket, "<init>", "()V");
jobject objPacket = jniNewObject(env, clsPacket, initPacket, packet);
jobject jpacket = jniNewObject(env, clsPacket, initPacket, packet);
struct timeval tv;
gettimeofday(&tv, NULL);
@ -2535,37 +2590,26 @@ void log_packet(
jstring jdata = (*env)->NewStringUTF(env, data);
const char *string = "Ljava/lang/String;";
(*env)->SetLongField(env, objPacket, jniGetFieldID(env, clsPacket, "time", "J"), t);
(*env)->SetIntField(env, objPacket, jniGetFieldID(env, clsPacket, "version", "I"), version);
(*env)->SetIntField(env, objPacket, jniGetFieldID(env, clsPacket, "protocol", "I"), protocol);
(*env)->SetObjectField(env, objPacket, jniGetFieldID(env, clsPacket, "flags", string), jflags);
(*env)->SetObjectField(env, objPacket, jniGetFieldID(env, clsPacket, "saddr", string), jsource);
(*env)->SetIntField(env, objPacket, jniGetFieldID(env, clsPacket, "sport", "I"), sport);
(*env)->SetObjectField(env, objPacket, jniGetFieldID(env, clsPacket, "daddr", string), jdest);
(*env)->SetIntField(env, objPacket, jniGetFieldID(env, clsPacket, "dport", "I"), dport);
(*env)->SetBooleanField(env, objPacket, jniGetFieldID(env, clsPacket, "outbound", "Z"),
outbound);
(*env)->SetObjectField(env, objPacket, jniGetFieldID(env, clsPacket, "data", string), jdata);
(*env)->SetIntField(env, objPacket, jniGetFieldID(env, clsPacket, "uid", "I"), uid);
(*env)->SetBooleanField(env, objPacket, jniGetFieldID(env, clsPacket, "allowed", "Z"), allowed);
(*env)->CallVoidMethod(env, args->instance, logPacket, objPacket);
jniCheckException(env);
(*env)->SetLongField(env, jpacket, jniGetFieldID(env, clsPacket, "time", "J"), t);
(*env)->SetIntField(env, jpacket, jniGetFieldID(env, clsPacket, "version", "I"), version);
(*env)->SetIntField(env, jpacket, jniGetFieldID(env, clsPacket, "protocol", "I"), protocol);
(*env)->SetObjectField(env, jpacket, jniGetFieldID(env, clsPacket, "flags", string), jflags);
(*env)->SetObjectField(env, jpacket, jniGetFieldID(env, clsPacket, "saddr", string), jsource);
(*env)->SetIntField(env, jpacket, jniGetFieldID(env, clsPacket, "sport", "I"), sport);
(*env)->SetObjectField(env, jpacket, jniGetFieldID(env, clsPacket, "daddr", string), jdest);
(*env)->SetIntField(env, jpacket, jniGetFieldID(env, clsPacket, "dport", "I"), dport);
(*env)->SetBooleanField(env, jpacket, jniGetFieldID(env, clsPacket, "outbound", "Z"), outbound);
(*env)->SetObjectField(env, jpacket, jniGetFieldID(env, clsPacket, "data", string), jdata);
(*env)->SetIntField(env, jpacket, jniGetFieldID(env, clsPacket, "uid", "I"), uid);
(*env)->SetBooleanField(env, jpacket, jniGetFieldID(env, clsPacket, "allowed", "Z"), allowed);
(*env)->DeleteLocalRef(env, jdata);
(*env)->DeleteLocalRef(env, jdest);
(*env)->DeleteLocalRef(env, jsource);
(*env)->DeleteLocalRef(env, jflags);
(*env)->DeleteLocalRef(env, objPacket);
(*env)->DeleteLocalRef(env, clsService);
// Caller needs to delete reference to packet
#ifdef PROFILE
gettimeofday(&end, NULL);
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
(end.tv_usec - start.tv_usec) / 1000.0;
if (mselapsed > 1)
log_android(ANDROID_LOG_INFO, "log java %f", mselapsed);
#endif
return jpacket;
}
void write_pcap_hdr() {
@ -2645,46 +2689,6 @@ char *trim(char *str) {
return str;
}
void read_hosts(const char *name, struct arguments *args) {
log_android(ANDROID_LOG_INFO, "Reading %s", name);
args->hcount = 0;
args->hosts = NULL;
FILE *hosts;
if ((hosts = fopen(name, "r")) == NULL) {
log_android(ANDROID_LOG_ERROR, "fopen(%s) error %d: %s", name, errno, strerror(errno));
return;
}
char buffer[160];
while (fgets(buffer, sizeof(buffer), hosts)) {
char *hash = strchr(buffer, '#');
if (hash)
*hash = 0;
char *host = trim(buffer);
while (*host && !isspace(*host))
host++;
if (isspace(*host)) {
host++;
if (*host && strcmp(host, "localhost")) {
args->hosts = realloc(args->hosts, sizeof(char *) * (args->hcount + 1));
args->hosts[args->hcount] = malloc(strlen(host) + 1);
strcpy(args->hosts[args->hcount], host);
args->hcount++;
}
}
}
if (fclose(hosts))
log_android(ANDROID_LOG_ERROR, "fclose(%s) error %d: %s", name, errno, strerror(errno));
for (int i = 0; i < args->hcount; i++)
log_android(ANDROID_LOG_VERBOSE, "host '%s'", args->hosts[i]);
}
const char *strstate(const int state) {
switch (state) {
case TCP_ESTABLISHED:

View File

@ -28,12 +28,6 @@ struct arguments {
JNIEnv *env;
jobject instance;
int tun;
jint ucount;
jint *uids;
int hcount;
char **hosts;
jboolean log;
jboolean filter;
};
struct udp_session {
@ -203,8 +197,6 @@ typedef struct dhcp_option {
// Prototypes
void check_allowed(const struct arguments *args);
void clear_sessions();
void handle_signal(int sig, siginfo_t *info, void *context);
@ -213,6 +205,8 @@ void *handle_events(void *a);
void report_exit(const struct arguments *args, const char *fmt, ...);
void check_allowed(const struct arguments *args);
void check_sessions(const struct arguments *args);
int get_selects(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds);
@ -232,7 +226,7 @@ void handle_ip(const struct arguments *args, const uint8_t *buffer, size_t lengt
jboolean handle_udp(const struct arguments *args,
const uint8_t *pkt, size_t length,
const uint8_t *payload,
int uid, char *extra);
int uid);
int get_dns(const struct arguments *args, const struct udp_session *u,
const uint8_t *data, const size_t datalen,
@ -248,7 +242,7 @@ int check_dhcp(const struct arguments *args, const struct udp_session *u,
jboolean handle_tcp(const struct arguments *args,
const uint8_t *pkt, size_t length,
const uint8_t *payload,
int uid, char *extra);
int uid);
int open_udp_socket(const struct arguments *args, const struct udp_session *cur);
@ -301,18 +295,24 @@ int __system_property_get(const char *name, char *value);
void log_android(int prio, const char *fmt, ...);
void log_packet(const struct arguments *args,
jint version,
jint protocol,
const char *flags,
const char *source,
jint sport,
const char *dest,
jint dport,
jboolean outbound,
const char *data,
jint uid,
jboolean allowed);
void log_packet(const struct arguments *args, jobject jpacket);
jboolean is_domain_blocked(const struct arguments *args, const char *name);
jboolean is_address_allowed(const struct arguments *args, jobject objPacket);
jobject create_packet(const struct arguments *args,
jint version,
jint protocol,
const char *flags,
const char *source,
jint sport,
const char *dest,
jint dport,
jboolean outbound,
const char *data,
jint uid,
jboolean allowed);
void write_pcap_hdr();

View File

@ -174,13 +174,8 @@
<PreferenceCategory
android:key="category_development"
android:title="Development">
<SwitchPreference
android:defaultValue="false"
android:key="debug"
android:title="Debug" />
<ListPreference
android:defaultValue="4"
android:dependency="debug"
android:entries="@array/logLevelNames"
android:entryValues="@array/logLevelValues"
android:key="loglevel"