diff --git a/app/src/main/jni/vpnproxy-jni/capture_proxy.c b/app/src/main/jni/vpnproxy-jni/capture_proxy.c index 058b714a..a6e11769 100644 --- a/app/src/main/jni/vpnproxy-jni/capture_proxy.c +++ b/app/src/main/jni/vpnproxy-jni/capture_proxy.c @@ -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 diff --git a/app/src/main/jni/vpnproxy-jni/capture_root.c b/app/src/main/jni/vpnproxy-jni/capture_root.c index 4f4a3c1f..36fbfdde 100644 --- a/app/src/main/jni/vpnproxy-jni/capture_root.c +++ b/app/src/main/jni/vpnproxy-jni/capture_root.c @@ -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); diff --git a/app/src/main/jni/vpnproxy-jni/vpnproxy.c b/app/src/main/jni/vpnproxy-jni/vpnproxy.c index 24b726e7..c65b23c4 100644 --- a/app/src/main/jni/vpnproxy-jni/vpnproxy.c +++ b/app/src/main/jni/vpnproxy-jni/vpnproxy.c @@ -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); diff --git a/app/src/main/jni/vpnproxy-jni/vpnproxy.h b/app/src/main/jni/vpnproxy-jni/vpnproxy.h index 8072b89b..3fab4be4 100644 --- a/app/src/main/jni/vpnproxy-jni/vpnproxy.h +++ b/app/src/main/jni/vpnproxy-jni/vpnproxy.h @@ -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;