diff --git a/app/app.iml b/app/app.iml
index 6e13ec56..c8002dfb 100644
--- a/app/app.iml
+++ b/app/app.iml
@@ -98,6 +98,7 @@
+
diff --git a/app/src/main/java/eu/faircode/netguard/ActivityLog.java b/app/src/main/java/eu/faircode/netguard/ActivityLog.java
index d952cf5d..fa970072 100644
--- a/app/src/main/java/eu/faircode/netguard/ActivityLog.java
+++ b/app/src/main/java/eu/faircode/netguard/ActivityLog.java
@@ -292,6 +292,9 @@ public class ActivityLog extends AppCompatActivity {
OutputStream out = null;
FileInputStream in = null;
try {
+ // Stop capture
+ SinkholeService.setPcap(null);
+
Log.i(TAG, "Export PCAP URI=" + data.getData());
out = getContentResolver().openOutputStream(data.getData());
@@ -313,6 +316,13 @@ public class ActivityLog extends AppCompatActivity {
Util.sendCrashReport(ex, ActivityLog.this);
return ex;
} finally {
+ // Resume capture
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(ActivityLog.this);
+ if (prefs.getBoolean("pcap", false)) {
+ File pcap_file = new File(getCacheDir(), "netguard.pcap");
+ SinkholeService.setPcap(pcap_file);
+ }
+
if (out != null)
try {
out.close();
diff --git a/app/src/main/jni/netguard/netguard.c b/app/src/main/jni/netguard/netguard.c
index bbdb2035..7734ec45 100644
--- a/app/src/main/jni/netguard/netguard.c
+++ b/app/src/main/jni/netguard/netguard.c
@@ -55,6 +55,7 @@ int signaled = 0;
struct udp_session *udp_session = NULL;
struct tcp_session *tcp_session = NULL;
+
int loglevel = 0;
FILE *pcap_file = NULL;
@@ -608,7 +609,9 @@ void check_udp_sockets(const struct arguments *args, fd_set *rfds, fd_set *wfds,
inet_ntop(AF_INET, &(cur->daddr), dest, sizeof(dest));
log_android(ANDROID_LOG_INFO, "UDP recv bytes %d for %s/%u @tun",
bytes, dest, ntohs(cur->dest));
- write_udp(args, cur, buffer, bytes, args->tun);
+ if (write_udp(args, cur, buffer, bytes, args->tun) < 0)
+ log_android(ANDROID_LOG_ERROR, "write UDP error %d: %s",
+ errno, strerror((errno)));
}
}
}
@@ -928,60 +931,15 @@ jboolean handle_udp(const struct arguments *args, const uint8_t *buffer, uint16_
cur = cur->next;
}
+ char source[40];
+ char dest[40];
+ inet_ntop(version == 4 ? AF_INET : AF_INET6, &(iphdr->saddr), source, sizeof(source));
+ inet_ntop(version == 4 ? AF_INET : AF_INET6, &(iphdr->daddr), dest, sizeof(dest));
+
// Create new session if needed
if (cur == NULL) {
- log_android(ANDROID_LOG_INFO, "UDP new session");
-
- // Check for DNS
- if (ntohs(udphdr->dest) == 53 && datalen > sizeof(struct dns_header)) {
- char *h = hex(buffer + dataoff, datalen);
- log_android(ANDROID_LOG_DEBUG, "DNS off %d len %d hex %s", dataoff, datalen, h);
- free(h);
-
- struct dns_header *dns = (struct dns_header *) (buffer + dataoff);
- uint16_t flags = ntohs(dns->flags);
-
- // Check if standard query
- if ((flags & DNS_QR) == 0 && (flags & DNS_OPCODE) == 0 && dns->qdcount != 0) {
- char name[64];
- uint8_t noff = 0;
-
- // http://tools.ietf.org/html/rfc1035
- uint8_t len;
- uint16_t ptr;
- uint16_t qdoff = dataoff + sizeof(struct dns_header);
- do {
- len = *(buffer + qdoff);
- if (len < 63) {
- ptr = qdoff;
- qdoff += (1 + len);
- } else {
- // TODO check top 2 bits
- ptr = dataoff + (len & 63);
- len = *(buffer + ptr);
- qdoff += 1;
- }
- log_android(ANDROID_LOG_DEBUG, "DNS len %d ptr %d qdoff %d", len, ptr, qdoff);
- if (len && ptr + 1 + len <= dataoff + datalen) {
- memcpy(name + noff, buffer + ptr + 1, len);
- *(name + noff + len) = '.';
- noff += (len + 1);
- }
- } while (len && ptr + 1 + len <= dataoff + datalen);
-
- if (noff > 0 && qdoff + 4 <= dataoff + datalen) {
- *(name + noff - 1) = 0;
- uint16_t qtype = ntohs(*((uint16_t *) (buffer + qdoff)));
- uint16_t qclass = ntohs(*((uint16_t *) (buffer + qdoff + 2)));
- log_android(ANDROID_LOG_WARN, "DNS %s qtype %d qclass %d", name, qtype, qclass);
- for (int i = 0; i < args->hcount; i++)
- if (!strcmp(name, args->hosts[i])) {
- log_android(ANDROID_LOG_WARN, "DNS %s blocked", name);
- return 0;
- }
- }
- }
- }
+ log_android(ANDROID_LOG_WARN, "UDP new session from %s/%u to %s/%u",
+ source, ntohs(udphdr->source), dest, ntohs(udphdr->dest));
// Register session
struct udp_session *u = malloc(sizeof(struct udp_session));
@@ -1023,10 +981,9 @@ jboolean handle_udp(const struct arguments *args, const uint8_t *buffer, uint16_
}
}
- char source[40];
- char dest[40];
- inet_ntop(version == 4 ? AF_INET : AF_INET6, &(iphdr->saddr), source, sizeof(source));
- inet_ntop(version == 4 ? AF_INET : AF_INET6, &(iphdr->daddr), dest, sizeof(dest));
+ // Check for DNS
+ if (check_dns(args, cur, buffer, length))
+ return 0;
log_android(ANDROID_LOG_INFO, "UDP forward from tun %s/%u to %s/%u data %d",
source, ntohs(udphdr->source), dest, ntohs(udphdr->dest), datalen);
@@ -1048,6 +1005,90 @@ jboolean handle_udp(const struct arguments *args, const uint8_t *buffer, uint16_
return 1;
}
+int check_dns(const struct arguments *args, const struct udp_session *u,
+ const uint8_t *buffer, const uint16_t length) {
+ // Get headers
+ struct iphdr *iphdr = buffer;
+ uint8_t ipoptlen = (iphdr->ihl - 5) * 4;
+ struct udphdr *udphdr = buffer + sizeof(struct iphdr) + ipoptlen;
+
+ // Get data
+ uint16_t dataoff = sizeof(struct iphdr) + ipoptlen + sizeof(struct udphdr);
+ uint16_t datalen = length - dataoff;
+
+ if (ntohs(udphdr->dest) == 53 && datalen > sizeof(struct dns_header)) {
+ const struct dns_header *dns = (struct dns_header *) (buffer + dataoff);
+ uint16_t flags = ntohs(dns->flags);
+
+ // Check if standard query
+ if ((flags & DNS_QR) == 0 && (flags & DNS_OPCODE) == 0 && dns->qdcount != 0) {
+ char name[64];
+ uint8_t noff = 0;
+
+ // http://tools.ietf.org/html/rfc1035
+ uint8_t len;
+ uint16_t ptr;
+ uint16_t qdoff = dataoff + sizeof(struct dns_header);
+ do {
+ len = *(buffer + qdoff);
+ if (len <= 0x3F) {
+ ptr = qdoff;
+ qdoff += (1 + len);
+ } else {
+ // TODO check top 2 bits
+ ptr = dataoff + (len & 0x3F) * 256 + *(buffer + qdoff + 1);
+ len = *(buffer + ptr);
+ qdoff += 2;
+ }
+ if (len && ptr + 1 + len <= dataoff + datalen) {
+ memcpy(name + noff, buffer + ptr + 1, len);
+ *(name + noff + len) = '.';
+ noff += (len + 1);
+ }
+ } while (len && ptr + 1 + len <= dataoff + datalen);
+
+ if (noff > 0 && qdoff + 4 <= dataoff + datalen) {
+ *(name + noff - 1) = 0;
+ uint16_t qtype = ntohs(*((uint16_t *) (buffer + qdoff)));
+ uint16_t qclass = ntohs(*((uint16_t *) (buffer + qdoff + 2)));
+ qdoff += 4;
+
+ if (qtype == DNS_QTYPE_A && qclass == DNS_QCLASS_IN)
+ for (int i = 0; i < args->hcount; i++)
+ if (!strcmp(name, args->hosts[i])) {
+ log_android(ANDROID_LOG_WARN, "DNS %s blocked", name);
+
+ struct dns_response reply;
+ reply.qname_ptr = htons(sizeof(struct dns_header) | 0xC000);
+ reply.qtype = htons(qtype);
+ reply.qclass = htons(qclass);
+ reply.ttl = htonl(DNS_TTL); // seconds
+ reply.rdlength = htons(sizeof(reply.rdata));
+ inet_aton("127.0.0.1", &(reply.rdata));
+
+ uint16_t qsize = qdoff - dataoff;
+ uint16_t rsize = qsize + sizeof(struct dns_response);
+ uint8_t *response = malloc(rsize);
+ memcpy(response, buffer + dataoff, qsize);
+ memcpy(response + qsize, &reply, sizeof(struct dns_response));
+
+ struct dns_header *rh = response;
+ rh->flags = htons(DNS_QR);
+ rh->ancount = htons(1);
+
+ if (write_udp(args, u, response, rsize, args->tun) < 0)
+ log_android(ANDROID_LOG_ERROR, "write UDP error %d: %s",
+ errno, strerror((errno)));
+
+ return 1;
+ }
+ }
+ }
+ }
+
+ return 0;
+}
+
jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, uint16_t length, int uid) {
#ifdef PROFILE
float mselapsed;
@@ -1555,8 +1596,9 @@ int write_udp(const struct arguments *args, const struct udp_session *cur,
log_android(ANDROID_LOG_INFO, "tun UDP write %f", mselapsed);
#endif
- log_packet(args, cur->version, ip->protocol, "",
- source, ntohs(udp->source), dest, ntohs(udp->dest), cur->uid, 1);
+ if (args->log)
+ log_packet(args, cur->version, ip->protocol, "",
+ source, ntohs(udp->source), dest, ntohs(udp->dest), cur->uid, 1);
// Write pcap record
if (pcap_file != NULL)
@@ -2063,7 +2105,7 @@ void read_hosts(const char *name, struct arguments *args) {
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_WARN, "host '%s'", args->hosts[i]);
+ log_android(ANDROID_LOG_DEBUG, "host '%s'", args->hosts[i]);
}
const char *strstate(const int state) {
@@ -2108,5 +2150,7 @@ char *hex(const u_int8_t *data, const u_int16_t len) {
hexout[i * 3 + 1] = hex_str[(data[i]) & 0x0F];
hexout[i * 3 + 2] = ' ';
}
+ hexout[len * 3] = 0;
+
return hexout;
}
diff --git a/app/src/main/jni/netguard/netguard.h b/app/src/main/jni/netguard/netguard.h
index be578fdc..8b07bf8d 100644
--- a/app/src/main/jni/netguard/netguard.h
+++ b/app/src/main/jni/netguard/netguard.h
@@ -23,7 +23,7 @@
#define UID_MAXTRY 3
#define MAX_PCAP_FILE (1024 * 1024) // bytes
-#define MAX_PCAP_RECORD 80 // bytes
+#define MAX_PCAP_RECORD 128 // bytes
struct arguments {
JNIEnv *env;
@@ -72,11 +72,11 @@ struct tcp_session {
// https://wiki.wireshark.org/Development/LibpcapFileFormat
-typedef unsigned short guint16_t;
-typedef unsigned int guint32_t;
-typedef signed int gint32_t;
+typedef uint16_t guint16_t;
+typedef uint32_t guint32_t;
+typedef int32_t gint32_t;
-typedef struct pcap_hdr_s {
+typedef struct __attribute__((__packed__)) pcap_hdr_s {
guint32_t magic_number;
guint16_t version_major;
guint16_t version_minor;
@@ -86,8 +86,7 @@ typedef struct pcap_hdr_s {
guint32_t network;
} pcap_hdr_t;
-
-typedef struct pcaprec_hdr_s {
+typedef struct __attribute__((__packed__)) pcaprec_hdr_s {
guint32_t ts_sec;
guint32_t ts_usec;
guint32_t incl_len;
@@ -96,7 +95,7 @@ typedef struct pcaprec_hdr_s {
#define LINKTYPE_RAW 101
-typedef struct dns_header {
+typedef struct __attribute__((__packed__)) dns_header {
__be16 msgid;
__be16 flags;
__be16 qdcount;
@@ -105,6 +104,15 @@ typedef struct dns_header {
__be16 arcount;
} dns_header_t;
+typedef struct __attribute__((__packed__)) dns_response {
+ __be16 qname_ptr;
+ __be16 qtype;
+ __be16 qclass;
+ __be32 ttl;
+ __be16 rdlength;
+ __be32 rdata;
+} dns_response_t;
+
#define DNS_QR 1
#define DNS_OPCODE 30
#define DNS_TC 8
@@ -113,6 +121,8 @@ typedef struct dns_header {
#define DNS_QTYPE_AAAA 28 // IPv6
#define DNS_QCLASS_IN 1
+#define DNS_TTL 3600 // seconds
+
void clear_sessions();
void handle_signal(int sig, siginfo_t *info, void *context);
@@ -137,6 +147,9 @@ jboolean handle_udp(const struct arguments *args, const uint8_t *buffer, uint16_
jboolean handle_tcp(const struct arguments *args, const uint8_t *buffer, uint16_t length, int uid);
+int check_dns(const struct arguments *args, const struct udp_session *u,
+ const uint8_t *buffer, const uint16_t length);
+
int open_socket(const struct tcp_session *cur, const struct arguments *args);
uint16_t get_local_port(const int sock);