Send resource records to Java

This commit is contained in:
M66B 2016-01-30 09:51:41 +01:00
parent 1d2a6baaa1
commit 55042bedd3
5 changed files with 146 additions and 107 deletions

View File

@ -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);
}

View 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;
}
}

View File

@ -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));

View File

@ -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_WARN, "DNS response qcount %d acount %d", qcount, acount);
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);

View File

@ -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);