mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-05-08 21:12:26 +00:00
Spoof DNS replies for blocked hosts
This prevents the DNS resolver from spamming multiple requests when no reply is seen.
This commit is contained in:
@@ -212,3 +212,38 @@ char* humanSize(char *buf, int bufsize, double bytes) {
|
||||
snprintf(buf, bufsize, "%.02f %s", bytes, suffix[i]);
|
||||
return buf;
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
/* Dumps packets in the hex format of "od -A x -t x1", which makes it compatible with
|
||||
* text2pcap. */
|
||||
void hexdump(const char *buf, size_t bufsize) {
|
||||
size_t off = 0;
|
||||
char out[64];
|
||||
int idx = 0;
|
||||
static const char hex[] = "0123456789abcdef";
|
||||
|
||||
while(off < bufsize) {
|
||||
if((off % 16) == 0) {
|
||||
if(off > 0) {
|
||||
out[idx] = '\0';
|
||||
log_d("%s", out);
|
||||
}
|
||||
idx = sprintf(out, "%06x", off);
|
||||
}
|
||||
|
||||
out[idx++] = ' ';
|
||||
out[idx++] = hex[(buf[off] & 0xF0) >> 4];
|
||||
out[idx++] = hex[buf[off] & 0x0F];
|
||||
off++;
|
||||
}
|
||||
|
||||
if((off % 16) != 0) {
|
||||
out[idx] = '\0';
|
||||
log_d("%s", out);
|
||||
idx = sprintf(out, "%06x", off);
|
||||
}
|
||||
|
||||
out[idx] = '\0';
|
||||
log_d("%s", out);
|
||||
}
|
||||
@@ -49,5 +49,6 @@ jfieldID jniFieldID(JNIEnv *env, jclass cls, const char *name, const char *type)
|
||||
jobject jniEnumVal(JNIEnv *env, const char *class_name, const char *enum_key);
|
||||
int jniCheckException(JNIEnv *env);
|
||||
char* humanSize(char *buf, int bufsize, double bytes);
|
||||
void hexdump(const char *buf, size_t bufsize);
|
||||
|
||||
#endif // __LOG_UTILS_H__
|
||||
|
||||
@@ -242,6 +242,82 @@ static bool check_dns_req_allowed(pcapdroid_t *pd, zdtun_conn_t *conn, pkt_conte
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static bool spoof_dns_reply(pcapdroid_t *pd, zdtun_conn_t *conn, pkt_context_t *pctx) {
|
||||
// Step 1: ensure that this is a valid query
|
||||
zdtun_pkt_t *pkt = pctx->pkt;
|
||||
if(pkt->l7_len < (sizeof(dns_packet_t) + 5))
|
||||
return false;
|
||||
|
||||
dns_packet_t *req = (dns_packet_t*) pkt->l7;
|
||||
if(ntohs(req->questions) != 1)
|
||||
return false;
|
||||
|
||||
int remaining = pkt->l7_len - sizeof(dns_packet_t);
|
||||
int qlen=0;
|
||||
while(remaining >= 5) {
|
||||
if(!req->queries[qlen])
|
||||
break;
|
||||
qlen++;
|
||||
remaining--;
|
||||
}
|
||||
|
||||
if((req->queries[qlen] != 0) || (req->queries[qlen + 1] != 0) ||
|
||||
(req->queries[qlen + 3] != 0) || (req->queries[qlen + 4] != 1))
|
||||
return false; // invalid
|
||||
|
||||
uint8_t qtype = req->queries[qlen + 2];
|
||||
if((qtype != 0x01) && (qtype != 0x1c))
|
||||
return false; // invalid query type
|
||||
|
||||
// Step 2: spoof the reply
|
||||
log_d("Spoofing %s DNS reply", (qtype == 0x01) ? "A" : "AAAA");
|
||||
|
||||
const zdtun_5tuple_t *tuple = pctx->tuple;
|
||||
uint8_t alen = (qtype == 0x01) ? 4 : 16;
|
||||
int iplen = zdtun_iphdr_len(pd->zdt, conn);
|
||||
unsigned int len = iplen + 8 /* UDP */ + sizeof(dns_packet_t) + qlen + 5 /* type, ... */ + 12 /* answer */ + alen;
|
||||
char buf[len];
|
||||
memset(buf, 0, len);
|
||||
|
||||
zdtun_make_iphdr(pd->zdt, conn, buf, len - iplen);
|
||||
|
||||
struct udphdr *udp = (struct udphdr*)(buf + iplen);
|
||||
udp->uh_sport = tuple->dst_port;
|
||||
udp->uh_dport = tuple->src_port;
|
||||
udp->len = htons(len - iplen);
|
||||
|
||||
dns_packet_t *dns = (dns_packet_t*)(buf + iplen + 8);
|
||||
dns->transaction_id = req->transaction_id;
|
||||
dns->flags = htons(0x8180);
|
||||
dns->questions = req->questions;
|
||||
dns->answ_rrs = dns->questions;
|
||||
dns->auth_rrs = dns->additional_rrs = 0;
|
||||
|
||||
// Queries
|
||||
memcpy(dns->queries, req->queries, qlen + 5);
|
||||
|
||||
// Answers
|
||||
uint8_t *answ = dns->queries + qlen + 5;
|
||||
|
||||
answ[0] = 0xc0, answ[1] = 0x0c; // name ptr
|
||||
answ[2] = 0x00, answ[3] = qtype; // type
|
||||
answ[4] = 0x00, answ[5] = 0x01; // class IN
|
||||
*(uint32_t*)(answ + 6) = htonl(10); // TTL: 10s
|
||||
answ[10] = 0x00, answ[11] = alen; // addr length
|
||||
memset(answ + 12, 0, alen); // addr: 0.0.0.0/::
|
||||
|
||||
// checksum
|
||||
udp->uh_sum = 0;
|
||||
udp->uh_sum = zdtun_l3_checksum(pd->zdt, conn, buf, (char*)udp, len - iplen);
|
||||
|
||||
//hexdump(buf, len);
|
||||
write(pd->vpn.tunfd, buf, len);
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static int handle_new_connection(zdtun_t *zdt, zdtun_conn_t *conn_info) {
|
||||
pcapdroid_t *pd = ((pcapdroid_t *) zdtun_userdata(zdt));
|
||||
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn_info);
|
||||
@@ -260,7 +336,7 @@ static int handle_new_connection(zdtun_t *zdt, zdtun_conn_t *conn_info) {
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void connnection_closed(zdtun_t *zdt, const zdtun_conn_t *conn_info) {
|
||||
static void connection_closed(zdtun_t *zdt, const zdtun_conn_t *conn_info) {
|
||||
pcapdroid_t *pd = (pcapdroid_t*) zdtun_userdata(zdt);
|
||||
pd_conn_t *data = zdtun_conn_get_userdata(conn_info);
|
||||
|
||||
@@ -320,7 +396,7 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
|
||||
.account_packet = update_conn_status,
|
||||
.on_socket_open = protectSocketCallback,
|
||||
.on_connection_open = handle_new_connection,
|
||||
.on_connection_close = connnection_closed,
|
||||
.on_connection_close = connection_closed,
|
||||
};
|
||||
|
||||
// List of known DNS servers
|
||||
@@ -437,7 +513,14 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
|
||||
if(data->sent_pkts == 0) {
|
||||
data->to_block |= !check_dns_req_allowed(pd, conn, &pctx);
|
||||
|
||||
if(pd->socks5.enabled && !data->to_block && (tuple->ipproto == IPPROTO_TCP))
|
||||
if(data->to_block) {
|
||||
// blocking a DNS query can cause multiple requests to be spammed. Better to
|
||||
// spoof a reply with an invalid IP.
|
||||
if((data->l7proto == NDPI_PROTOCOL_DNS) && (tuple->ipproto == IPPROTO_UDP)) {
|
||||
spoof_dns_reply(pd, conn, &pctx);
|
||||
zdtun_destroy_conn(zdt, conn);
|
||||
}
|
||||
} else if(pd->socks5.enabled && (tuple->ipproto == IPPROTO_TCP))
|
||||
zdtun_conn_proxy(conn);
|
||||
}
|
||||
|
||||
|
||||
@@ -219,7 +219,6 @@ typedef struct {
|
||||
uint16_t answ_rrs;
|
||||
uint16_t auth_rrs;
|
||||
uint16_t additional_rrs;
|
||||
uint8_t initial_dot; // just skip
|
||||
uint8_t queries[];
|
||||
} __attribute__((packed)) dns_packet_t;
|
||||
|
||||
|
||||
+1
-1
Submodule submodules/zdtun updated: 3e5e749215...a000e4f3b1
Reference in New Issue
Block a user