mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-05-08 21:12:26 +00:00
Host is now extracted and blocked immediately
This commit also introduces the pkt_context_t to remove the need to store a temporary cur_pkt, reducing chance to introduce bugs in VPN mode.
This commit is contained in:
@@ -271,67 +271,66 @@ static void remove_connection(pcapdroid_t *pd, pcap_conn_t *conn) {
|
||||
|
||||
// Determines when a connection gets closed
|
||||
static void update_connection_status(pcapdroid_t *nc, pcap_conn_t *conn, zdtun_pkt_t *pkt, uint8_t dir) {
|
||||
// NOTE: pcap_conn_t neeeded below in remove_connection
|
||||
if((conn->data->status >= CONN_STATUS_CLOSED) || (pkt->flags & ZDTUN_PKT_IS_FRAGMENT))
|
||||
return;
|
||||
|
||||
zdtun_conn_status_t old_status = conn->data->status;
|
||||
zdtun_5tuple_t *tuple = &conn->tuple;
|
||||
pd_conn_t *data = conn->data;
|
||||
|
||||
if(conn->tuple.ipproto == IPPROTO_TCP) {
|
||||
if(tuple->ipproto == IPPROTO_TCP) {
|
||||
struct tcphdr *tcp = pkt->tcp;
|
||||
|
||||
conn->data->tcp_flags[dir] |= tcp->th_flags;
|
||||
uint8_t seen_flags = conn->data->tcp_flags[0] & conn->data->tcp_flags[1];
|
||||
data->tcp_flags[dir] |= tcp->th_flags;
|
||||
uint8_t seen_flags = data->tcp_flags[0] & data->tcp_flags[1];
|
||||
|
||||
if(tcp->th_flags & TH_RST)
|
||||
conn->data->status = CONN_STATUS_RESET;
|
||||
data->status = CONN_STATUS_RESET;
|
||||
else if(seen_flags & TH_FIN) {
|
||||
// closed when both the peers have sent FIN and the last FIN was acknowledged
|
||||
if(!conn->data->last_ack)
|
||||
conn->data->last_ack = true; // wait for the last ACK
|
||||
if(!data->last_ack)
|
||||
data->last_ack = true; // wait for the last ACK
|
||||
else if(tcp->th_flags & TH_ACK)
|
||||
conn->data->status = CONN_STATUS_CLOSED;
|
||||
} else if(conn->data->status < CONN_STATUS_CONNECTED) {
|
||||
data->status = CONN_STATUS_CLOSED;
|
||||
} else if(data->status < CONN_STATUS_CONNECTED) {
|
||||
const uint8_t syn_ack_flags = TH_SYN | TH_ACK;
|
||||
|
||||
// the 3-way-handshake is complete when both the peers have sent the SYN+ACK flags
|
||||
if((pkt->l7_len > 0) ||
|
||||
((seen_flags & syn_ack_flags) == syn_ack_flags))
|
||||
conn->data->status = CONN_STATUS_CONNECTED;
|
||||
data->status = CONN_STATUS_CONNECTED;
|
||||
else
|
||||
conn->data->status = CONN_STATUS_CONNECTING;
|
||||
data->status = CONN_STATUS_CONNECTING;
|
||||
}
|
||||
} else {
|
||||
if(conn->data->status < CONN_STATUS_CONNECTED)
|
||||
conn->data->status = CONN_STATUS_CONNECTED;
|
||||
if(data->status < CONN_STATUS_CONNECTED)
|
||||
data->status = CONN_STATUS_CONNECTED;
|
||||
|
||||
if((conn->tuple.ipproto == IPPROTO_UDP) &&
|
||||
if((tuple->ipproto == IPPROTO_UDP) &&
|
||||
pkt->l7_len >= sizeof(dns_packet_t) &&
|
||||
(conn->tuple.dst_port == ntohs(53))) {
|
||||
(tuple->dst_port == ntohs(53))) {
|
||||
const dns_packet_t *dns = (dns_packet_t *)pkt->l7;
|
||||
|
||||
if((dns->flags & DNS_FLAGS_MASK) == DNS_TYPE_REQUEST)
|
||||
conn->data->pending_dns_queries++;
|
||||
data->pending_dns_queries++;
|
||||
else if((dns->flags & DNS_FLAGS_MASK) == DNS_TYPE_RESPONSE) {
|
||||
conn->data->pending_dns_queries--;
|
||||
data->pending_dns_queries--;
|
||||
|
||||
// Close the connection as soon as all the responses arrive
|
||||
if(conn->data->pending_dns_queries == 0) {
|
||||
conn->data->status = CONN_STATUS_CLOSED;
|
||||
if(data->pending_dns_queries == 0) {
|
||||
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;
|
||||
data->to_purge = true;
|
||||
remove_connection(nc, conn);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if(old_status != conn->data->status) {
|
||||
conn->data->update_type |= CONN_UPDATE_STATS;
|
||||
pd_notify_connection_update(nc, &conn->tuple, conn->data);
|
||||
}
|
||||
// no need to call pd_notify_connection_update, it will be called as part of pd_account_stats
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
@@ -346,9 +345,6 @@ static void handle_packet(pcapdroid_t *pd, pcapd_hdr_t *hdr, const char *buffer)
|
||||
return;
|
||||
}
|
||||
|
||||
struct timeval tv = hdr->ts;
|
||||
pd_set_current_packet(pd, &pkt, is_tx, &tv);
|
||||
|
||||
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
|
||||
@@ -366,7 +362,6 @@ static void handle_packet(pcapdroid_t *pd, pcapd_hdr_t *hdr, const char *buffer)
|
||||
}
|
||||
|
||||
HASH_FIND(hh, pd->root.connections, &pkt.tuple, sizeof(zdtun_5tuple_t), conn);
|
||||
|
||||
if(!conn) {
|
||||
// is_tx may be wrong, search in the other direction
|
||||
is_tx = !is_tx;
|
||||
@@ -419,10 +414,15 @@ static void handle_packet(pcapdroid_t *pd, pcapd_hdr_t *hdr, const char *buffer)
|
||||
}
|
||||
}
|
||||
|
||||
conn->data->last_update_ms = pd->now_ms;
|
||||
// like last_seen but monotonic
|
||||
conn->data->root.last_update_ms = pd->now_ms;
|
||||
|
||||
struct timeval tv = hdr->ts;
|
||||
pkt_context_t pinfo;
|
||||
pd_process_packet(pd, &pkt, is_tx, &conn->tuple, conn->data, &tv, &pinfo);
|
||||
|
||||
pd_account_stats(pd, &conn->tuple, conn->data);
|
||||
update_connection_status(pd, conn, &pkt, !is_tx);
|
||||
pd_account_stats(pd, &pinfo);
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
@@ -445,7 +445,7 @@ static void purge_expired_connections(pcapdroid_t *pd, uint8_t purge_all) {
|
||||
break;
|
||||
}
|
||||
|
||||
if(purge_all || (pd->now_ms >= (conn->data->last_update_ms + timeout))) {
|
||||
if(purge_all || (pd->now_ms >= (conn->data->root.last_update_ms + timeout))) {
|
||||
//log_d("IDLE (type=%d)", conn->tuple.ipproto);
|
||||
|
||||
// The connection data will be purged
|
||||
|
||||
@@ -86,36 +86,48 @@ static struct timeval* get_pkt_timestamp(pcapdroid_t *pd, struct timeval *tv) {
|
||||
return tv;
|
||||
}
|
||||
|
||||
// use the last pkt timestamp
|
||||
log_w("clock_gettime failed[%d]: %s", errno, strerror(errno));
|
||||
return &pd->cur_pkt.tv;
|
||||
return tv;
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static int net2tun(zdtun_t *zdt, zdtun_pkt_t *pkt, const zdtun_conn_t *conn_info) {
|
||||
static int remote2vpn(zdtun_t *zdt, zdtun_pkt_t *pkt, const zdtun_conn_t *conn_info) {
|
||||
if(!running)
|
||||
// e.g. during zdtun_finalize
|
||||
return 0;
|
||||
|
||||
pcapdroid_t *pd = (pcapdroid_t*) zdtun_userdata(zdt);
|
||||
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn_info);
|
||||
pd_conn_t *data = zdtun_conn_get_userdata(conn_info);
|
||||
|
||||
struct timeval tv;
|
||||
pd_refresh_time(pd);
|
||||
pd_set_current_packet(pd, pkt, false, get_pkt_timestamp(pd, &tv));
|
||||
// if this is called inside zdtun_forward, account the egress packet first
|
||||
if(data->vpn.fw_pctx) {
|
||||
pd_account_stats(pd, data->vpn.fw_pctx);
|
||||
data->vpn.fw_pctx = NULL;
|
||||
}
|
||||
|
||||
if(data->to_block) // NOTE: blocked_pkts accounted in pd_account_stats
|
||||
return 0;
|
||||
struct timeval tv;
|
||||
pkt_context_t pctx;
|
||||
pd_refresh_time(pd);
|
||||
|
||||
pd_process_packet(pd, pkt, false, tuple, data, get_pkt_timestamp(pd, &tv), &pctx);
|
||||
if(data->to_block) {
|
||||
data->blocked_pkts++;
|
||||
|
||||
// Returning -1 will result into an error condition on the connection, forcing a connection
|
||||
// close. Closing the connection is mandatory as it's not possible to handle dropped packets
|
||||
// via zdtun, since data received via the zdtun TCP sockets must be delivered to the client.
|
||||
return -1;
|
||||
}
|
||||
|
||||
int rv = write(pd->vpn.tunfd, pkt->buf, pkt->len);
|
||||
|
||||
if(rv < 0) {
|
||||
if(errno == ENOBUFS) {
|
||||
char buf[256];
|
||||
|
||||
// Do not abort, the connection will be terminated
|
||||
log_e("Got ENOBUFS %s", zdtun_5tuple2str(zdtun_conn_get_5tuple(conn_info), buf, sizeof(buf)));
|
||||
log_e("Got ENOBUFS %s", zdtun_5tuple2str(tuple, buf, sizeof(buf)));
|
||||
} else if(errno == EIO) {
|
||||
log_i("Got I/O error (terminating?)");
|
||||
running = false;
|
||||
@@ -126,31 +138,25 @@ static int net2tun(zdtun_t *zdt, zdtun_pkt_t *pkt, const zdtun_conn_t *conn_info
|
||||
} else if(rv != pkt->len) {
|
||||
log_f("partial zdt write (%d / %d)", rv, pkt->len);
|
||||
rv = -1;
|
||||
} else
|
||||
} else {
|
||||
// Success
|
||||
rv = 0;
|
||||
pd_account_stats(pd, &pctx);
|
||||
}
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void check_socks5_redirection(pcapdroid_t *pd, zdtun_pkt_t *pkt, zdtun_conn_t *conn) {
|
||||
pd_conn_t *data = zdtun_conn_get_userdata(conn);
|
||||
|
||||
if((pkt->tuple.ipproto == IPPROTO_TCP) && (((data->sent_pkts + data->rcvd_pkts) == 0)))
|
||||
zdtun_conn_proxy(conn);
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
/*
|
||||
* If the packet contains a DNS request directed to the IP address used internally by PCAPdroid,
|
||||
* then rewrite the server address with the actual DNS server.
|
||||
* Moreover, if a private DNS connection is detected in opportunistic mode (block_private_dns true),
|
||||
* then block this connection to force the fallback to non-private DNS mode.
|
||||
*/
|
||||
static bool check_dns_req_allowed(pcapdroid_t *pd, zdtun_conn_t *conn) {
|
||||
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn);
|
||||
static bool check_dns_req_allowed(pcapdroid_t *pd, zdtun_conn_t *conn, pkt_context_t *pctx) {
|
||||
const zdtun_5tuple_t *tuple = pctx->tuple;
|
||||
|
||||
if(new_dns_server != 0) {
|
||||
// Reload DNS server
|
||||
@@ -164,7 +170,7 @@ static bool check_dns_req_allowed(pcapdroid_t *pd, zdtun_conn_t *conn) {
|
||||
log_d("Using new DNS server");
|
||||
}
|
||||
|
||||
if(zdtun_conn_get_5tuple(conn)->ipproto == IPPROTO_ICMP)
|
||||
if(pctx->tuple->ipproto == IPPROTO_ICMP)
|
||||
return true;
|
||||
|
||||
bool is_internal_dns = (tuple->ipver == 4) && (tuple->dst_ip.ip4 == pd->vpn.internal_dns);
|
||||
@@ -199,7 +205,7 @@ static bool check_dns_req_allowed(pcapdroid_t *pd, zdtun_conn_t *conn) {
|
||||
return(true);
|
||||
|
||||
if((tuple->ipproto == IPPROTO_UDP) && (ntohs(tuple->dst_port) == 53)) {
|
||||
zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
|
||||
zdtun_pkt_t *pkt = pctx->pkt;
|
||||
int dns_length = pkt->l7_len;
|
||||
|
||||
if(dns_length >= sizeof(dns_packet_t)) {
|
||||
@@ -245,7 +251,6 @@ static int handle_new_connection(zdtun_t *zdt, zdtun_conn_t *conn_info) {
|
||||
}
|
||||
|
||||
zdtun_conn_set_userdata(conn_info, data);
|
||||
data->to_block = !check_dns_req_allowed(pd, conn_info);
|
||||
|
||||
/* accept connection */
|
||||
return(0);
|
||||
@@ -276,30 +281,13 @@ static void destroy_connection(zdtun_t *zdt, const zdtun_conn_t *conn_info) {
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void on_packet(zdtun_t *zdt, const zdtun_pkt_t *pkt, uint8_t from_tun, const zdtun_conn_t *conn_info) {
|
||||
if(!running)
|
||||
// e.g. during zdtun_finalize
|
||||
return;
|
||||
|
||||
pcapdroid_t *pd = ((pcapdroid_t*)zdtun_userdata(zdt));
|
||||
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn_info);
|
||||
|
||||
// This is called after remote2vpn or zdtun_forward
|
||||
// No need to call pd_notify_connection_update, pd_account_stats is executed before
|
||||
static void update_conn_status(zdtun_t *zdt, const zdtun_pkt_t *pkt, uint8_t from_tun, const zdtun_conn_t *conn_info) {
|
||||
pd_conn_t *data = zdtun_conn_get_userdata(conn_info);
|
||||
if(!data) {
|
||||
log_e("Missing data in connection");
|
||||
return;
|
||||
}
|
||||
|
||||
// Update the connection status
|
||||
data->status = zdtun_conn_get_status(conn_info);
|
||||
|
||||
if(data->to_block) {
|
||||
data->blocked_pkts++;
|
||||
data->last_seen = pd->cur_pkt.ms;
|
||||
return;
|
||||
}
|
||||
|
||||
pd_account_stats(pd, tuple, data);
|
||||
|
||||
if(data->status >= CONN_STATUS_CLOSED)
|
||||
data->to_purge = true;
|
||||
}
|
||||
@@ -326,8 +314,8 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
|
||||
pd->vpn.known_dns_servers = ndpi_ptree_create();
|
||||
|
||||
zdtun_callbacks_t callbacks = {
|
||||
.send_client = net2tun,
|
||||
.account_packet = on_packet,
|
||||
.send_client = remote2vpn,
|
||||
.account_packet = update_conn_status,
|
||||
.on_socket_open = protectSocketCallback,
|
||||
.on_connection_open = handle_new_connection,
|
||||
.on_connection_close = destroy_connection,
|
||||
@@ -406,9 +394,6 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
|
||||
goto housekeeping;
|
||||
}
|
||||
|
||||
struct timeval tv;
|
||||
pd_set_current_packet(pd, &pkt, true, get_pkt_timestamp(pd, &tv));
|
||||
|
||||
if((pkt.tuple.ipver == 6) && (!pd->ipv6.enabled)) {
|
||||
char buf[512];
|
||||
|
||||
@@ -438,18 +423,28 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
|
||||
goto housekeeping;
|
||||
}
|
||||
|
||||
// Process the packet
|
||||
struct timeval tv;
|
||||
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn);
|
||||
pkt_context_t pctx;
|
||||
pd_conn_t *data = zdtun_conn_get_userdata(conn);
|
||||
|
||||
pd_process_packet(pd, &pkt, true, tuple, data, get_pkt_timestamp(pd, &tv), &pctx);
|
||||
data->vpn.fw_pctx = &pctx;
|
||||
|
||||
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))
|
||||
zdtun_conn_proxy(conn);
|
||||
}
|
||||
|
||||
if(data->to_block) {
|
||||
data->blocked_pkts++;
|
||||
data->last_seen = pd->cur_pkt.ms;
|
||||
if(!data->first_seen)
|
||||
data->first_seen = data->last_seen;
|
||||
goto housekeeping;
|
||||
}
|
||||
|
||||
if(pd->socks5.enabled)
|
||||
check_socks5_redirection(pd, &pkt, conn);
|
||||
|
||||
// NOTE: zdtun_forward may cause nested calls to remote2vpn
|
||||
if(zdtun_forward(zdt, &pkt, conn) != 0) {
|
||||
char buf[512];
|
||||
|
||||
@@ -459,6 +454,10 @@ int run_vpn(pcapdroid_t *pd, int tunfd) {
|
||||
pd->num_dropped_connections++;
|
||||
zdtun_destroy_conn(zdt, conn);
|
||||
goto housekeeping;
|
||||
} else if(data->vpn.fw_pctx) {
|
||||
// not accounted in remote2vpn, account here
|
||||
pd_account_stats(pd, data->vpn.fw_pctx);
|
||||
data->vpn.fw_pctx = NULL;
|
||||
}
|
||||
} else {
|
||||
pd_refresh_time(pd);
|
||||
|
||||
@@ -66,13 +66,13 @@ int pcap_rec_size(int pkt_len) {
|
||||
|
||||
/* Dumps a packet into the provided buffer. The buffer must have at least pcap_rec_size()
|
||||
* bytes available */
|
||||
void pcap_dump_rec(u_char *buffer, pcapdroid_t *pd, pd_conn_t *conn) {
|
||||
const zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
|
||||
void pcap_dump_rec(pcapdroid_t *pd, u_char *buffer, pkt_context_t *pctx) {
|
||||
const zdtun_pkt_t *pkt = pctx->pkt;
|
||||
struct pcaprec_hdr_s *pcap_rec = (pcaprec_hdr_s*) buffer;
|
||||
int offset = 0;
|
||||
|
||||
pcap_rec->ts_sec = pd->cur_pkt.tv.tv_sec;
|
||||
pcap_rec->ts_usec = pd->cur_pkt.tv.tv_usec;
|
||||
pcap_rec->ts_sec = pctx->tv.tv_sec;
|
||||
pcap_rec->ts_usec = pctx->tv.tv_usec;
|
||||
pcap_rec->incl_len = pcap_rec_size(pkt->len) - (int)sizeof(struct pcaprec_hdr_s);
|
||||
pcap_rec->orig_len = pkt->len;
|
||||
buffer += sizeof(struct pcaprec_hdr_s);
|
||||
@@ -107,7 +107,7 @@ void pcap_dump_rec(u_char *buffer, pcapdroid_t *pd, pd_conn_t *conn) {
|
||||
// Populate the custom data
|
||||
pcapdroid_trailer_t *cdata = (pcapdroid_trailer_t*)(buffer + offset);
|
||||
|
||||
fill_custom_data(cdata, pd, conn);
|
||||
fill_custom_data(cdata, pd, pctx->data);
|
||||
|
||||
//clock_t start = clock();
|
||||
cdata->fcs = crc32(buffer, pcap_rec->incl_len - 4, 0);
|
||||
|
||||
@@ -60,6 +60,6 @@ typedef struct pcapdroid_trailer {
|
||||
void pcap_set_pcapdroid_trailer(uint8_t enabled);
|
||||
void pcap_build_hdr(struct pcap_hdr_s *pcap_hdr);
|
||||
int pcap_rec_size(int pkt_len);
|
||||
void pcap_dump_rec(u_char *buffer, pcapdroid_t *pd, pd_conn_t *conn);
|
||||
void pcap_dump_rec(pcapdroid_t *pd, u_char *buffer, pkt_context_t *pctx);
|
||||
|
||||
#endif // __MY_PCAP_H__
|
||||
|
||||
@@ -348,7 +348,6 @@ static void check_blacklisted_domain(pcapdroid_t *pd, pd_conn_t *data, const zdt
|
||||
|
||||
pd_conn_t* pd_new_connection(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, int uid) {
|
||||
pd_conn_t *data = pd_calloc(1, sizeof(pd_conn_t));
|
||||
|
||||
if(!data) {
|
||||
log_e("calloc(pd_conn_t) failed with code %d/%s",
|
||||
errno, strerror(errno));
|
||||
@@ -386,6 +385,7 @@ pd_conn_t* pd_new_connection(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, int u
|
||||
inet_ntop(family, &dst_ip, resip, sizeof(resip));
|
||||
|
||||
log_d("Host LRU cache HIT: %s -> %s", resip, data->info);
|
||||
data->info_from_lru = true;
|
||||
|
||||
if(data->uid != UID_UNKNOWN) {
|
||||
// When a DNS request is followed by a TLS connection or similar, mark the DNS request
|
||||
@@ -477,6 +477,44 @@ static bool is_numeric_host(const char *host) {
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void process_ndpi_data(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, pd_conn_t *data) {
|
||||
char *found_info = NULL;
|
||||
|
||||
switch(data->l7proto.master_protocol) {
|
||||
case NDPI_PROTOCOL_DNS:
|
||||
if(data->ndpi_flow->host_server_name[0])
|
||||
found_info = (char*)data->ndpi_flow->host_server_name;
|
||||
break;
|
||||
case NDPI_PROTOCOL_HTTP:
|
||||
if(data->ndpi_flow->host_server_name[0] &&
|
||||
!is_numeric_host((char*)data->ndpi_flow->host_server_name))
|
||||
found_info = (char*)data->ndpi_flow->host_server_name;
|
||||
|
||||
if(!data->url && data->ndpi_flow->http.url) {
|
||||
data->url = pd_strndup(data->ndpi_flow->http.url, 256);
|
||||
data->update_type |= CONN_UPDATE_INFO;
|
||||
}
|
||||
|
||||
break;
|
||||
case NDPI_PROTOCOL_TLS:
|
||||
if(data->ndpi_flow->protos.tls_quic_stun.tls_quic.client_requested_server_name[0])
|
||||
found_info = (char*)data->ndpi_flow->protos.tls_quic_stun.tls_quic.client_requested_server_name;
|
||||
break;
|
||||
}
|
||||
|
||||
if(found_info && (!data->info || data->info_from_lru)) {
|
||||
if(data->info)
|
||||
pd_free(data->info);
|
||||
data->info = pd_strndup(found_info, 256);
|
||||
data->info_from_lru = false;
|
||||
|
||||
check_blacklisted_domain(pd, data, tuple);
|
||||
data->update_type |= CONN_UPDATE_INFO;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
/* Stop the DPI detection and determine the l7proto of the connection. */
|
||||
void pd_giveup_dpi(pcapdroid_t *pd, pd_conn_t *data, const zdtun_5tuple_t *tuple) {
|
||||
if(!data->ndpi_flow)
|
||||
@@ -495,40 +533,7 @@ void pd_giveup_dpi(pcapdroid_t *pd, pd_conn_t *data, const zdtun_5tuple_t *tuple
|
||||
log_d("nDPI completed[ipver=%d, proto=%d] -> l7proto: app=%d, master=%d",
|
||||
tuple->ipver, tuple->ipproto, data->l7proto.app_protocol, data->l7proto.master_protocol);
|
||||
|
||||
switch (data->l7proto.master_protocol) {
|
||||
case NDPI_PROTOCOL_DNS:
|
||||
if(data->ndpi_flow->host_server_name[0]) {
|
||||
if(data->info)
|
||||
pd_free(data->info);
|
||||
data->info = pd_strndup((char*)data->ndpi_flow->host_server_name, 256);
|
||||
}
|
||||
break;
|
||||
case NDPI_PROTOCOL_HTTP:
|
||||
if(data->ndpi_flow->host_server_name[0] &&
|
||||
(!data->info || !is_numeric_host((char*)data->ndpi_flow->host_server_name))) {
|
||||
if(data->info)
|
||||
pd_free(data->info);
|
||||
data->info = pd_strndup((char*) data->ndpi_flow->host_server_name, 256);
|
||||
}
|
||||
|
||||
if(data->ndpi_flow->http.url)
|
||||
data->url = pd_strndup(data->ndpi_flow->http.url, 256);
|
||||
|
||||
break;
|
||||
case NDPI_PROTOCOL_TLS:
|
||||
if(data->ndpi_flow->protos.tls_quic_stun.tls_quic.client_requested_server_name[0]) {
|
||||
if(data->info)
|
||||
pd_free(data->info);
|
||||
|
||||
data->info = pd_strndup(data->ndpi_flow->protos.tls_quic_stun.tls_quic.client_requested_server_name, 256);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO early match
|
||||
check_blacklisted_domain(pd, data, tuple);
|
||||
|
||||
data->update_type |= CONN_UPDATE_INFO;
|
||||
process_ndpi_data(pd, tuple, data);
|
||||
conn_free_ndpi(data);
|
||||
}
|
||||
|
||||
@@ -540,11 +545,12 @@ static int is_plaintext(char c) {
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void process_request_data(pcapdroid_t *pd, pd_conn_t *data) {
|
||||
const zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
|
||||
static void process_request_data(pcapdroid_t *pd, pkt_context_t *pctx) {
|
||||
const zdtun_pkt_t *pkt = pctx->pkt;
|
||||
pd_conn_t *data = pctx->data;
|
||||
|
||||
if(pkt->l7_len > 0) {
|
||||
if(pd->cur_pkt.is_tx && is_plaintext(pkt->l7[0])) {
|
||||
if(pctx->is_tx && is_plaintext(pkt->l7[0])) {
|
||||
int request_len = data->request_data ? (int)strlen(data->request_data) : 0;
|
||||
int num_chars = min(MAX_PLAINTEXT_LENGTH - request_len, pkt->l7_len);
|
||||
|
||||
@@ -657,10 +663,11 @@ static void process_dns_reply(pd_conn_t *data, pcapdroid_t *pd, const struct zdt
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void process_ndpi_packet(pcapdroid_t *pd, pd_conn_t *data) {
|
||||
bool giveup = ((data->sent_pkts + data->rcvd_pkts) >= MAX_DPI_PACKETS);
|
||||
zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
|
||||
bool is_tx = pd->cur_pkt.is_tx;
|
||||
static void perform_dpi(pcapdroid_t *pd, pkt_context_t *pctx) {
|
||||
pd_conn_t *data = pctx->data;
|
||||
bool giveup = ((data->sent_pkts + data->rcvd_pkts + 1) >= MAX_DPI_PACKETS);
|
||||
zdtun_pkt_t *pkt = pctx->pkt;
|
||||
bool is_tx = pctx->is_tx;
|
||||
|
||||
u_int16_t old_proto = data->l7proto.master_protocol;
|
||||
data->l7proto = ndpi_detection_process_packet(pd->ndpi, data->ndpi_flow, (const u_char *)pkt->buf,
|
||||
@@ -672,7 +679,7 @@ static void process_ndpi_packet(pcapdroid_t *pd, pd_conn_t *data) {
|
||||
data->update_type |= CONN_UPDATE_INFO;
|
||||
|
||||
if((!data->request_done) && !data->ndpi_flow->packet.tcp_retransmission)
|
||||
process_request_data(pd, data);
|
||||
process_request_data(pd, pctx);
|
||||
|
||||
bool is_dns = ((data->l7proto.master_protocol == NDPI_PROTOCOL_DNS) ||
|
||||
(data->l7proto.app_protocol == NDPI_PROTOCOL_DNS));
|
||||
@@ -681,7 +688,22 @@ static void process_ndpi_packet(pcapdroid_t *pd, pd_conn_t *data) {
|
||||
|
||||
if(giveup || ((data->l7proto.app_protocol != NDPI_PROTOCOL_UNKNOWN) &&
|
||||
!ndpi_extra_dissection_possible(pd->ndpi, data->ndpi_flow)))
|
||||
pd_giveup_dpi(pd, data, &pkt->tuple);
|
||||
pd_giveup_dpi(pd, data, &pkt->tuple); // calls process_ndpi_data
|
||||
else
|
||||
process_ndpi_data(pd, &pkt->tuple, data);
|
||||
|
||||
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 == 0)
|
||||
&& ((netd_resolve_waiting > 0) || ((next_connections_dump - NETD_RESOLVE_DELAY_MS) < pd->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.
|
||||
log_d("Adding netd resolution delay");
|
||||
next_connections_dump += NETD_RESOLVE_DELAY_MS;
|
||||
}
|
||||
netd_resolve_waiting++;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
@@ -1178,12 +1200,8 @@ static int check_blocked_conn_cb(zdtun_t *zdt, const zdtun_conn_t *conn_info, vo
|
||||
/* ******************************************************* */
|
||||
|
||||
/* Perfom periodic tasks. This should be called after processing a packet or after some time has
|
||||
* passed (e.g. after a select with no packet). pd->cur_pkt.pkt is invalidated after this call. */
|
||||
* passed (e.g. after a select with no packet). */
|
||||
void pd_housekeeping(pcapdroid_t *pd) {
|
||||
// can reach housekeeping from any state except housekeeping
|
||||
assert(pd->pkt_phase != PKT_PHASE_HOUSEKEEPING);
|
||||
pd->pkt_phase = PKT_PHASE_HOUSEKEEPING;
|
||||
|
||||
if(pd->capture_stats.new_stats
|
||||
&& ((pd->now_ms - pd->capture_stats.last_update_ms) >= CAPTURE_STATS_UPDATE_FREQUENCY_MS) ||
|
||||
dump_capture_stats_now) {
|
||||
@@ -1233,9 +1251,6 @@ void pd_housekeeping(pcapdroid_t *pd) {
|
||||
if(pd->zdt)
|
||||
zdtun_iter_connections(pd->zdt, check_blocked_conn_cb, pd);
|
||||
}
|
||||
|
||||
// avoid using freed data
|
||||
pd->cur_pkt.pkt = NULL;
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
@@ -1244,9 +1259,6 @@ void pd_housekeeping(pcapdroid_t *pd) {
|
||||
void pd_refresh_time(pcapdroid_t *pd) {
|
||||
struct timespec ts;
|
||||
|
||||
// can be reached by any phase
|
||||
pd->pkt_phase = PKT_PHASE_REFRESH_TIME;
|
||||
|
||||
if(clock_gettime(CLOCK_MONOTONIC_COARSE, &ts)) {
|
||||
log_d("clock_gettime failed[%d]: %s", errno, strerror(errno));
|
||||
return;
|
||||
@@ -1286,32 +1298,35 @@ void fill_custom_data(struct pcapdroid_trailer *cdata, pcapdroid_t *pd, pd_conn_
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
/* Set the current packet data. The packet pointer is available via pd->cur_pkt.pkt until the next
|
||||
* call to pd_housekeeping. This must be called after pd_refresh_time. */
|
||||
void pd_set_current_packet(pcapdroid_t *pd, zdtun_pkt_t *pkt, bool is_tx, struct timeval *tv) {
|
||||
assert(pd->pkt_phase == PKT_PHASE_REFRESH_TIME);
|
||||
pd->pkt_phase = PKT_PHASE_PKT_SET;
|
||||
/* Process the packet (e.g. perform DPI) and fill the packet context. */
|
||||
void pd_process_packet(pcapdroid_t *pd, zdtun_pkt_t *pkt, bool is_tx, const zdtun_5tuple_t *tuple,
|
||||
pd_conn_t *data, struct timeval *tv, pkt_context_t *pctx) {
|
||||
pctx->pkt = pkt;
|
||||
pctx->tv = *tv;
|
||||
pctx->ms = (uint64_t)tv->tv_sec * 1000 + tv->tv_usec / 1000;
|
||||
pctx->is_tx = is_tx;
|
||||
pctx->tuple = tuple;
|
||||
pctx->data = data;
|
||||
|
||||
pd->cur_pkt.pkt = pkt;
|
||||
pd->cur_pkt.tv = *tv;
|
||||
pd->cur_pkt.ms = (uint64_t)tv->tv_sec * 1000 + tv->tv_usec / 1000;
|
||||
pd->cur_pkt.is_tx = is_tx;
|
||||
data->last_seen = pctx->ms;
|
||||
if(!data->first_seen)
|
||||
data->first_seen = data->last_seen;
|
||||
|
||||
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)
|
||||
perform_dpi(pd, pctx);
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
/* Update the stats for the current packet. */
|
||||
void pd_account_stats(pcapdroid_t *pd, const zdtun_5tuple_t *conn_tuple, pd_conn_t *data) {
|
||||
zdtun_pkt_t *pkt = pd->cur_pkt.pkt;
|
||||
/* Update the stats for the current packet and dump it if requested. */
|
||||
void pd_account_stats(pcapdroid_t *pd, pkt_context_t *pctx) {
|
||||
zdtun_pkt_t *pkt = pctx->pkt;
|
||||
pd_conn_t *data = pctx->data;
|
||||
|
||||
assert(pd->pkt_phase == PKT_PHASE_PKT_SET);
|
||||
pd->pkt_phase = PKT_PHASE_ACCOUNT_STATS;
|
||||
|
||||
data->last_seen = pd->cur_pkt.ms;
|
||||
if(!data->first_seen)
|
||||
data->first_seen = data->last_seen;
|
||||
|
||||
if(pd->cur_pkt.is_tx) {
|
||||
if(pctx->is_tx) {
|
||||
data->sent_pkts++;
|
||||
data->sent_bytes += pkt->len;
|
||||
pd->capture_stats.sent_pkts++;
|
||||
@@ -1323,32 +1338,13 @@ void pd_account_stats(pcapdroid_t *pd, const zdtun_5tuple_t *conn_tuple, pd_conn
|
||||
pd->capture_stats.rcvd_bytes += pkt->len;
|
||||
}
|
||||
|
||||
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(pd, data);
|
||||
|
||||
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 - NETD_RESOLVE_DELAY_MS) < pd->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.
|
||||
log_d("Adding netd resolution delay");
|
||||
next_connections_dump += NETD_RESOLVE_DELAY_MS;
|
||||
}
|
||||
netd_resolve_waiting++;
|
||||
}
|
||||
}
|
||||
|
||||
/* New stats to notify */
|
||||
pd->capture_stats.new_stats = true;
|
||||
|
||||
data->update_type |= CONN_UPDATE_STATS;
|
||||
pd_notify_connection_update(pd, conn_tuple, data);
|
||||
pd_notify_connection_update(pd, pctx->tuple, pctx->data);
|
||||
|
||||
if (pd->pcap_dump.buffer) {
|
||||
if(pd->pcap_dump.buffer) {
|
||||
int rec_size = pcap_rec_size(pkt->len);
|
||||
|
||||
if ((JAVA_PCAP_BUFFER_SIZE - pd->pcap_dump.buffer_idx) <= rec_size) {
|
||||
@@ -1360,8 +1356,8 @@ void pd_account_stats(pcapdroid_t *pd, const zdtun_5tuple_t *conn_tuple, pd_conn
|
||||
log_e("Invalid buffer size [size=%d, idx=%d, tot_size=%d]",
|
||||
JAVA_PCAP_BUFFER_SIZE, pd->pcap_dump.buffer_idx, rec_size);
|
||||
else {
|
||||
pcap_dump_rec((u_char *) pd->pcap_dump.buffer + pd->pcap_dump.buffer_idx,
|
||||
pd, data);
|
||||
pcap_dump_rec(pd, (u_char *) pd->pcap_dump.buffer + pd->pcap_dump.buffer_idx,
|
||||
pctx);
|
||||
|
||||
pd->pcap_dump.buffer_idx += rec_size;
|
||||
}
|
||||
@@ -1486,7 +1482,6 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) {
|
||||
|
||||
memset(&pd.stats, 0, sizeof(pd.stats));
|
||||
|
||||
pd.pkt_phase = PKT_PHASE_HOUSEKEEPING;
|
||||
pd_refresh_time(&pd);
|
||||
last_connections_dump = pd.now_ms;
|
||||
next_connections_dump = last_connections_dump + 500 /* first update after 500 ms */;
|
||||
|
||||
@@ -57,15 +57,6 @@ typedef struct {
|
||||
u_int64_t last_update_ms;
|
||||
} capture_stats_t;
|
||||
|
||||
// This tracks the packet processing phases, to ensure that the API functions are called in
|
||||
// the correct order.
|
||||
typedef enum {
|
||||
PKT_PHASE_REFRESH_TIME = 0, // pd_refresh_time
|
||||
PKT_PHASE_PKT_SET, // pd_set_current_packet
|
||||
PKT_PHASE_ACCOUNT_STATS, // pd_account_stats
|
||||
PKT_PHASE_HOUSEKEEPING, // pd_housekeeping
|
||||
} pkt_processing_phase_t;
|
||||
|
||||
typedef struct {
|
||||
jint incr_id; // an incremental number which identifies a specific connection
|
||||
|
||||
@@ -74,7 +65,15 @@ typedef struct {
|
||||
struct ndpi_id_struct *src_id, *dst_id;
|
||||
ndpi_protocol l7proto;
|
||||
|
||||
uint64_t last_update_ms; // like last_seen but monotonic (only root)
|
||||
union {
|
||||
struct {
|
||||
uint64_t last_update_ms; // like last_seen but monotonic
|
||||
} root;
|
||||
struct {
|
||||
struct pkt_context *fw_pctx; // context for the forwarded packet
|
||||
} vpn;
|
||||
};
|
||||
|
||||
jlong first_seen;
|
||||
jlong last_seen;
|
||||
jlong sent_bytes;
|
||||
@@ -92,6 +91,7 @@ typedef struct {
|
||||
};
|
||||
bool pending_notification;
|
||||
bool to_purge;
|
||||
bool info_from_lru;
|
||||
bool request_done;
|
||||
bool blacklisted_ip;
|
||||
bool blacklisted_domain;
|
||||
@@ -118,13 +118,21 @@ typedef struct {
|
||||
UT_hash_handle hh;
|
||||
} uid_to_app_t;
|
||||
|
||||
typedef struct pkt_context {
|
||||
zdtun_pkt_t *pkt;
|
||||
struct timeval tv; // Packet timestamp, need by pcap_dump_rec
|
||||
uint64_t ms; // Packet timestamp in ms
|
||||
bool is_tx;
|
||||
const zdtun_5tuple_t *tuple;
|
||||
pd_conn_t *data;
|
||||
} pkt_context_t;
|
||||
|
||||
typedef struct pcap_conn pcap_conn_t;
|
||||
|
||||
typedef struct {
|
||||
JNIEnv *env;
|
||||
jobject capture_service;
|
||||
jint sdk_ver;
|
||||
pkt_processing_phase_t pkt_phase;
|
||||
int new_conn_id;
|
||||
uint64_t now_ms; // Monotonic timestamp, see pd_refresh_time
|
||||
struct ndpi_detection_module_struct *ndpi;
|
||||
@@ -164,14 +172,6 @@ typedef struct {
|
||||
} root;
|
||||
};
|
||||
|
||||
// populated via pd_set_current_packet
|
||||
struct {
|
||||
zdtun_pkt_t *pkt;
|
||||
struct timeval tv; // Packet timestamp, need by pcap_dump_rec
|
||||
uint64_t ms; // Packet timestamp in ms
|
||||
bool is_tx;
|
||||
} cur_pkt;
|
||||
|
||||
struct {
|
||||
bool enabled;
|
||||
// the crc32 implementation requires 4-bytes aligned accesses.
|
||||
@@ -270,8 +270,9 @@ extern bool block_private_dns;
|
||||
|
||||
// capture API
|
||||
void pd_refresh_time(pcapdroid_t *pd);
|
||||
void pd_set_current_packet(pcapdroid_t *pd, zdtun_pkt_t *pkt, bool is_tx, struct timeval *tv);
|
||||
void pd_account_stats(pcapdroid_t *pd, const zdtun_5tuple_t *conn_tuple, pd_conn_t *data);
|
||||
void pd_process_packet(pcapdroid_t *pd, zdtun_pkt_t *pkt, bool is_tx, const zdtun_5tuple_t *tuple,
|
||||
pd_conn_t *data, struct timeval *tv, pkt_context_t *pctx);
|
||||
void pd_account_stats(pcapdroid_t *pd, pkt_context_t *pctx);
|
||||
void pd_housekeeping(pcapdroid_t *pd);
|
||||
pd_conn_t* pd_new_connection(pcapdroid_t *pd, const zdtun_5tuple_t *tuple, int uid);
|
||||
void pd_destroy_connection(pd_conn_t *data);
|
||||
|
||||
+1
-1
Submodule submodules/zdtun updated: 2c2f8febb7...3e5e749215
Reference in New Issue
Block a user