mirror of https://github.com/M66B/NetGuard.git
Native rewrite address/domain check
This commit is contained in:
parent
db5f58aa70
commit
e16c4c4af2
|
@ -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
|
||||
|
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
@ -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) {
|
||||
|
|
|
@ -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:
|
||||
|
|
|
@ -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();
|
||||
|
||||
|
|
|
@ -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"
|
||||
|
|
Loading…
Reference in New Issue