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:
parent
0b03b6c443
commit
705811f01f
4 changed files with 136 additions and 68 deletions
|
@ -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" />
|
||||
|
|
|
@ -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();
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
Loading…
Reference in a new issue