mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-06-16 21:10:57 +08:00
Show nDPI information
NOTE: the code is still buggy and crashes randomly due to unaligned accesses
This commit is contained in:
parent
03b6a391c1
commit
4e25aac8e6
@ -66,5 +66,6 @@ In order to run without root, the app takes advantage of the Android VPNService
|
||||
2. Clone https://github.com/emanuele-f/zdtun beside this repository
|
||||
3. Clone https://github.com/ntop/nDPI beside this repository
|
||||
4. Link the cmake file into nDPI: from this repo execute: `ln -s $(readlink -f nDPI/CMakeLists.txt) ../nDPI`
|
||||
5. Build the `zdtun` and `ndpi` modules first
|
||||
6. Then build the `app` module
|
||||
4. Inside the nDPI directory, run `./autogen.sh`
|
||||
6. Build the `zdtun` and `ndpi` modules first
|
||||
7. Then build the `app` module
|
||||
|
||||
@ -36,4 +36,5 @@ dependencies {
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.2.0'
|
||||
implementation project(path: ':zdtun')
|
||||
implementation 'cat.ereza:customactivityoncrash:2.2.0'
|
||||
implementation project(path: ':ndpi')
|
||||
}
|
||||
|
||||
@ -19,13 +19,15 @@ class ConnDescriptor implements Serializable {
|
||||
int sent_pkts;
|
||||
int rcvd_pkts;
|
||||
String info;
|
||||
String l7proto;
|
||||
int uid;
|
||||
int incr_id;
|
||||
|
||||
/* Invoked by native code */
|
||||
public void setData(int _ipproto, String _src_ip, String _dst_ip, int _src_port, int _dst_port,
|
||||
long _first_seen, long _last_seen, long _sent_bytes, long _rcvd_bytes,
|
||||
int _sent_pkts, int _rcvd_pkts, String _info, int _uid, int _incr_id) {
|
||||
int _sent_pkts, int _rcvd_pkts, String _info, String _l7proto, int _uid,
|
||||
int _incr_id) {
|
||||
/* Metadata */
|
||||
ipproto = _ipproto;
|
||||
src_ip = _src_ip;
|
||||
@ -41,6 +43,7 @@ class ConnDescriptor implements Serializable {
|
||||
sent_pkts = _sent_pkts;
|
||||
rcvd_pkts = _rcvd_pkts;
|
||||
info = _info;
|
||||
l7proto = _l7proto;
|
||||
uid = _uid;
|
||||
incr_id = _incr_id;
|
||||
}
|
||||
|
||||
@ -43,19 +43,21 @@ public class ConnectionsAdapter extends BaseAdapter {
|
||||
assert conn != null;
|
||||
ImageView icon = convertView.findViewById(R.id.icon);
|
||||
TextView remote = convertView.findViewById(R.id.remote);
|
||||
TextView l7proto = convertView.findViewById(R.id.l7proto);
|
||||
TextView traffic = convertView.findViewById(R.id.traffic);
|
||||
AppDescriptor app = mActivity.findAppByUid(conn.uid);
|
||||
Drawable appIcon;
|
||||
|
||||
if(app != null)
|
||||
appIcon = Objects.requireNonNull(app.getIcon().getConstantState()).newDrawable();
|
||||
else
|
||||
appIcon = mUnknownIcon;
|
||||
|
||||
appIcon = (app != null) ? Objects.requireNonNull(app.getIcon().getConstantState()).newDrawable() : mUnknownIcon;
|
||||
icon.setImageDrawable(appIcon);
|
||||
|
||||
remote.setText(String.format(mActivity.getResources().getString(R.string.ip_and_port),
|
||||
conn.dst_ip, conn.dst_port));
|
||||
if(conn.info.length() > 0)
|
||||
remote.setText(conn.info);
|
||||
else
|
||||
remote.setText(String.format(mActivity.getResources().getString(R.string.ip_and_port),
|
||||
conn.dst_ip, conn.dst_port));
|
||||
|
||||
l7proto.setText(conn.l7proto);
|
||||
traffic.setText(Utils.formatBytes(conn.sent_bytes + conn.rcvd_bytes));
|
||||
|
||||
return(convertView);
|
||||
|
||||
@ -154,7 +154,8 @@ public class MainActivity extends AppCompatActivity implements LoaderManager.Loa
|
||||
protected void onSaveInstanceState(Bundle outState) {
|
||||
super.onSaveInstanceState(outState);
|
||||
|
||||
getSupportFragmentManager().putFragment(outState, "ConnectionsFragment", mConnectionsFragment);
|
||||
if(mConnectionsFragment != null)
|
||||
getSupportFragmentManager().putFragment(outState, "ConnectionsFragment", mConnectionsFragment);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@ -8,7 +8,8 @@ add_library(vpnproxy-jni
|
||||
pcap)
|
||||
|
||||
include_directories(../../../..)
|
||||
include_directories(../../../../../../../src/zdtun)
|
||||
include_directories(../../../../../../zdtun)
|
||||
include_directories(../../../../../../nDPI/src/include)
|
||||
|
||||
find_library( log-lib
|
||||
log)
|
||||
@ -24,6 +25,13 @@ if (NOT zdtun-lib)
|
||||
message(FATAL_ERROR "zdtun not found!")
|
||||
endif()
|
||||
|
||||
set(ndpi-lib ${CMAKE_CURRENT_SOURCE_DIR}/../../../../../ndpi/build/intermediates/library_and_local_jars_jni/debug/${ANDROID_ABI}/libndpi.so)
|
||||
|
||||
if (NOT ndpi-lib)
|
||||
message(FATAL_ERROR "nDPI not found!")
|
||||
endif()
|
||||
|
||||
target_link_libraries( vpnproxy-jni
|
||||
${zdtun-lib}
|
||||
${ndpi-lib}
|
||||
${log-lib})
|
||||
|
||||
@ -21,10 +21,12 @@
|
||||
#include <netinet/ip.h>
|
||||
#include "vpnproxy.h"
|
||||
#include "pcap.h"
|
||||
#include "../../../../../../nDPI/src/include/ndpi_protocol_ids.h"
|
||||
|
||||
#define VPN_TAG "VPNProxy"
|
||||
#define CAPTURE_STATS_UPDATE_FREQUENCY_MS 300
|
||||
#define CONNECTION_DUMP_UPDATE_FREQUENCY_MS 3000
|
||||
#define MAX_DPI_PACKETS 12
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
@ -41,12 +43,18 @@ typedef struct dns_packet {
|
||||
uint16_t additional_rrs;
|
||||
uint8_t initial_dot; // just skip
|
||||
uint8_t queries[];
|
||||
} dns_packet_t __attribute__((packed));
|
||||
} __attribute__((packed)) dns_packet_t;
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
typedef struct conn_data {
|
||||
int incr_id; /* an incremental identifier */
|
||||
|
||||
/* nDPI */
|
||||
struct ndpi_flow_struct *ndpi_flow;
|
||||
struct ndpi_id_struct *src_id, *dst_id;
|
||||
ndpi_protocol l7proto;
|
||||
|
||||
time_t first_seen;
|
||||
time_t last_seen;
|
||||
u_int64_t sent_bytes;
|
||||
@ -198,6 +206,84 @@ static char* getApplicationByUid(vpnproxy_data_t *proxy, int uid, char *buf, siz
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
struct ndpi_detection_module_struct* init_ndpi() {
|
||||
struct ndpi_detection_module_struct *ndpi = ndpi_init_detection_module();
|
||||
NDPI_PROTOCOL_BITMASK protocols;
|
||||
|
||||
if(!ndpi)
|
||||
return(NULL);
|
||||
|
||||
// enable all the protocols
|
||||
NDPI_BITMASK_SET_ALL(protocols);
|
||||
|
||||
ndpi_set_protocol_detection_bitmask2(ndpi, &protocols);
|
||||
ndpi_finalize_initalization(ndpi);
|
||||
|
||||
return(ndpi);
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
void free_ndpi(conn_data_t *data) {
|
||||
if(data->ndpi_flow) {
|
||||
ndpi_free_flow(data->ndpi_flow);
|
||||
data->ndpi_flow = NULL;
|
||||
}
|
||||
if(data->src_id) {
|
||||
ndpi_free(data->src_id);
|
||||
data->src_id = NULL;
|
||||
}
|
||||
if(data->dst_id) {
|
||||
ndpi_free(data->dst_id);
|
||||
data->dst_id = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
const char *getL7ProtoName(struct ndpi_detection_module_struct *mod, ndpi_protocol l7proto) {
|
||||
return ndpi_get_proto_name(mod, l7proto.master_protocol);
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void process_ndpi_packet(conn_data_t *data, vpnproxy_data_t *proxy, const char *packet,
|
||||
ssize_t size, uint8_t from_tap) {
|
||||
bool giveup = ((data->sent_pkts + data->rcvd_pkts) >= MAX_DPI_PACKETS);
|
||||
|
||||
data->l7proto = ndpi_detection_process_packet(proxy->ndpi, data->ndpi_flow, packet, size, data->last_seen,
|
||||
from_tap ? data->src_id : data->dst_id,
|
||||
from_tap ? data->dst_id : data->src_id);
|
||||
|
||||
if(giveup || ((data->l7proto.app_protocol != NDPI_PROTOCOL_UNKNOWN) &&
|
||||
(!ndpi_extra_dissection_possible(proxy->ndpi, data->ndpi_flow)))) {
|
||||
if (data->l7proto.app_protocol == NDPI_PROTOCOL_UNKNOWN) {
|
||||
uint8_t proto_guessed;
|
||||
|
||||
data->l7proto = ndpi_detection_giveup(proxy->ndpi, data->ndpi_flow, 1 /* Guess */,
|
||||
&proto_guessed);
|
||||
}
|
||||
|
||||
__android_log_print(ANDROID_LOG_DEBUG, VPN_TAG, "l7proto: app=%d, master=%d",
|
||||
data->l7proto.app_protocol, data->l7proto.master_protocol);
|
||||
|
||||
switch (data->l7proto.master_protocol) {
|
||||
case NDPI_PROTOCOL_DNS:
|
||||
case NDPI_PROTOCOL_HTTP:
|
||||
case NDPI_PROTOCOL_TLS:
|
||||
if (data->ndpi_flow->host_server_name[0]) {
|
||||
data->info = strdup(data->ndpi_flow->host_server_name);
|
||||
__android_log_print(ANDROID_LOG_DEBUG, VPN_TAG, "info: %s", data->info);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
free_ndpi(data);
|
||||
}
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void account_packet(zdtun_t *tun, const char *packet, ssize_t size, uint8_t from_tap, const zdtun_conn_t *conn_info) {
|
||||
struct sockaddr_in servaddr = {0};
|
||||
conn_data_t *data = (conn_data_t*)conn_info->user_data;
|
||||
@ -232,6 +318,9 @@ static void account_packet(zdtun_t *tun, const char *packet, ssize_t size, uint8
|
||||
|
||||
data->last_seen = time(NULL);
|
||||
|
||||
if(data->ndpi_flow)
|
||||
process_ndpi_packet(data, proxy, packet, size, from_tap);
|
||||
|
||||
if(((proxy->pcap_dump.uid_filter != -1) && (proxy->pcap_dump.uid_filter != uid))
|
||||
&& (!is_unknown_app || !proxy->pcap_dump.capture_unknown_app_traffic)) {
|
||||
//__android_log_print(ANDROID_LOG_DEBUG, VPN_TAG, "Discarding connection: UID=%d [filter=%d]", uid, proxy->pcap_dump.uid_filter);
|
||||
@ -323,6 +412,22 @@ static int handle_new_connection(zdtun_t *tun, const zdtun_conn_t *conn_info, vo
|
||||
return(1);
|
||||
}
|
||||
|
||||
/* nDPI */
|
||||
if((data->ndpi_flow = ndpi_flow_malloc(SIZEOF_FLOW_STRUCT)) == NULL) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, VPN_TAG, "ndpi_flow_malloc failed");
|
||||
free_ndpi(data);
|
||||
}
|
||||
|
||||
if((data->src_id = ndpi_malloc(SIZEOF_ID_STRUCT)) == NULL) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, VPN_TAG, "ndpi_malloc(src_id) failed");
|
||||
free_ndpi(data);
|
||||
}
|
||||
|
||||
if((data->dst_id = ndpi_malloc(SIZEOF_ID_STRUCT)) == NULL) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, VPN_TAG, "ndpi_malloc(dst_id) failed");
|
||||
free_ndpi(data);
|
||||
}
|
||||
|
||||
data->incr_id = proxy->incr_id++;
|
||||
data->first_seen = data->last_seen = time(NULL);
|
||||
data->uid = resolve_uid(proxy, conn_info);
|
||||
@ -342,6 +447,8 @@ static void destroy_connection(zdtun_t *tun, const zdtun_conn_t *conn_info) {
|
||||
return;
|
||||
}
|
||||
|
||||
free_ndpi(data);
|
||||
|
||||
if(data->info)
|
||||
free(data->info);
|
||||
|
||||
@ -497,6 +604,7 @@ static int connection_dumper(zdtun_t *tun, const zdtun_conn_t *conn_info, void *
|
||||
}
|
||||
|
||||
jobject info_string = (*env)->NewStringUTF(env, data->info ? data->info : "");
|
||||
jobject proto_string = (*env)->NewStringUTF(env, getL7ProtoName(proxy->ndpi, data->l7proto));
|
||||
jobject src_string = (*env)->NewStringUTF(env, srcip);
|
||||
jobject dst_string = (*env)->NewStringUTF(env, dstip);
|
||||
jobject conn_descriptor = (*env)->NewObject(env, dump_data->conn_cls, dump_data->conn_constructor);
|
||||
@ -513,7 +621,7 @@ static int connection_dumper(zdtun_t *tun, const zdtun_conn_t *conn_info, void *
|
||||
(*env)->CallVoidMethod(env, conn_descriptor, dump_data->conn_set_data,
|
||||
conn_info->ipproto, src_string, dst_string, ntohs(conn_info->src_port), ntohs(conn_info->dst_port),
|
||||
data->first_seen, data->last_seen, data->sent_bytes, data->rcvd_bytes,
|
||||
data->sent_pkts, data->rcvd_pkts, info_string, data->uid, data->incr_id);
|
||||
data->sent_pkts, data->rcvd_pkts, info_string, proto_string, data->uid, data->incr_id);
|
||||
|
||||
/* Add the connection to the array */
|
||||
(*env)->SetObjectArrayElement(env, dump_data->connections, dump_data->idx++, conn_descriptor);
|
||||
@ -551,7 +659,7 @@ static void sendConnectionsDump(zdtun_t *tun, vpnproxy_data_t *proxy) {
|
||||
|
||||
/* NOTE: must match ConnDescriptor::setData */
|
||||
dump_data.conn_set_data = (*env)->GetMethodID(env, dump_data.conn_cls, "setData",
|
||||
"(ILjava/lang/String;Ljava/lang/String;IIJJJJIILjava/lang/String;II)V");
|
||||
"(ILjava/lang/String;Ljava/lang/String;IIJJJJIILjava/lang/String;Ljava/lang/String;II)V");
|
||||
if(dump_data.conn_set_data == NULL) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, VPN_TAG, "GetMethodID(conn_set_data) failed");
|
||||
return;
|
||||
@ -613,7 +721,7 @@ static int connect_dumper(vpnproxy_data_t *proxy) {
|
||||
servaddr.sin_port = proxy->pcap_dump.collector_port;
|
||||
servaddr.sin_addr.s_addr = proxy->pcap_dump.collector_addr;
|
||||
|
||||
if(connect(dumper_socket, &servaddr, sizeof(servaddr)) < 0) {
|
||||
if(connect(dumper_socket, (struct sockaddr*)&servaddr, sizeof(servaddr)) < 0) {
|
||||
__android_log_print(ANDROID_LOG_ERROR, VPN_TAG,
|
||||
"connection to the PCAP receiver failed [%d]: %s", errno,
|
||||
strerror(errno));
|
||||
@ -665,6 +773,14 @@ static int run_tun(JNIEnv *env, jclass vpn, int tapfd, jint sdk) {
|
||||
send_header = true;
|
||||
running = 1;
|
||||
|
||||
/* nDPI */
|
||||
proxy.ndpi = init_ndpi();
|
||||
|
||||
if(proxy.ndpi == NULL) {
|
||||
__android_log_print(ANDROID_LOG_FATAL, VPN_TAG, "nDPI initialization failed");
|
||||
return(-1);
|
||||
}
|
||||
|
||||
signal(SIGPIPE, SIG_IGN);
|
||||
|
||||
// Set blocking
|
||||
@ -750,6 +866,7 @@ static int run_tun(JNIEnv *env, jclass vpn, int tapfd, jint sdk) {
|
||||
__android_log_print(ANDROID_LOG_DEBUG, VPN_TAG, "Stopped packet loop");
|
||||
|
||||
ztdun_finalize(tun);
|
||||
ndpi_exit_detection_module(proxy.ndpi);
|
||||
|
||||
if(dumper_socket > 0) {
|
||||
close(dumper_socket);
|
||||
|
||||
@ -20,6 +20,7 @@
|
||||
#include <jni.h>
|
||||
#include <android/log.h>
|
||||
#include "zdtun.h"
|
||||
#include "ndpi_api.h"
|
||||
|
||||
#ifndef REMOTE_CAPTURE_VPNPROXY_H
|
||||
#define REMOTE_CAPTURE_VPNPROXY_H
|
||||
@ -39,12 +40,12 @@ typedef struct vpnproxy_data {
|
||||
int incr_id;
|
||||
jint sdk;
|
||||
JNIEnv *env;
|
||||
jobject handler_cls; // TODO remove?
|
||||
jobject vpn_service;
|
||||
u_int32_t vpn_dns;
|
||||
u_int32_t public_dns;
|
||||
u_int32_t vpn_ipv4;
|
||||
bool dns_changed;
|
||||
struct ndpi_detection_module_struct *ndpi;
|
||||
|
||||
struct {
|
||||
u_int32_t collector_addr;
|
||||
|
||||
@ -1,19 +1,28 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
android:layout_width="match_parent"
|
||||
android:paddingHorizontal="10dp"
|
||||
android:paddingHorizontal="5dp"
|
||||
android:layout_height="40dp">
|
||||
|
||||
<ImageView
|
||||
android:layout_width="40dp"
|
||||
android:layout_height="match_parent"
|
||||
android:layout_marginEnd="10dp"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:id="@+id/icon"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="0.7"
|
||||
android:layout_weight="0.2"
|
||||
android:text="HTTP"
|
||||
android:gravity="center_vertical"
|
||||
android:layout_marginEnd="5dp"
|
||||
android:id="@+id/l7proto"/>
|
||||
|
||||
<TextView
|
||||
android:layout_width="0dp"
|
||||
android:layout_height="fill_parent"
|
||||
android:layout_weight="0.5"
|
||||
android:text="example.org:80"
|
||||
android:gravity="center_vertical"
|
||||
android:id="@+id/remote"/>
|
||||
|
||||
@ -9,9 +9,9 @@ add_definitions(-DNDPI_LIB_COMPILATION)
|
||||
|
||||
include_directories(./src/include ./src/lib/third_party/include)
|
||||
|
||||
AUX_SOURCE_DIRECTORY(./src/lib AllSources)
|
||||
AUX_SOURCE_DIRECTORY(./src/lib/third_party/src AllSources)
|
||||
AUX_SOURCE_DIRECTORY(./src/lib/protocols AllSources)
|
||||
AUX_SOURCE_DIRECTORY(./src/lib AllSources)
|
||||
|
||||
# libndpi.so target
|
||||
ADD_LIBRARY(ndpi SHARED ${AllSources})
|
||||
|
||||
Loading…
Reference in New Issue
Block a user