mirror of
https://github.com/M66B/NetGuard.git
synced 2025-01-03 13:54:09 +00:00
Send resource records to Java
This commit is contained in:
parent
1d2a6baaa1
commit
55042bedd3
5 changed files with 146 additions and 107 deletions
6
app/proguard-rules.pro
vendored
6
app/proguard-rules.pro
vendored
|
@ -23,11 +23,17 @@
|
|||
#NetGuard
|
||||
-keepnames class eu.faircode.netguard.** { *; }
|
||||
|
||||
#JNI
|
||||
-keepclasseswithmembernames class * {
|
||||
native <methods>;
|
||||
}
|
||||
|
||||
#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);
|
||||
void dnsResolved(eu.faircode.netguard.ResourceRecord);
|
||||
boolean isDomainBlocked(java.lang.String);
|
||||
boolean isAddressAllowed(eu.faircode.netguard.Packet);
|
||||
}
|
||||
|
|
35
app/src/main/java/eu/faircode/netguard/ResourceRecord.java
Normal file
35
app/src/main/java/eu/faircode/netguard/ResourceRecord.java
Normal file
|
@ -0,0 +1,35 @@
|
|||
package eu.faircode.netguard;
|
||||
|
||||
/*
|
||||
This file is part of NetGuard.
|
||||
|
||||
NetGuard is free software: you can redistribute it and/or modify
|
||||
it under the terms of the GNU General Public License as published by
|
||||
the Free Software Foundation, either version 3 of the License, or
|
||||
(at your option) any later version.
|
||||
|
||||
NetGuard is distributed in the hope that it will be useful,
|
||||
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
GNU General Public License for more details.
|
||||
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with NetGuard. If not, see <http://www.gnu.org/licenses/>.
|
||||
|
||||
Copyright 2015-2016 by Marcel Bokhorst (M66B)
|
||||
*/
|
||||
|
||||
public class ResourceRecord {
|
||||
public String QName;
|
||||
public String AName;
|
||||
public String Resource;
|
||||
public int TTL;
|
||||
|
||||
public ResourceRecord() {
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "Q " + QName + " A " + AName + " R " + Resource + " TTL " + TTL;
|
||||
}
|
||||
}
|
|
@ -930,6 +930,11 @@ public class SinkholeService extends VpnService implements SharedPreferences.OnS
|
|||
}
|
||||
}
|
||||
|
||||
// Called from native code
|
||||
private void dnsResolved(ResourceRecord rr) {
|
||||
Log.i(TAG, rr.toString());
|
||||
}
|
||||
|
||||
// Called from native code
|
||||
private boolean isDomainBlocked(String name) {
|
||||
boolean blocked = (mapDomainBlocked.containsKey(name) && mapDomainBlocked.get(name));
|
||||
|
|
|
@ -62,6 +62,7 @@ static FILE *pcap_file = NULL;
|
|||
// JNI
|
||||
|
||||
jclass clsPacket;
|
||||
jclass clsRR;
|
||||
|
||||
jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
||||
log_android(ANDROID_LOG_INFO, "JNI load");
|
||||
|
@ -74,6 +75,8 @@ jint JNI_OnLoad(JavaVM *vm, void *reserved) {
|
|||
|
||||
const char *packet = "eu/faircode/netguard/Packet";
|
||||
clsPacket = jniGlobalRef(env, jniFindClass(env, packet));
|
||||
const char *rr = "eu/faircode/netguard/ResourceRecord";
|
||||
clsRR = jniGlobalRef(env, jniFindClass(env, rr));
|
||||
|
||||
return JNI_VERSION_1_6;
|
||||
}
|
||||
|
@ -86,6 +89,7 @@ void JNI_OnUnload(JavaVM *vm, void *reserved) {
|
|||
log_android(ANDROID_LOG_INFO, "JNI load GetEnv failed");
|
||||
else {
|
||||
(*env)->DeleteGlobalRef(env, clsPacket);
|
||||
(*env)->DeleteGlobalRef(env, clsRR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -793,8 +797,6 @@ int get_qname(const uint8_t *data, const size_t datalen, uint16_t off, char *qna
|
|||
return (c ? off : ptr);
|
||||
}
|
||||
|
||||
static struct dns_entry *dns_entry = NULL;
|
||||
|
||||
void parse_dns_response(const struct arguments *args, const uint8_t *data, const size_t datalen) {
|
||||
if (datalen < sizeof(struct dns_header) + 1) {
|
||||
log_android(ANDROID_LOG_WARN, "DNS response length %d", datalen);
|
||||
|
@ -807,33 +809,31 @@ void parse_dns_response(const struct arguments *args, const uint8_t *data, const
|
|||
int qcount = ntohs(dns->q_count);
|
||||
int acount = ntohs(dns->ans_count);
|
||||
if (dns->qr == 1 && dns->opcode == 0 && qcount > 0 && acount > 0) {
|
||||
log_android(ANDROID_LOG_DEBUG, "DNS response qcount %d acount %d", qcount, acount);
|
||||
if (qcount > 1)
|
||||
log_android(ANDROID_LOG_WARN, "DNS response qcount %d acount %d", qcount, acount);
|
||||
|
||||
// http://tools.ietf.org/html/rfc1035
|
||||
|
||||
struct dns_entry *entry = malloc(sizeof(struct dns_entry));
|
||||
entry->acount = 0;
|
||||
entry->answer = NULL;
|
||||
|
||||
char qname[DNS_QNAME_MAX + 1];
|
||||
|
||||
char name[DNS_QNAME_MAX + 1];
|
||||
uint16_t off = sizeof(struct dns_header);
|
||||
for (int q = 0; q < qcount; q++) {
|
||||
off = get_qname(data, datalen, off, qname);
|
||||
off = get_qname(data, datalen, off, name);
|
||||
if (off > 0 && off + 4 <= datalen) {
|
||||
uint16_t qtype = ntohs(*((uint16_t *) (data + off)));
|
||||
uint16_t qclass = ntohs(*((uint16_t *) (data + off + 2)));
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"DNS question %d qtype %d qclass %d qname %s",
|
||||
q, qtype, qclass, qname);
|
||||
q, qtype, qclass, name);
|
||||
off += 4;
|
||||
|
||||
// TODO multiple qnames?
|
||||
if (q == 0)
|
||||
entry->qname = malloc(strlen(qname) + 1);
|
||||
strcpy(entry->qname, qname);
|
||||
strcpy(qname, name);
|
||||
}
|
||||
else {
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"DNS response Q invalid off %d datalen %d",
|
||||
off, datalen);
|
||||
return;
|
||||
|
@ -841,7 +841,7 @@ void parse_dns_response(const struct arguments *args, const uint8_t *data, const
|
|||
}
|
||||
|
||||
for (int a = 0; a < acount; a++) {
|
||||
off = get_qname(data, datalen, off, qname);
|
||||
off = get_qname(data, datalen, off, name);
|
||||
if (off > 0 && off + 10 <= datalen) {
|
||||
uint16_t qtype = ntohs(*((uint16_t *) (data + off)));
|
||||
uint16_t qclass = ntohs(*((uint16_t *) (data + off + 2)));
|
||||
|
@ -851,101 +851,47 @@ void parse_dns_response(const struct arguments *args, const uint8_t *data, const
|
|||
|
||||
if (off + rdlength <= datalen) {
|
||||
if (qclass == DNS_QCLASS_IN &&
|
||||
(qtype == DNS_QTYPE_A ||
|
||||
qtype == DNS_QTYPE_AAAA ||
|
||||
qtype == DNS_QTYPE_CNAME)) {
|
||||
(qtype == DNS_QTYPE_A || qtype == DNS_QTYPE_AAAA)) {
|
||||
|
||||
char rd[INET6_ADDRSTRLEN + 1];
|
||||
if (qtype == DNS_QTYPE_A)
|
||||
inet_ntop(AF_INET, data + off, rd, sizeof(rd));
|
||||
else if (qclass == DNS_QCLASS_IN && qtype == DNS_QTYPE_AAAA)
|
||||
inet_ntop(AF_INET6, data + off, rd, sizeof(rd));
|
||||
else if (qclass == DNS_QCLASS_IN && qtype == DNS_QTYPE_CNAME) {
|
||||
if (get_qname(data, datalen, off, rd) < 0)
|
||||
log_android(ANDROID_LOG_WARN, "DNS cname error");
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
dns_resolved(args, qname, name, rd, ttl);
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"DNS answer %d qname %s qtype %d ttl %d data %s",
|
||||
a, qname, qtype, ttl, rd);
|
||||
|
||||
entry->answer = realloc(entry->answer,
|
||||
sizeof(struct dns_answer *) * (entry->acount + 1));
|
||||
entry->answer[entry->acount] = malloc(sizeof(struct dns_answer));
|
||||
entry->answer[entry->acount]->time = time(NULL);
|
||||
entry->answer[entry->acount]->ttl = ttl;
|
||||
entry->answer[entry->acount]->qtype = qtype;
|
||||
entry->answer[entry->acount]->rd = malloc(strlen(rd) + 1);
|
||||
strcpy(entry->answer[entry->acount]->rd, rd);
|
||||
entry->acount++;
|
||||
a, name, qtype, ttl, rd);
|
||||
|
||||
} else
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"DNS answer %d qname %s qclass %d qtype %d ttl %d length %d",
|
||||
a, qname, qclass, qtype, ttl, rdlength);
|
||||
|
||||
a, name, qclass, qtype, ttl, rdlength);
|
||||
|
||||
off += rdlength;
|
||||
}
|
||||
else {
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"DNS response A invalid off %d rdlength %d datalen %d",
|
||||
off, rdlength, datalen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
else {
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"DNS response A invalid off %d datalen %d",
|
||||
off, datalen);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (entry->acount > 0) {
|
||||
// TODO cleanup old entries
|
||||
entry->next = dns_entry;
|
||||
dns_entry = entry;
|
||||
}
|
||||
else
|
||||
free(entry);
|
||||
}
|
||||
else
|
||||
log_android(ANDROID_LOG_INFO,
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
"DNS response qr %d opcode %d qcount %d acount %d",
|
||||
dns->qr, dns->opcode, qcount, acount);
|
||||
}
|
||||
|
||||
JNIEXPORT jstring JNICALL
|
||||
Java_eu_faircode_netguard_Util_jni_1resolve(JNIEnv *env, jclass type, jstring ip_) {
|
||||
const char *ip = (*env)->GetStringUTFChars(env, ip_, 0);
|
||||
|
||||
if (pthread_mutex_lock(&lock))
|
||||
log_android(ANDROID_LOG_ERROR, "pthread_mutex_lock failed");
|
||||
|
||||
char *result = NULL;
|
||||
struct dns_entry *entry = dns_entry;
|
||||
while (entry != NULL) {
|
||||
for (int a = 0; a < entry->acount; a++)
|
||||
// TODO check ttl
|
||||
if (entry->answer[a]->qtype == DNS_QTYPE_CNAME) {
|
||||
// TODO cname recursion
|
||||
}
|
||||
else if (!strcmp(entry->answer[a]->rd, ip)) {
|
||||
result = entry->qname;
|
||||
break;
|
||||
}
|
||||
entry = entry->next;
|
||||
}
|
||||
|
||||
if (pthread_mutex_unlock(&lock))
|
||||
log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed");
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, ip_, ip);
|
||||
|
||||
return (result == NULL ? NULL : (*env)->NewStringUTF(env, result));
|
||||
}
|
||||
|
||||
void check_tcp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds, fd_set *efds) {
|
||||
struct tcp_session *cur = tcp_session;
|
||||
while (cur != NULL) {
|
||||
|
@ -1387,7 +1333,7 @@ jboolean handle_udp(const struct arguments *args,
|
|||
uint16_t qtype;
|
||||
uint16_t qclass;
|
||||
if (get_dns_query(args, cur, data, datalen, &qtype, &qclass, qname) >= 0) {
|
||||
log_android(ANDROID_LOG_WARN,
|
||||
log_android(ANDROID_LOG_DEBUG,
|
||||
"DNS query qtype %d qclass %d name %s",
|
||||
qtype, qclass, qname);
|
||||
|
||||
|
@ -2668,6 +2614,68 @@ void log_packet(const struct arguments *args, jobject jpacket) {
|
|||
#endif
|
||||
}
|
||||
|
||||
static jmethodID midDnsResolved = NULL;
|
||||
static jmethodID midInitRR = NULL;
|
||||
jfieldID fidQName = NULL;
|
||||
jfieldID fidAName = NULL;
|
||||
jfieldID fidResource = NULL;
|
||||
jfieldID fidTTL = NULL;
|
||||
|
||||
void dns_resolved(const struct arguments *args,
|
||||
const char *qname, const char *aname, const char *resource, int ttl) {
|
||||
#ifdef PROFILE_JNI
|
||||
float mselapsed;
|
||||
struct timeval start, end;
|
||||
gettimeofday(&start, NULL);
|
||||
#endif
|
||||
|
||||
jclass clsService = (*args->env)->GetObjectClass(args->env, args->instance);
|
||||
|
||||
const char *signature = "(Leu/faircode/netguard/ResourceRecord;)V";
|
||||
if (midDnsResolved == NULL)
|
||||
midDnsResolved = jniGetMethodID(args->env, clsService, "dnsResolved", signature);
|
||||
|
||||
const char *rr = "eu/faircode/netguard/ResourceRecord";
|
||||
if (midInitRR == NULL)
|
||||
midInitRR = jniGetMethodID(args->env, clsRR, "<init>", "()V");
|
||||
|
||||
jobject jrr = jniNewObject(args->env, clsRR, midInitRR, rr);
|
||||
|
||||
if (fidQName == NULL) {
|
||||
const char *string = "Ljava/lang/String;";
|
||||
fidQName = jniGetFieldID(args->env, clsRR, "QName", string);
|
||||
fidAName = jniGetFieldID(args->env, clsRR, "AName", string);
|
||||
fidResource = jniGetFieldID(args->env, clsRR, "Resource", string);
|
||||
fidTTL = jniGetFieldID(args->env, clsRR, "TTL", "I");
|
||||
}
|
||||
|
||||
jstring jqname = (*args->env)->NewStringUTF(args->env, qname);
|
||||
jstring janame = (*args->env)->NewStringUTF(args->env, aname);
|
||||
jstring jresource = (*args->env)->NewStringUTF(args->env, resource);
|
||||
|
||||
(*args->env)->SetObjectField(args->env, jrr, fidQName, jqname);
|
||||
(*args->env)->SetObjectField(args->env, jrr, fidAName, janame);
|
||||
(*args->env)->SetObjectField(args->env, jrr, fidResource, jresource);
|
||||
(*args->env)->SetIntField(args->env, jrr, fidTTL, ttl);
|
||||
|
||||
(*args->env)->CallVoidMethod(args->env, args->instance, midDnsResolved, jrr);
|
||||
jniCheckException(args->env);
|
||||
|
||||
(*args->env)->DeleteLocalRef(args->env, jresource);
|
||||
(*args->env)->DeleteLocalRef(args->env, janame);
|
||||
(*args->env)->DeleteLocalRef(args->env, jqname);
|
||||
(*args->env)->DeleteLocalRef(args->env, jrr);
|
||||
(*args->env)->DeleteLocalRef(args->env, clsService);
|
||||
|
||||
#ifdef PROFILE_JNI
|
||||
gettimeofday(&end, NULL);
|
||||
mselapsed = (end.tv_sec - start.tv_sec) * 1000.0 +
|
||||
(end.tv_usec - start.tv_usec) / 1000.0;
|
||||
if (mselapsed > PROFILE_JNI)
|
||||
log_android(ANDROID_LOG_WARN, "log_packet %f", mselapsed);
|
||||
#endif
|
||||
}
|
||||
|
||||
static jmethodID midIsDomainBlocked = NULL;
|
||||
|
||||
jboolean is_domain_blocked(const struct arguments *args, const char *name) {
|
||||
|
@ -2681,8 +2689,7 @@ jboolean is_domain_blocked(const struct arguments *args, const char *name) {
|
|||
|
||||
const char *signature = "(Ljava/lang/String;)Z";
|
||||
if (midIsDomainBlocked == NULL)
|
||||
midIsDomainBlocked = jniGetMethodID(args->env, clsService,
|
||||
"isDomainBlocked", signature);
|
||||
midIsDomainBlocked = jniGetMethodID(args->env, clsService, "isDomainBlocked", signature);
|
||||
|
||||
jstring jname = (*args->env)->NewStringUTF(args->env, name);
|
||||
|
||||
|
@ -2717,8 +2724,7 @@ jboolean is_address_allowed(const struct arguments *args, jobject jpacket) {
|
|||
|
||||
const char *signature = "(Leu/faircode/netguard/Packet;)Z";
|
||||
if (midIsAddressAllowed == NULL)
|
||||
midIsAddressAllowed = jniGetMethodID(args->env, clsService,
|
||||
"isAddressAllowed", signature);
|
||||
midIsAddressAllowed = jniGetMethodID(args->env, clsService, "isAddressAllowed", signature);
|
||||
|
||||
jboolean jallowed = (*args->env)->CallBooleanMethod(
|
||||
args->env, args->instance, midIsAddressAllowed, jpacket);
|
||||
|
@ -2776,14 +2782,6 @@ jobject create_packet(const struct arguments *args,
|
|||
midInitPacket = jniGetMethodID(env, clsPacket, "<init>", "()V");
|
||||
jobject jpacket = jniNewObject(env, clsPacket, midInitPacket, packet);
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
jlong t = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
|
||||
jstring jflags = (*env)->NewStringUTF(env, flags);
|
||||
jstring jsource = (*env)->NewStringUTF(env, source);
|
||||
jstring jdest = (*env)->NewStringUTF(env, dest);
|
||||
jstring jdata = (*env)->NewStringUTF(env, data);
|
||||
|
||||
if (fidTime == NULL) {
|
||||
const char *string = "Ljava/lang/String;";
|
||||
fidTime = jniGetFieldID(env, clsPacket, "time", "J");
|
||||
|
@ -2799,6 +2797,14 @@ jobject create_packet(const struct arguments *args,
|
|||
fidAllowed = jniGetFieldID(env, clsPacket, "allowed", "Z");
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
gettimeofday(&tv, NULL);
|
||||
jlong t = tv.tv_sec * 1000LL + tv.tv_usec / 1000;
|
||||
jstring jflags = (*env)->NewStringUTF(env, flags);
|
||||
jstring jsource = (*env)->NewStringUTF(env, source);
|
||||
jstring jdest = (*env)->NewStringUTF(env, dest);
|
||||
jstring jdata = (*env)->NewStringUTF(env, data);
|
||||
|
||||
(*env)->SetLongField(env, jpacket, fidTime, t);
|
||||
(*env)->SetIntField(env, jpacket, fidVersion, version);
|
||||
(*env)->SetIntField(env, jpacket, fidProtocol, protocol);
|
||||
|
|
|
@ -122,7 +122,6 @@ typedef struct pcaprec_hdr_s {
|
|||
|
||||
#define DNS_QCLASS_IN 1
|
||||
#define DNS_QTYPE_A 1 // IPv4
|
||||
#define DNS_QTYPE_CNAME 5
|
||||
#define DNS_QTYPE_AAAA 28 // IPv6
|
||||
|
||||
#define DNS_QNAME_MAX 63
|
||||
|
@ -169,21 +168,6 @@ typedef struct dns_rr {
|
|||
__be16 rdlength;
|
||||
} __packed;
|
||||
|
||||
typedef struct dns_answer {
|
||||
int time;
|
||||
int ttl;
|
||||
char *qname;
|
||||
int qtype;
|
||||
char *rd;
|
||||
};
|
||||
|
||||
typedef struct dns_entry {
|
||||
char *qname;
|
||||
int acount;
|
||||
struct dns_answer **answer;
|
||||
struct dns_entry *next;
|
||||
};
|
||||
|
||||
// DHCP
|
||||
|
||||
#define DHCP_OPTION_MAGIC_NUMBER (0x63825363)
|
||||
|
@ -321,6 +305,9 @@ void log_android(int prio, const char *fmt, ...);
|
|||
|
||||
void log_packet(const struct arguments *args, jobject jpacket);
|
||||
|
||||
void dns_resolved(const struct arguments *args,
|
||||
const char *qname, const char *aname, const char *resource, int ttl);
|
||||
|
||||
jboolean is_domain_blocked(const struct arguments *args, const char *name);
|
||||
|
||||
jboolean is_address_allowed(const struct arguments *args, jobject objPacket);
|
||||
|
|
Loading…
Reference in a new issue