diff --git a/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/HTTPServer.java b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/HTTPServer.java index 78775e31..8df0dd33 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/HTTPServer.java +++ b/app/src/main/java/com/emanuelef/remote_capture/pcap_dump/HTTPServer.java @@ -53,7 +53,8 @@ public class HTTPServer extends NanoHTTPD implements PcapDumper { return(r); } - /* Creates a new Response and add it to the active responses. */ + /* Creates a new Response and add it to the active responses. + * NOTE: socket protect not needed for inbound connections. */ private synchronized Response newPcapStream() { /* NOTE: response length is unknown */ Response res = newChunkedResponse(Status.OK, PCAP_MIME, new ChunkedInputStream()); diff --git a/app/src/main/jni/pcapd/pcapd.c b/app/src/main/jni/pcapd/pcapd.c index 5fb3ff09..a3dea2a2 100644 --- a/app/src/main/jni/pcapd/pcapd.c +++ b/app/src/main/jni/pcapd/pcapd.c @@ -59,6 +59,7 @@ typedef struct { int client; char bpf[512]; + zdtun_t *tun; pcap_t *pd; int dlink; int ipoffset; @@ -585,6 +586,10 @@ static int run_pcap_dump(int uid_filter, const char *bpf) { time_t next_stats_update = 0; uid_resolver_t *resolver = NULL; uid_lru_t *lru = NULL; + zdtun_callbacks_t callbacks = {.send_client = (void*)1}; + + if(!(rt.tun = zdtun_init(&callbacks, NULL))) + goto cleanup; if(!(resolver = init_uid_resolver_from_proc())) goto cleanup; @@ -662,7 +667,7 @@ static int run_pcap_dump(int uid_filter, const char *bpf) { pkt += to_skip; hdr->caplen -= to_skip; - if(zdtun_parse_pkt((const char*)pkt, hdr->caplen, &zpkt) == 0) { + if(zdtun_parse_pkt(rt.tun, (const char*)pkt, hdr->caplen, &zpkt) == 0) { if(!is_tx) { // Packet from the internet, swap src and dst tupleSwapPeers(&zpkt.tuple); @@ -711,6 +716,8 @@ static int run_pcap_dump(int uid_filter, const char *bpf) { cleanup: finish_pcapd_capture(&rt); + if(rt.tun) + zdtun_finalize(rt.tun); if(resolver) destroy_uid_resolver(resolver); if(lru) diff --git a/app/src/main/jni/vpnproxy-jni/capture_proxy.c b/app/src/main/jni/vpnproxy-jni/capture_proxy.c index c0b59ced..e1b1697a 100644 --- a/app/src/main/jni/vpnproxy-jni/capture_proxy.c +++ b/app/src/main/jni/vpnproxy-jni/capture_proxy.c @@ -364,11 +364,17 @@ int run_proxy(vpnproxy_data_t *proxy) { if (size > 0) { zdtun_pkt_t pkt; - if (zdtun_parse_pkt(buffer, size, &pkt) != 0) { + if(zdtun_parse_pkt(tun, buffer, size, &pkt) != 0) { log_d("zdtun_parse_pkt failed"); goto housekeeping; } + if(pkt.flags & ZDTUN_PKT_IS_FRAGMENT) { + log_d("discarding IP fragment"); + proxy->num_discarded_fragments++; + goto housekeeping; + } + proxy->last_pkt = &pkt; proxy->last_conn_blocked = false; @@ -433,7 +439,7 @@ int run_proxy(vpnproxy_data_t *proxy) { } } - ztdun_finalize(tun); + zdtun_finalize(tun); return(0); } diff --git a/app/src/main/jni/vpnproxy-jni/capture_root.c b/app/src/main/jni/vpnproxy-jni/capture_root.c index 3b5f9350..edb9ab3d 100644 --- a/app/src/main/jni/vpnproxy-jni/capture_root.c +++ b/app/src/main/jni/vpnproxy-jni/capture_root.c @@ -195,12 +195,22 @@ static void handle_packet(vpnproxy_data_t *proxy, pcap_conn_t **connections, pca 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 - // NOTE: only IP packets supported - if(zdtun_parse_pkt(buffer, hdr->len, &pkt) != 0) { + if(zdtun_parse_pkt(proxy->tun, buffer, hdr->len, &pkt) != 0) { log_d("zdtun_parse_pkt failed"); return; } + if((pkt.flags & ZDTUN_PKT_IS_FRAGMENT) && + (pkt.tuple.src_port == 0) && (pkt.tuple.dst_port == 0)) { + // This fragment cannot be mapped to the original src/dst ports. This may happen if the first + // IP fragment is lost or was not captured (e.g. for packets matching the BPF of getPcapDumperBpf). + // In such a case, we can only ignore the packet as we cannot determine the connection it belongs to. + + //log_d("unmatched IP fragment (ID = 0x%04x)", pkt.ip4->id); + proxy->num_discarded_fragments++; + return; + } + if(!from_tun) { // Packet from the internet, swap src and dst tupleSwapPeers(&pkt.tuple); @@ -216,6 +226,12 @@ static void handle_packet(vpnproxy_data_t *proxy, pcap_conn_t **connections, pca HASH_FIND(hh, *connections, &pkt.tuple, sizeof(zdtun_5tuple_t), conn); if(!conn) { + if((pkt.flags & ZDTUN_PKT_IS_FRAGMENT) && !(pkt.flags & ZDTUN_PKT_IS_FIRST_FRAGMENT)) { + log_d("ignoring fragment as it cannot start a connection"); + proxy->num_discarded_fragments++; + return; + } + // assume from_tun was correct from_tun = !from_tun; tupleSwapPeers(&pkt.tuple); @@ -316,15 +332,21 @@ static void purge_expired_connections(vpnproxy_data_t *proxy, pcap_conn_t **conn /* ******************************************************* */ int run_root(vpnproxy_data_t *proxy) { - int sock = connectPcapd(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}; - if(sock < 0) + if((proxy->tun = zdtun_init(&callbacks, NULL)) == NULL) return(-1); + if((sock = connectPcapd(proxy)) < 0) { + rv = -1; + goto cleanup; + } + refresh_time(proxy); next_purge_ms = proxy->now_ms + PERIODIC_PURGE_TIMEOUT_MS; @@ -381,6 +403,8 @@ int run_root(vpnproxy_data_t *proxy) { cleanup: purge_expired_connections(proxy, &connections, 1 /* purge_all */); - close(sock); + if(proxy->tun) zdtun_finalize(proxy->tun); + if(sock > 0) close(sock); + return rv; } diff --git a/app/src/main/jni/vpnproxy-jni/vpnproxy.c b/app/src/main/jni/vpnproxy-jni/vpnproxy.c index 836c6c16..948e3f6b 100644 --- a/app/src/main/jni/vpnproxy-jni/vpnproxy.c +++ b/app/src/main/jni/vpnproxy-jni/vpnproxy.c @@ -915,7 +915,9 @@ void account_packet(vpnproxy_data_t *proxy, const zdtun_pkt_t *pkt, uint8_t from proxy->capture_stats.rcvd_bytes += pkt->len; } - if(data->ndpi_flow) { + if(data->ndpi_flow && + (!(pkt->flags & ZDTUN_PKT_IS_FRAGMENT) || (pkt->flags & ZDTUN_PKT_IS_FIRST_FRAGMENT))) { + // 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) @@ -1070,6 +1072,7 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) { ndpi_ptree_destroy(proxy.known_dns_servers); log_d("Host LRU cache size: %d", ip_lru_size(proxy.ip_to_host)); + log_d("Discarded fragments: %ld", proxy.num_discarded_fragments); ip_lru_destroy(proxy.ip_to_host); logcallback = NULL; diff --git a/app/src/main/jni/vpnproxy-jni/vpnproxy.h b/app/src/main/jni/vpnproxy-jni/vpnproxy.h index 5a9986f6..a58071d3 100644 --- a/app/src/main/jni/vpnproxy-jni/vpnproxy.h +++ b/app/src/main/jni/vpnproxy-jni/vpnproxy.h @@ -106,6 +106,7 @@ typedef struct vpnproxy_data { ip_lru_t *ip_to_host; uint64_t now_ms; u_int num_dropped_pkts; + long num_discarded_fragments; u_int32_t num_dropped_connections; u_int32_t num_dns_requests; conn_array_t new_conns; diff --git a/submodules/zdtun b/submodules/zdtun index 6fadd10d..16ffe132 160000 --- a/submodules/zdtun +++ b/submodules/zdtun @@ -1 +1 @@ -Subproject commit 6fadd10dee10120be7634c1b3d0f23aea34a993d +Subproject commit 16ffe132167b75ea5bbad90261ab6214071a0036