mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-16 21:10:57 +08:00
Improve connections handling
- Improve netd resolution with root capture - Fix nDPI detection not performed on some closed connections - Fix missing blacklist match on some connections
This commit is contained in:
parent
eece3e8cce
commit
47ddb23e4b
@ -239,6 +239,7 @@ static void destroy_connection(zdtun_t *tun, const zdtun_conn_t *conn_info) {
|
||||
data->update_type |= CONN_UPDATE_STATS;
|
||||
notify_connection(&proxy->conns_updates, tuple, data);
|
||||
|
||||
conn_end_ndpi_detection(data, proxy, tuple);
|
||||
data->status = zdtun_conn_get_status(conn_info);
|
||||
data->to_purge = true;
|
||||
} else
|
||||
|
||||
@ -33,12 +33,12 @@
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
typedef struct {
|
||||
struct pcap_conn {
|
||||
zdtun_5tuple_t tuple;
|
||||
conn_data_t *data;
|
||||
|
||||
UT_hash_handle hh;
|
||||
} pcap_conn_t;
|
||||
};
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
@ -250,11 +250,32 @@ cleanup:
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void remove_connection(vpnproxy_data_t *proxy, const pcap_conn_t *conn) {
|
||||
switch (conn->tuple.ipproto) {
|
||||
case IPPROTO_TCP:
|
||||
proxy->stats.num_tcp_conn--;
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
proxy->stats.num_udp_conn--;
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
proxy->stats.num_icmp_conn--;
|
||||
break;
|
||||
}
|
||||
|
||||
HASH_DELETE(hh, proxy->connections, conn);
|
||||
free(conn);
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
// Determines when a connection gets closed
|
||||
static void update_connection_status(pcap_conn_t *conn, zdtun_pkt_t *pkt, uint8_t dir) {
|
||||
static void update_connection_status(vpnproxy_data_t *proxy, pcap_conn_t *conn, zdtun_pkt_t *pkt, uint8_t dir) {
|
||||
if((conn->data->status >= CONN_STATUS_CLOSED) || (pkt->flags & ZDTUN_PKT_IS_FRAGMENT))
|
||||
return;
|
||||
|
||||
zdtun_conn_status_t old_status = conn->data->status;
|
||||
|
||||
if(conn->tuple.ipproto == IPPROTO_TCP) {
|
||||
struct tcphdr *tcp = pkt->tcp;
|
||||
|
||||
@ -294,16 +315,28 @@ static void update_connection_status(pcap_conn_t *conn, zdtun_pkt_t *pkt, uint8_
|
||||
conn->data->pending_dns_queries--;
|
||||
|
||||
// Close the connection as soon as all the responses arrive
|
||||
if(conn->data->pending_dns_queries == 0)
|
||||
if(conn->data->pending_dns_queries == 0) {
|
||||
conn->data->status = CONN_STATUS_CLOSED;
|
||||
|
||||
// Remove the connection from the hash to ensure that if the DNS connection is
|
||||
// reused for a new query, it will generated a new connection, to properly
|
||||
// extract and handle the new DNS query. This also happens for AAAA + A queries.
|
||||
conn->data->to_purge = true;
|
||||
remove_connection(proxy, conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(old_status != conn->data->status) {
|
||||
conn->data->update_type |= CONN_UPDATE_STATS;
|
||||
notify_connection(&proxy->conns_updates, &conn->tuple, conn->data);
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void handle_packet(vpnproxy_data_t *proxy, pcap_conn_t **connections, pcapd_hdr_t *hdr, const char *buffer) {
|
||||
static void handle_packet(vpnproxy_data_t *proxy, pcapd_hdr_t *hdr, const char *buffer) {
|
||||
zdtun_pkt_t pkt;
|
||||
pcap_conn_t *conn = NULL;
|
||||
uint8_t from_tun = (hdr->flags & PCAPD_FLAG_TX); // NOTE: the direction uses an heuristic so it may be wrong
|
||||
@ -329,14 +362,14 @@ static void handle_packet(vpnproxy_data_t *proxy, pcap_conn_t **connections, pca
|
||||
tupleSwapPeers(&pkt.tuple);
|
||||
}
|
||||
|
||||
HASH_FIND(hh, *connections, &pkt.tuple, sizeof(zdtun_5tuple_t), conn);
|
||||
HASH_FIND(hh, proxy->connections, &pkt.tuple, sizeof(zdtun_5tuple_t), conn);
|
||||
|
||||
if(!conn) {
|
||||
// from_tun may be wrong, search in the other direction
|
||||
from_tun = !from_tun;
|
||||
tupleSwapPeers(&pkt.tuple);
|
||||
|
||||
HASH_FIND(hh, *connections, &pkt.tuple, sizeof(zdtun_5tuple_t), conn);
|
||||
HASH_FIND(hh, proxy->connections, &pkt.tuple, sizeof(zdtun_5tuple_t), conn);
|
||||
|
||||
if(!conn) {
|
||||
if((pkt.flags & ZDTUN_PKT_IS_FRAGMENT) && !(pkt.flags & ZDTUN_PKT_IS_FIRST_FRAGMENT)) {
|
||||
@ -365,7 +398,7 @@ static void handle_packet(vpnproxy_data_t *proxy, pcap_conn_t **connections, pca
|
||||
conn->tuple = pkt.tuple;
|
||||
conn->data = data;
|
||||
|
||||
HASH_ADD(hh, *connections, tuple, sizeof(zdtun_5tuple_t), conn);
|
||||
HASH_ADD(hh, proxy->connections, tuple, sizeof(zdtun_5tuple_t), conn);
|
||||
|
||||
data->incr_id = proxy->incr_id++;
|
||||
notify_connection(&proxy->new_conns, &pkt.tuple, data);
|
||||
@ -393,15 +426,15 @@ static void handle_packet(vpnproxy_data_t *proxy, pcap_conn_t **connections, pca
|
||||
uint64_t pkt_ms = (uint64_t)hdr->ts.tv_sec * 1000 + hdr->ts.tv_usec / 1000;
|
||||
account_packet(proxy, &pkt, from_tun, &conn->tuple, conn->data, pkt_ms);
|
||||
|
||||
update_connection_status(conn, &pkt, !from_tun);
|
||||
update_connection_status(proxy, conn, &pkt, !from_tun);
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void purge_expired_connections(vpnproxy_data_t *proxy, pcap_conn_t **connections, uint8_t purge_all) {
|
||||
static void purge_expired_connections(vpnproxy_data_t *proxy, uint8_t purge_all) {
|
||||
pcap_conn_t *conn, *tmp;
|
||||
|
||||
HASH_ITER(hh, *connections, conn, tmp) {
|
||||
HASH_ITER(hh, proxy->connections, conn, tmp) {
|
||||
uint64_t timeout = 0;
|
||||
|
||||
switch(conn->tuple.ipproto) {
|
||||
@ -433,20 +466,7 @@ static void purge_expired_connections(vpnproxy_data_t *proxy, pcap_conn_t **conn
|
||||
} else
|
||||
conn_free_data(conn->data);
|
||||
|
||||
switch (conn->tuple.ipproto) {
|
||||
case IPPROTO_TCP:
|
||||
proxy->stats.num_tcp_conn--;
|
||||
break;
|
||||
case IPPROTO_UDP:
|
||||
proxy->stats.num_udp_conn--;
|
||||
break;
|
||||
case IPPROTO_ICMP:
|
||||
proxy->stats.num_icmp_conn--;
|
||||
break;
|
||||
}
|
||||
|
||||
HASH_DELETE(hh, *connections, conn);
|
||||
free(conn);
|
||||
remove_connection(proxy, conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -457,7 +477,6 @@ int run_root(vpnproxy_data_t *proxy) {
|
||||
int sock = -1;
|
||||
int rv = -1;
|
||||
char buffer[65535];
|
||||
pcap_conn_t *connections = NULL;
|
||||
u_int64_t next_purge_ms;
|
||||
zdtun_callbacks_t callbacks = {.send_client = (void*)1};
|
||||
|
||||
@ -509,13 +528,13 @@ int run_root(vpnproxy_data_t *proxy) {
|
||||
}
|
||||
|
||||
proxy->num_dropped_pkts = hdr.pkt_drops;
|
||||
handle_packet(proxy, &connections, &hdr, buffer);
|
||||
handle_packet(proxy, &hdr, buffer);
|
||||
|
||||
housekeeping:
|
||||
run_housekeeping(proxy);
|
||||
|
||||
if(proxy->now_ms >= next_purge_ms) {
|
||||
purge_expired_connections(proxy, &connections, 0);
|
||||
purge_expired_connections(proxy, 0);
|
||||
next_purge_ms = proxy->now_ms + PERIODIC_PURGE_TIMEOUT_MS;
|
||||
}
|
||||
}
|
||||
@ -523,7 +542,7 @@ int run_root(vpnproxy_data_t *proxy) {
|
||||
rv = 0;
|
||||
|
||||
cleanup:
|
||||
purge_expired_connections(proxy, &connections, 1 /* purge_all */);
|
||||
purge_expired_connections(proxy, 1 /* purge_all */);
|
||||
|
||||
if(proxy->tun) zdtun_finalize(proxy->tun);
|
||||
if(sock > 0) close(sock);
|
||||
|
||||
@ -84,6 +84,11 @@ void conn_free_data(conn_data_t *data) {
|
||||
/* ******************************************************* */
|
||||
|
||||
void notify_connection(conn_array_t *arr, const zdtun_5tuple_t *tuple, conn_data_t *data) {
|
||||
// End the detection when the connection is closed
|
||||
// Always check this, even pending_notification are present
|
||||
if(data->status >= CONN_STATUS_CLOSED)
|
||||
conn_end_ndpi_detection(data, global_proxy, tuple);
|
||||
|
||||
if(data->pending_notification)
|
||||
return;
|
||||
|
||||
@ -294,6 +299,21 @@ const char *getProtoName(struct ndpi_detection_module_struct *mod, ndpi_protocol
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static bool check_blacklisted_domain(vpnproxy_data_t *proxy, conn_data_t *data, const zdtun_5tuple_t *tuple) {
|
||||
if(proxy->malware_detection.bl && data->info && data->info[0] && !data->blacklisted_domain) {
|
||||
data->blacklisted_domain = blacklist_match_domain(proxy->malware_detection.bl, data->info);
|
||||
if(data->blacklisted_domain) {
|
||||
char appbuf[64];
|
||||
char buf[512];
|
||||
|
||||
get_appname_by_uid(proxy, data->uid, appbuf, sizeof(appbuf));
|
||||
log_w("Blacklisted domain [%s]: %s [%s]", data->info, zdtun_5tuple2str(tuple, buf, sizeof(buf)), appbuf);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
conn_data_t* new_connection(vpnproxy_data_t *proxy, const zdtun_5tuple_t *tuple, int uid) {
|
||||
conn_data_t *data = calloc(1, sizeof(conn_data_t));
|
||||
|
||||
@ -361,6 +381,8 @@ conn_data_t* new_connection(vpnproxy_data_t *proxy, const zdtun_5tuple_t *tuple,
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
check_blacklisted_domain(proxy, data, tuple);
|
||||
}
|
||||
|
||||
if(proxy->malware_detection.bl && (tuple->ipver == 4)) {
|
||||
@ -444,17 +466,8 @@ void conn_end_ndpi_detection(conn_data_t *data, vpnproxy_data_t *proxy, const zd
|
||||
break;
|
||||
}
|
||||
|
||||
if(proxy->malware_detection.bl && data->info && data->info[0] && !data->blacklisted_domain) {
|
||||
// TODO early match
|
||||
data->blacklisted_domain = blacklist_match_domain(proxy->malware_detection.bl, data->info);
|
||||
if(data->blacklisted_domain) {
|
||||
char appbuf[64];
|
||||
char buf[512];
|
||||
|
||||
get_appname_by_uid(proxy, data->uid, appbuf, sizeof(appbuf));
|
||||
log_w("Blacklisted domain [%s]: %s [%s]", data->info, zdtun_5tuple2str(tuple, buf, sizeof(buf)), appbuf);
|
||||
}
|
||||
}
|
||||
// TODO early match
|
||||
check_blacklisted_domain(proxy, data, tuple);
|
||||
|
||||
data->update_type |= CONN_UPDATE_INFO;
|
||||
conn_free_ndpi(data);
|
||||
@ -599,12 +612,13 @@ static void process_ndpi_packet(conn_data_t *data, vpnproxy_data_t *proxy,
|
||||
if((!data->request_done) && !data->ndpi_flow->packet.tcp_retransmission)
|
||||
process_request_data(data, pkt, from_tun);
|
||||
|
||||
if(!from_tun && ((data->l7proto.master_protocol == NDPI_PROTOCOL_DNS)
|
||||
|| (data->l7proto.app_protocol == NDPI_PROTOCOL_DNS)))
|
||||
bool is_dns = ((data->l7proto.master_protocol == NDPI_PROTOCOL_DNS) ||
|
||||
(data->l7proto.app_protocol == NDPI_PROTOCOL_DNS));
|
||||
if(!from_tun && is_dns)
|
||||
process_dns_reply(data, proxy, pkt);
|
||||
|
||||
if(giveup || ((data->l7proto.app_protocol != NDPI_PROTOCOL_UNKNOWN) &&
|
||||
(!ndpi_extra_dissection_possible(proxy->ndpi, data->ndpi_flow))))
|
||||
!ndpi_extra_dissection_possible(proxy->ndpi, data->ndpi_flow)))
|
||||
conn_end_ndpi_detection(data, proxy, &pkt->tuple);
|
||||
}
|
||||
|
||||
@ -1045,10 +1059,10 @@ void account_packet(vpnproxy_data_t *proxy, const zdtun_pkt_t *pkt, uint8_t from
|
||||
// nDPI cannot handle fragments, since they miss the L4 layer (see ndpi_iph_is_valid_and_not_fragmented)
|
||||
process_ndpi_packet(data, proxy, pkt, from_tun);
|
||||
|
||||
if((data->l7proto.master_protocol == NDPI_PROTOCOL_DNS)
|
||||
if(((data->l7proto.master_protocol == NDPI_PROTOCOL_DNS) || (data->l7proto.app_protocol == NDPI_PROTOCOL_DNS))
|
||||
&& (data->uid == UID_NETD)
|
||||
&& (data->sent_pkts + data->rcvd_pkts == 1)
|
||||
&& ((netd_resolve_waiting > 0) || (next_connections_dump > (proxy->now_ms - NETD_RESOLVE_DELAY_MS)))) {
|
||||
&& ((netd_resolve_waiting > 0) || ((next_connections_dump - NETD_RESOLVE_DELAY_MS) < proxy->now_ms))) {
|
||||
if(netd_resolve_waiting == 0) {
|
||||
// Wait before sending the dump to possibly resolve netd DNS connections uid.
|
||||
// Only delay for the first DNS request, to avoid excessive delay.
|
||||
@ -1181,7 +1195,10 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) {
|
||||
}
|
||||
|
||||
proxy.malware_detection.bl = bl;
|
||||
// the CaptureService will ask for reload via reload_blacklists_now
|
||||
if(reload_blacklists_now) {
|
||||
reload_blacklists_now = false;
|
||||
reload_blacklists(&proxy);
|
||||
}
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
@ -107,6 +107,8 @@ typedef struct {
|
||||
UT_hash_handle hh;
|
||||
} uid_to_app_t;
|
||||
|
||||
typedef struct pcap_conn pcap_conn_t;
|
||||
|
||||
typedef struct vpnproxy_data {
|
||||
int tunfd;
|
||||
int incr_id;
|
||||
@ -139,6 +141,7 @@ typedef struct vpnproxy_data {
|
||||
bool root_capture;
|
||||
zdtun_statistics_t stats;
|
||||
uid_to_app_t *uid2app;
|
||||
pcap_conn_t *connections; // root only
|
||||
|
||||
struct {
|
||||
bool enabled;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user