1
0
Fork 0
mirror of https://github.com/M66B/NetGuard.git synced 2025-01-01 12:54:07 +00:00

Native DNS domain blocking

This commit is contained in:
M66B 2016-01-23 15:50:18 +01:00
parent 0b03b6c443
commit 705811f01f
4 changed files with 136 additions and 68 deletions

View file

@ -98,6 +98,7 @@
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/manifests" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/objectFiles" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/pre-dexed" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/proguard-rules" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/res" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/rs" />
<excludeFolder url="file://$MODULE_DIR$/build/intermediates/symbols" />

View file

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

View file

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

View file

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