diff --git a/app/src/main/jni/CMakeLists.txt b/app/src/main/jni/CMakeLists.txt
index 14512bfa..74f47613 100644
--- a/app/src/main/jni/CMakeLists.txt
+++ b/app/src/main/jni/CMakeLists.txt
@@ -13,6 +13,10 @@ set(ZDTUN_ROOT ${ROOTDIR}/submodules/zdtun)
include_directories(${ZDTUN_ROOT})
add_subdirectory(${ZDTUN_ROOT} zdtun_build)
+# nDPI
+set(NDPI_ROOT ${ROOTDIR}/submodules/nDPI)
+include_directories(${NDPI_ROOT}/src/include ${NDPI_ROOT}/src/lib/third_party/include)
+
# base
include_directories(${CMAKE_CURRENT_SOURCE_DIR})
diff --git a/app/src/main/jni/common/utils.c b/app/src/main/jni/common/utils.c
index c4bbb364..f72492df 100644
--- a/app/src/main/jni/common/utils.c
+++ b/app/src/main/jni/common/utils.c
@@ -108,6 +108,7 @@ ssize_t xwrite(int fd, const void *buf, size_t count) {
/* ******************************************************* */
+// returns < 0 on error, 0 if fd is closed
ssize_t xread(int fd, void *buf, size_t count) {
size_t sofar = 0;
ssize_t rv;
@@ -125,9 +126,9 @@ ssize_t xread(int fd, void *buf, size_t count) {
} while((sofar != count) && (rv != 0));
if(sofar != count)
- return -1;
+ return (rv == 0) ? 0 : -1;
- return 0;
+ return count;
}
/* ******************************************************* */
diff --git a/app/src/main/jni/core/CMakeLists.txt b/app/src/main/jni/core/CMakeLists.txt
index 949c2649..173fa906 100644
--- a/app/src/main/jni/core/CMakeLists.txt
+++ b/app/src/main/jni/core/CMakeLists.txt
@@ -12,9 +12,7 @@ add_library(capture SHARED
jni_impl.c)
# nDPI
-set(NDPI_ROOT ${ROOTDIR}/submodules/nDPI)
add_definitions(-DNDPI_LIB_COMPILATION)
-include_directories(${NDPI_ROOT}/src/include ${NDPI_ROOT}/src/lib/third_party/include)
AUX_SOURCE_DIRECTORY(${NDPI_ROOT}/src/lib ndpiSources)
AUX_SOURCE_DIRECTORY(${NDPI_ROOT}/src/lib/third_party/src ndpiSources)
AUX_SOURCE_DIRECTORY(${NDPI_ROOT}/src/lib/protocols ndpiSources)
diff --git a/app/src/main/jni/core/blacklist.c b/app/src/main/jni/core/blacklist.c
index 773d2dc6..ea501af4 100644
--- a/app/src/main/jni/core/blacklist.c
+++ b/app/src/main/jni/core/blacklist.c
@@ -253,6 +253,23 @@ bool blacklist_match_ip(blacklist_t *bl, const zdtun_ip_t *ip, int ipver) {
/* ******************************************************* */
+bool blacklist_match_ipstr(blacklist_t *bl, const char *ip_str) {
+ ndpi_ip_addr_t addr;
+ int ipver = ndpi_parse_ip_string(ip_str, &addr);
+ zdtun_ip_t ip;
+
+ if(ipver == 4)
+ ip.ip4 = addr.ipv4;
+ else if(ipver == 6)
+ memcpy(&ip.ip6, &addr.ipv6, 16);
+ else
+ return false;
+
+ return blacklist_match_ip(bl, &ip, ipver);
+}
+
+/* ******************************************************* */
+
bool blacklist_match_domain(blacklist_t *bl, const char *domain) {
string_entry_t *entry = NULL;
@@ -337,4 +354,4 @@ int blacklist_load_list_descriptor(blacklist_t *bl, JNIEnv *env, jobject ld) {
return 0;
}
-#endif // ANDROID
\ No newline at end of file
+#endif // ANDROID
diff --git a/app/src/main/jni/core/blacklist.h b/app/src/main/jni/core/blacklist.h
index 4ffcfdcf..1fefa8f9 100644
--- a/app/src/main/jni/core/blacklist.h
+++ b/app/src/main/jni/core/blacklist.h
@@ -76,6 +76,7 @@ int blacklist_load_file(blacklist_t *bl, const char *path, blacklist_type btype,
int blacklist_load_list_descriptor(blacklist_t *bl, JNIEnv *env, jobject ld);
#endif
bool blacklist_match_ip(blacklist_t *bl, const zdtun_ip_t *ip, int ipver);
+bool blacklist_match_ipstr(blacklist_t *bl, const char *ip);
bool blacklist_match_domain(blacklist_t *bl, const char *domain);
bool blacklist_match_uid(blacklist_t *bl, int uid);
void blacklist_get_stats(const blacklist_t *bl, blacklists_stats_t *stats);
diff --git a/app/src/main/jni/core/capture_root.c b/app/src/main/jni/core/capture_root.c
index 4c3e5e48..65ed0795 100644
--- a/app/src/main/jni/core/capture_root.c
+++ b/app/src/main/jni/core/capture_root.c
@@ -45,7 +45,7 @@ struct pcap_conn {
/* ******************************************************* */
-static int su_cmd(const char *prog, const char *args, bool check_error) {
+static int run_cmd(const char *prog, const char *args, bool as_root, bool check_error) {
int in_p[2], out_p[2];
int rv = -1;
pid_t pid;
@@ -57,7 +57,7 @@ static int su_cmd(const char *prog, const char *args, bool check_error) {
if((pid = fork()) == 0) {
// child
- char *argp[] = {"sh", "-c", "su", NULL};
+ char *argp[] = {"sh", "-c", as_root ? "su" : "sh", NULL};
close(in_p[1]);
close(out_p[0]);
@@ -76,7 +76,7 @@ static int su_cmd(const char *prog, const char *args, bool check_error) {
close(out_p[1]);
// write "su" command input
- log_d("su_cmd[%d]: %s %s", pid, prog, args);
+ log_d("run_cmd[%d]: %s %s", pid, prog, args);
write(in_p[1], prog, strlen(prog));
write(in_p[1], " ", 1);
write(in_p[1], args, strlen(args));
@@ -133,7 +133,7 @@ static void kill_pcapd(pcapdroid_t *nc) {
if(pid != 0) {
log_d("Killing old pcapd with pid %d", pid);
- su_cmd("kill", pid_s, false);
+ run_cmd("kill", pid_s, true, false);
}
fclose(f);
@@ -144,15 +144,8 @@ static void kill_pcapd(pcapdroid_t *nc) {
static int connectPcapd(pcapdroid_t *pd) {
int sock;
int client = -1;
- char bpf[256];
char pcapd[PATH_MAX];
- char capture_interface[16] = "@inet";
- bpf[0] = '\0';
-
-#if ANDROID
- getStringPref(pd, "getPcapDumperBpf", bpf, sizeof(bpf));
- getStringPref(pd, "getCaptureInterface", capture_interface, sizeof(capture_interface));
-#endif
+ char *bpf = pd->root.bpf ? pd->root.bpf : "";
if(pd->cb.get_libprog_path)
pd->cb.get_libprog_path(pd, "pcapd", pcapd, sizeof(pcapd));
@@ -198,8 +191,8 @@ static int connectPcapd(pcapdroid_t *pd) {
// Start the daemon
char args[256];
- snprintf(args, sizeof(args), "-l pcapd.log -i %s -d -u %d -t -b \"%s\"", capture_interface, pd->app_filter, bpf);
- if(su_cmd(pcapd, args, true) != 0)
+ snprintf(args, sizeof(args), "-l pcapd.log -i %s -d -u %d -t -b \"%s\"", pd->root.capture_interface, pd->app_filter, bpf);
+ if(run_cmd(pcapd, args, pd->root.as_root, true) != 0)
goto cleanup;
// Wait for pcapd to start
@@ -497,6 +490,16 @@ int run_root(pcapdroid_t *pd) {
u_int64_t next_purge_ms;
zdtun_callbacks_t callbacks = {.send_client = (void*)1};
+#if ANDROID
+ char capture_interface[16] = "@inet";
+ char bpf[256];
+ bpf[0] = '\0';
+
+ pd->root.as_root = true; // TODO support read from PCAP file
+ pd->root.bpf = getStringPref(pd, "getPcapDumperBpf", bpf, sizeof(bpf));
+ pd->root.capture_interface = getStringPref(pd, "getCaptureInterface", capture_interface, sizeof(capture_interface));
+#endif
+
if((pd->zdt = zdtun_init(&callbacks, NULL)) == NULL)
return(-1);
@@ -531,15 +534,17 @@ int run_root(pcapdroid_t *pd) {
if(!FD_ISSET(sock, &fdset))
goto housekeeping;
- if(xread(sock, &hdr, sizeof(hdr)) < 0) {
- log_e("read hdr from pcapd failed[%d]: %s", errno, strerror(errno));
+ ssize_t xrv = xread(sock, &hdr, sizeof(hdr));
+ if(xrv != sizeof(hdr)) {
+ if(xrv < 0)
+ log_e("read hdr from pcapd failed[%d]: %s", errno, strerror(errno));
goto cleanup;
}
if(hdr.len > sizeof(buffer)) {
log_e("packet too big (%d B)", hdr.len);
goto cleanup;
}
- if(xread(sock, buffer, hdr.len) < 0) {
+ if(xread(sock, buffer, hdr.len) != hdr.len) {
log_e("read %d B packet from pcapd failed[%d]: %s", hdr.len, errno, strerror(errno));
goto cleanup;
}
diff --git a/app/src/main/jni/core/jni_impl.c b/app/src/main/jni/core/jni_impl.c
index 236ac8f2..c1a7e3ed 100644
--- a/app/src/main/jni/core/jni_impl.c
+++ b/app/src/main/jni/core/jni_impl.c
@@ -473,10 +473,8 @@ Java_com_emanuelef_remote_1capture_CaptureService_runPacketLoop(JNIEnv *env, jcl
.notify_service_status = notifyServiceStatus,
.notify_blacklists_loaded = notifyBlacklistsLoaded,
},
- .ip_to_host = ip_lru_init(MAX_HOST_LRU_SIZE),
.app_filter = getIntPref(env, vpn, "getAppFilterUid"),
.root_capture = (bool) getIntPref(env, vpn, "isRootCapture"),
- .new_conn_id = 0,
.pcap_dump = {
.enabled = (bool) getIntPref(env, vpn, "pcapDumpEnabled"),
},
@@ -792,4 +790,4 @@ void getApplicationByUid(pcapdroid_t *pd, jint uid, char *buf, int bufsize) {
if(obj) (*env)->DeleteLocalRef(env, obj);
}
-#endif // ANDROID
\ No newline at end of file
+#endif // ANDROID
diff --git a/app/src/main/jni/core/pcapdroid.c b/app/src/main/jni/core/pcapdroid.c
index 7c20286c..90399919 100644
--- a/app/src/main/jni/core/pcapdroid.c
+++ b/app/src/main/jni/core/pcapdroid.c
@@ -1039,6 +1039,8 @@ int pd_run(pcapdroid_t *pd) {
return(-1);
}
+ pd->ip_to_host = ip_lru_init(MAX_HOST_LRU_SIZE);
+
if(pd->malware_detection.enabled && pd->cb.load_blacklists_info)
pd->cb.load_blacklists_info(pd);
@@ -1075,6 +1077,10 @@ int pd_run(pcapdroid_t *pd) {
log_d("Stopped packet loop");
+ // send last dump
+ if(pd->cb.send_connections_dump)
+ pd->cb.send_connections_dump(pd);
+
conns_clear(&pd->new_conns, true);
conns_clear(&pd->conns_updates, true);
diff --git a/app/src/main/jni/core/pcapdroid.h b/app/src/main/jni/core/pcapdroid.h
index 06a38e5a..d662af46 100644
--- a/app/src/main/jni/core/pcapdroid.h
+++ b/app/src/main/jni/core/pcapdroid.h
@@ -138,7 +138,7 @@ struct pcapdroid;
// Used to decouple pcapdroid.c from the JNI calls
typedef struct {
void (*get_libprog_path)(struct pcapdroid *pd, const char *prog_name, char *buf, int bufsize);
- int (*load_blacklists_info)(struct pcapdroid *pd);
+ int (*load_blacklists_info)(struct pcapdroid *pd);
void (*send_stats_dump)(struct pcapdroid *pd);
void (*send_connections_dump)(struct pcapdroid *pd);
void (*send_pcap_dump)(struct pcapdroid *pd);
@@ -193,7 +193,10 @@ typedef struct pcapdroid {
} vpn;
struct {
pcap_conn_t *connections;
- } root;
+ bool as_root;
+ char *bpf;
+ char *capture_interface;
+ } root; // TODO rename: it can run without root to read a PCAP file
};
struct {
diff --git a/app/src/main/jni/pcapd/pcapd.c b/app/src/main/jni/pcapd/pcapd.c
index 060fe7bb..5f4e4db9 100644
--- a/app/src/main/jni/pcapd/pcapd.c
+++ b/app/src/main/jni/pcapd/pcapd.c
@@ -73,6 +73,7 @@ typedef struct {
char name[IFNAMSIZ];
int ifidx;
uint8_t ifid; // positional interface index
+ uint8_t is_file;
pcap_t *pd;
int pf;
int dlink;
@@ -395,22 +396,28 @@ static void init_interface(pcapd_iface_t *iface) {
static int open_interface(pcapd_iface_t *iface, pcapd_runtime_t *rt, const char *ifname, int ifid) {
#ifndef READ_FROM_PCAP
+ int is_file = 0;
int mtu = get_iface_mtu(ifname);
- if(mtu < 0) {
- mtu = 1500;
- log_w("Could not get \"%s\" interface MTU, assuming %d", ifname, mtu);
- }
-
/* The snaplen includes the datalink overhead. Max datalink overhead (SLL2): 20 B */
int snaplen = mtu + SLL2_HDR_LEN;
- log_d("Using a %d snaplen (MTU %d)", snaplen, mtu);
pcap_t *pd = pcap_open_live(ifname, snaplen, 0, 1, errbuf);
-
if(!pd) {
- log_i("pcap_open_live(%s) failed: %s", ifname, errbuf);
- return -1;
+ // try to open as file
+ pd = pcap_open_offline(ifname, errbuf);
+
+ if(!pd) {
+ log_i("pcap_open(%s) failed: %s", ifname, errbuf);
+ return -1;
+ }
+ is_file = 1;
+ } else {
+ if(mtu < 0) {
+ mtu = 1500;
+ log_w("Could not get \"%s\" interface MTU, assuming %d", ifname, mtu);
+ }
+ log_d("Using a %d snaplen (MTU %d)", snaplen, mtu);
}
// Fixes pcap_next_ex sometimes hanging on interface down
@@ -479,20 +486,21 @@ static int open_interface(pcapd_iface_t *iface, pcapd_runtime_t *rt, const char
// Success
iface->pd = pd;
+ iface->is_file = is_file;
iface->mac = 0;
iface->ifid = ifid;
iface->ifidx = if_nametoindex(ifname);
errno = 0;
- if((dlink == DLT_EN10MB) && (get_iface_mac(ifname, &iface->mac) < 0))
+ if(!is_file && (dlink == DLT_EN10MB) && (get_iface_mac(ifname, &iface->mac) < 0))
log_i("Could not get interface \"%s\" MAC[%d]: %s", ifname, errno, strerror(errno));
uint32_t netmask;
iface->ip = 0;
- if(get_iface_ip(ifname, &iface->ip, &netmask) < 0)
+ if(!is_file && get_iface_ip(ifname, &iface->ip, &netmask) < 0)
log_i("Could not get interface \"%s\" IP[%d]: %s", ifname, errno, strerror(errno));
- if(get_iface_ip6(ifname, &iface->ip6) < 0) {
+ if(!is_file && get_iface_ip6(ifname, &iface->ip6) < 0) {
log_i("Could not get interface \"%s\" IPv6[%d]: %s", ifname, errno, strerror(errno));
memset(&iface->ip6, 0, sizeof(iface->ip6));
}
@@ -744,9 +752,12 @@ static int read_pkt(pcapd_runtime_t *rt, pcapd_iface_t *iface, time_t now) {
// abort, resuming other interfaces is not supported yet
return -1;
+ } else if(rv != 1) {
+ // TODO handle EOF without error
+ return -1;
}
- if((rv == 1) && (hdr->caplen >= to_skip)) {
+ if(hdr->caplen >= to_skip) {
pcapd_hdr_t phdr;
zdtun_pkt_t zpkt;
int len = hdr->caplen;
@@ -764,11 +775,15 @@ static int read_pkt(pcapd_runtime_t *rt, pcapd_iface_t *iface, time_t now) {
tupleSwapPeers(&zpkt.tuple);
}
- int uid = uid_lru_find(rt->lru, &zpkt.tuple);
+ int uid = UID_UNKNOWN;
- if(uid == -2) {
- uid = get_uid(rt->resolver, &zpkt.tuple);
- uid_lru_add(rt->lru, &zpkt.tuple, uid);
+ if(!iface->is_file) {
+ uid = uid_lru_find(rt->lru, &zpkt.tuple);
+
+ if(uid == -2) {
+ uid = get_uid(rt->resolver, &zpkt.tuple);
+ uid_lru_add(rt->lru, &zpkt.tuple, uid);
+ }
}
if((rt->conf->uid_filter == -1) || (rt->conf->uid_filter == uid)) {
@@ -1011,6 +1026,9 @@ int main(int argc, char *argv[]) {
rv = run_pcap_dump(&conf);
unlink(PCAPD_PID);
+ for(int i=0; i.
+ *
+ * Copyright 2022 - Emanuele Faranda
+ */
+
+#include "test_utils.h"
+
+/* ******************************************************* */
+
+static void test_match() {
+ blacklist_t *bl = blacklist_init();
+ assert(bl != NULL);
+
+ // Load blacklist
+ assert0(blacklist_add_domain(bl, "example.org"));
+ assert0(blacklist_add_ipstr(bl, "1.2.3.4"));
+ assert0(blacklist_add_ipstr(bl, "::2"));
+ assert0(blacklist_add_uid(bl, 777));
+ assert0(blacklist_add_uid(bl, 888));
+
+ // Use blacklist
+ assert1(blacklist_match_domain(bl, "www.example.org"));
+ //assert1(blacklist_match_domain(bl, "some.example.org")); // TODO support subdomains matching
+
+ assert0(blacklist_match_ipstr(bl, "1.2.3.0"));
+ assert1(blacklist_match_ipstr(bl, "1.2.3.4"));
+
+ assert0(blacklist_match_ipstr(bl, "::1"));
+ assert1(blacklist_match_ipstr(bl, "::2"));
+
+ assert0(blacklist_match_uid(bl, 0));
+ assert0(blacklist_match_uid(bl, 999));
+ assert1(blacklist_match_uid(bl, 777));
+
+ blacklist_destroy(bl);
+}
+
+/* ******************************************************* */
+
+int main(int argc, char **argv) {
+ add_test("match", test_match);
+ run_test(argc, argv);
+
+ return 0;
+}
diff --git a/app/src/main/jni/tests/pcap_reader.c b/app/src/main/jni/tests/pcap_reader.c
new file mode 100644
index 00000000..1c9b4dfc
--- /dev/null
+++ b/app/src/main/jni/tests/pcap_reader.c
@@ -0,0 +1,104 @@
+/*
+ * This file is part of PCAPdroid.
+ *
+ * PCAPdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PCAPdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PCAPdroid. If not, see .
+ *
+ * Copyright 2022 - Emanuele Faranda
+ */
+
+#include
+#include
+#include "test_utils.h"
+#include "common/utils.h"
+
+/* ******************************************************* */
+
+static void usage() {
+ fprintf(stderr, "pcap_reader - test for PCAPdroid\n"
+ "Copyright 2022 Emanuele Faranda \n\n"
+ "Usage: pcap_reader -i ifname\n"
+ " -i [ifname] capture packets on the specified interface or PCAP file.\n"
+ " The '@inet' keyword can be used to capture from the internet\n"
+ " interface\n"
+ );
+
+ exit(1);
+}
+
+/* ******************************************************* */
+
+static void sig_handler(int signo) {
+ if(running) {
+ running = false;
+ return;
+ }
+
+ fprintf(stderr, "exit now");
+ exit(1);
+}
+
+/* ******************************************************* */
+
+static void dump_connections(pcapdroid_t *pd) {
+ char buf[256];
+
+ for(int i=0; i < pd->new_conns.cur_items; i++) {
+ conn_and_tuple_t *conn = &pd->new_conns.items[i];
+
+ zdtun_5tuple2str(&conn->tuple, buf, sizeof(buf));
+ printf("%s [%s]\n", buf,
+ //pd_get_proto_name(pd, conn->data->l7proto, conn->tuple.ipproto),
+ conn->data->info ? conn->data->info : "");
+ }
+}
+
+/* ******************************************************* */
+
+int main(int argc, char *argv[]) {
+ int c;
+ char *ifname = NULL;
+ uint8_t verbose = 0;
+
+ while((c = getopt(argc, argv, "hi:v")) != -1) {
+ switch(c) {
+ case 'i':
+ ifname = strdup(optarg);
+ break;
+ case 'v':
+ verbose = 1;
+ break;
+ default:
+ usage();
+ }
+ }
+
+ if(ifname == NULL)
+ usage();
+
+ loglevel = verbose ? ANDROID_LOG_DEBUG : ANDROID_LOG_INFO;
+
+ pcapdroid_t *pd = pd_init(ifname);
+ pd->cb.send_connections_dump = dump_connections;
+
+ signal(SIGINT, sig_handler);
+ signal(SIGTERM, sig_handler);
+ signal(SIGHUP, sig_handler);
+
+ log_i("Capturing packets from %s", ifname);
+ pd_run(pd);
+
+ log_i("Terminated");
+ pd_free(pd);
+ free(ifname);
+}
diff --git a/app/src/main/jni/tests/run_tests.sh b/app/src/main/jni/tests/run_tests.sh
new file mode 100755
index 00000000..5dd3a268
--- /dev/null
+++ b/app/src/main/jni/tests/run_tests.sh
@@ -0,0 +1,4 @@
+#!/bin/bash
+
+mkdir -p build
+cd build && cmake .. && make -j$(nproc) run_tests
diff --git a/app/src/main/jni/tests/test_utils.c b/app/src/main/jni/tests/test_utils.c
new file mode 100644
index 00000000..14ae2fac
--- /dev/null
+++ b/app/src/main/jni/tests/test_utils.c
@@ -0,0 +1,90 @@
+/*
+ * This file is part of PCAPdroid.
+ *
+ * PCAPdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PCAPdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PCAPdroid. If not, see .
+ *
+ * Copyright 2022 - Emanuele Faranda
+ */
+
+#include "test_utils.h"
+
+#define TEST_NAME_MAX_LENGTH 32
+#define MAX_TESTS 32
+
+typedef struct {
+ char name[TEST_NAME_MAX_LENGTH];
+ void (*cb)();
+} test_spec;
+
+static test_spec all_tests[MAX_TESTS] = {0};
+
+static void getPcapdPath(struct pcapdroid *pd, const char *prog_name, char *buf, int bufsize) {
+ snprintf(buf, bufsize, "main/pcapd/libpcapd.so");
+}
+
+/* ******************************************************* */
+
+void add_test(const char *name, void (*test_cb)()) {
+ int len = strlen(name);
+ assert(len < TEST_NAME_MAX_LENGTH);
+
+ for(int i=0; icb();
+}
+
+/* ******************************************************* */
+
+pcapdroid_t* pd_init(const char *ifname) {
+ pcapdroid_t *pd = calloc(1, sizeof(pcapdroid_t));
+ assert(pd != NULL);
+
+ pd->root_capture = true;
+ pd->root.capture_interface = (char*) ifname;
+ pd->root.as_root = false; // don't run as root
+ pd->app_filter = -1; // don't filter
+ pd->cb.get_libprog_path = getPcapdPath;
+
+ strcpy(pd->cachedir, ".");
+ pd->cachedir_len = 1;
+
+ return pd;
+}
diff --git a/app/src/main/jni/tests/test_utils.h b/app/src/main/jni/tests/test_utils.h
new file mode 100644
index 00000000..85708ffd
--- /dev/null
+++ b/app/src/main/jni/tests/test_utils.h
@@ -0,0 +1,38 @@
+/*
+ * This file is part of PCAPdroid.
+ *
+ * PCAPdroid is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, either version 3 of the License, or
+ * (at your option) any later version.
+ *
+ * PCAPdroid is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with PCAPdroid. If not, see .
+ *
+ * Copyright 2022 - Emanuele Faranda
+ */
+
+#ifndef __TEST_UTILS_H__
+#define __TEST_UTILS_H__
+
+#include "core/pcapdroid.h"
+#include
+
+#define assert0(x) assert((x) == 0)
+#define assert1(x) assert((x) == 1)
+
+void add_test(const char *name, void (*test_cb)());
+void run_test(int argc, char **argv);
+
+pcapdroid_t* pd_init(const char *ifname);
+
+static inline void pd_free(pcapdroid_t *pd) {
+ free(pd);
+}
+
+#endif