mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-05-08 21:12:26 +00:00
Add IPv6 support
IPv6 must be manually enabled from the preferences. However, when an IPv6 gateway is not available, the connections will fail with network unreachable. Closes #2
This commit is contained in:
@@ -61,7 +61,9 @@ import com.emanuelef.remote_capture.model.VPNStats;
|
||||
import java.io.FileNotFoundException;
|
||||
import java.io.IOException;
|
||||
import java.io.OutputStream;
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
public class CaptureService extends VpnService implements Runnable {
|
||||
private static final String TAG = "CaptureService";
|
||||
@@ -78,6 +80,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
private String tls_proxy_address;
|
||||
private Prefs.DumpMode dump_mode;
|
||||
private boolean tls_decryption_enabled;
|
||||
private boolean ipv6_enabled;
|
||||
private int collector_port;
|
||||
private int http_server_port;
|
||||
private int tls_proxy_port;
|
||||
@@ -99,9 +102,11 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
public static final int CONNECTIONS_LOG_SIZE = 8192;
|
||||
|
||||
public static final String FALLBACK_DNS_SERVER = "8.8.8.8";
|
||||
public static final String IPV6_DNS_SERVER = "2001:4860:4860::8888";
|
||||
|
||||
/* The IP address of the virtual network interface */
|
||||
public static final String VPN_IP_ADDRESS = "10.215.173.1";
|
||||
public static final String VPN_IP6_ADDRESS = "fd00:2:fd00:1:fd00:1:fd00:1";
|
||||
|
||||
/* The DNS server IP address to use to internally analyze the DNS requests.
|
||||
* It must be in the same subnet of the VPN network interface.
|
||||
@@ -180,6 +185,7 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
tls_proxy_address = Prefs.getTlsProxyAddress(prefs);
|
||||
tls_proxy_port = Prefs.getTlsProxyPort(prefs);
|
||||
dump_mode = Prefs.getDumpMode(prefs);
|
||||
ipv6_enabled = Prefs.getIPv6Enabled(prefs);
|
||||
last_bytes = 0;
|
||||
last_connections = 0;
|
||||
|
||||
@@ -231,6 +237,19 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
.addRoute("128.0.0.0", 1)
|
||||
.addDnsServer(vpn_dns);
|
||||
|
||||
if(ipv6_enabled) {
|
||||
builder.addAddress(VPN_IP6_ADDRESS, 128);
|
||||
|
||||
// Route unicast IPv6 addresses
|
||||
builder.addRoute("2000::", 3);
|
||||
|
||||
try {
|
||||
builder.addDnsServer(InetAddress.getByName(IPV6_DNS_SERVER));
|
||||
} catch (UnknownHostException e) {
|
||||
Log.w(TAG, "Could not set IPv6 DNS server");
|
||||
}
|
||||
}
|
||||
|
||||
if((app_filter != null) && (!app_filter.isEmpty())) {
|
||||
Log.d(TAG, "Setting app filter: " + app_filter);
|
||||
|
||||
@@ -498,6 +517,8 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
return(dns_server);
|
||||
}
|
||||
|
||||
public String getIpv6DnsServer() { return(IPV6_DNS_SERVER); }
|
||||
|
||||
public String getPcapCollectorAddress() {
|
||||
return(collector_address);
|
||||
}
|
||||
@@ -512,6 +533,8 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
|
||||
public int getTlsProxyPort() { return(tls_proxy_port); }
|
||||
|
||||
public int getIPv6Enabled() { return(ipv6_enabled ? 1 : 0); }
|
||||
|
||||
// returns 1 if dumpPcapData should be called
|
||||
public int dumpPcapToJava() {
|
||||
return(((dump_mode == Prefs.DumpMode.HTTP_SERVER) || (dump_mode == Prefs.DumpMode.PCAP_FILE)) ? 1 : 0);
|
||||
@@ -534,10 +557,8 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
InetSocketAddress local = new InetSocketAddress(saddr, sport);
|
||||
InetSocketAddress remote = new InetSocketAddress(daddr, dport);
|
||||
|
||||
Log.i(TAG, "Get uid local=" + local + " remote=" + remote);
|
||||
int uid = cm.getConnectionOwnerUid(protocol, local, remote);
|
||||
Log.i(TAG, "Get uid=" + uid);
|
||||
return uid;
|
||||
Log.d(TAG, "Get uid local=" + local + " remote=" + remote);
|
||||
return cm.getConnectionOwnerUid(protocol, local, remote);
|
||||
}
|
||||
|
||||
public void sendConnectionsDump(ConnectionDescriptor[] new_conns, ConnectionDescriptor[] conns_updates) {
|
||||
|
||||
@@ -257,8 +257,6 @@ public class ConnectionsRegister {
|
||||
|
||||
public synchronized String dumpConnectionsCsv(Context context, int uidFilter) {
|
||||
StringBuilder builder = new StringBuilder();
|
||||
String statusOpen = context.getString(R.string.conn_status_open);
|
||||
String statusClosed = context.getString(R.string.conn_status_closed);
|
||||
AppsResolver resolver = new AppsResolver(context);
|
||||
|
||||
// Header
|
||||
@@ -279,7 +277,7 @@ public class ConnectionsRegister {
|
||||
builder.append(conn.uid); builder.append(",");
|
||||
builder.append((app != null) ? app.getName() : ""); builder.append(",");
|
||||
builder.append(conn.l7proto); builder.append(",");
|
||||
builder.append(conn.closed ? statusClosed : statusOpen); builder.append(",");
|
||||
builder.append(conn.status); builder.append(",");
|
||||
builder.append((conn.info != null) ? conn.info : ""); builder.append(",");
|
||||
builder.append(conn.sent_bytes); builder.append(",");
|
||||
builder.append(conn.rcvd_bytes); builder.append(",");
|
||||
|
||||
+1
-5
@@ -116,11 +116,7 @@ public class ConnectionDetailsActivity extends AppCompatActivity {
|
||||
mDurationView.setText(Utils.formatDuration(conn.last_seen - conn.first_seen));
|
||||
mFirstSeen.setText(Utils.formatEpochFull(this, conn.first_seen));
|
||||
mLastSeen.setText(Utils.formatEpochFull(this, conn.last_seen));
|
||||
|
||||
if(conn.closed)
|
||||
mStatus.setText(R.string.conn_status_closed);
|
||||
else
|
||||
mStatus.setText(R.string.conn_status_open);
|
||||
mStatus.setText(conn.status);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -53,12 +53,15 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
|
||||
|
||||
public static class ViewHolder extends RecyclerView.ViewHolder {
|
||||
ImageView icon;
|
||||
View statusInd;
|
||||
TextView statusInd;
|
||||
TextView remote;
|
||||
TextView l7proto;
|
||||
TextView traffic;
|
||||
TextView appName;
|
||||
TextView eta;
|
||||
final String mStatusOpen;
|
||||
final String mStatusError;
|
||||
final String mProtoAndPort;
|
||||
|
||||
ViewHolder(View itemView) {
|
||||
super(itemView);
|
||||
@@ -70,6 +73,11 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
|
||||
statusInd = itemView.findViewById(R.id.status_ind);
|
||||
appName = itemView.findViewById(R.id.app_name);
|
||||
eta = itemView.findViewById(R.id.eta);
|
||||
|
||||
Context context = itemView.getContext();
|
||||
mStatusOpen = context.getString(R.string.conn_status_open);
|
||||
mStatusError = context.getString(R.string.error);
|
||||
mProtoAndPort = context.getString(R.string.proto_and_port);
|
||||
}
|
||||
|
||||
public void bindConn(Context context, ConnectionDescriptor conn, AppsResolver apps, Drawable unknownIcon) {
|
||||
@@ -86,22 +94,33 @@ public class ConnectionsAdapter extends RecyclerView.Adapter<ConnectionsAdapter.
|
||||
remote.setText(conn.dst_ip);
|
||||
|
||||
if(conn.dst_port != 0)
|
||||
l7Text = String.format(context.getResources().getString(R.string.proto_and_port), conn.l7proto, conn.dst_port);
|
||||
l7Text = String.format(mProtoAndPort, conn.l7proto, conn.dst_port);
|
||||
else
|
||||
l7Text = conn.l7proto;
|
||||
|
||||
if(conn.ipver == 6)
|
||||
l7Text = l7Text + ", IPv6";
|
||||
|
||||
l7proto.setText(l7Text);
|
||||
|
||||
String info_txt = (app != null) ? app.getName() : Integer.toString(conn.uid);
|
||||
appName.setText(info_txt);
|
||||
traffic.setText(Utils.formatBytes(conn.sent_bytes + conn.rcvd_bytes));
|
||||
|
||||
if(conn.closed) {
|
||||
if(conn.status.equals("CLOSED")) {
|
||||
eta.setText(Utils.formatEpochShort(context, conn.first_seen));
|
||||
|
||||
eta.setVisibility(View.VISIBLE);
|
||||
statusInd.setVisibility(View.GONE);
|
||||
} else {
|
||||
if(conn.status.equals("ERROR")) {
|
||||
statusInd.setText(mStatusError);
|
||||
statusInd.setTextColor(0xFFF20015);
|
||||
} else {
|
||||
statusInd.setText(mStatusOpen);
|
||||
statusInd.setTextColor(0xFF28BC36);
|
||||
}
|
||||
|
||||
eta.setVisibility(View.GONE);
|
||||
statusInd.setVisibility(View.VISIBLE);
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@ import java.io.Serializable;
|
||||
/* Equivalent of zdtun_conn_t from zdtun and conn_data_t from vpnproxy.c */
|
||||
public class ConnectionDescriptor implements Serializable {
|
||||
/* Metadata */
|
||||
public int ipver;
|
||||
public int ipproto;
|
||||
public String src_ip;
|
||||
public String dst_ip;
|
||||
@@ -42,16 +43,17 @@ public class ConnectionDescriptor implements Serializable {
|
||||
public String l7proto;
|
||||
public int uid;
|
||||
public int incr_id;
|
||||
public boolean closed;
|
||||
public String status;
|
||||
|
||||
/* Invoked by native code
|
||||
* NOTE: interleaving String and int in the parameters is not good as it makes the app crash
|
||||
* nto the emulator! Better to put the strings first. */
|
||||
public void setData(String _src_ip, String _dst_ip, String _info, String _url, String _l7proto,
|
||||
int _ipproto, int _src_port, int _dst_port, long _first_seen, long _last_seen,
|
||||
public void setData(String _src_ip, String _dst_ip, String _status, String _info, String _url, String _l7proto,
|
||||
int _ipver, int _ipproto, int _src_port, int _dst_port, long _first_seen, long _last_seen,
|
||||
long _sent_bytes, long _rcvd_bytes, int _sent_pkts, int _rcvd_pkts, int _uid,
|
||||
int _incr_id, boolean _closed) {
|
||||
int _incr_id) {
|
||||
/* Metadata */
|
||||
ipver = _ipver;
|
||||
ipproto = _ipproto;
|
||||
src_ip = _src_ip;
|
||||
dst_ip = _dst_ip;
|
||||
@@ -65,11 +67,11 @@ public class ConnectionDescriptor implements Serializable {
|
||||
rcvd_bytes = _rcvd_bytes;
|
||||
sent_pkts = _sent_pkts;
|
||||
rcvd_pkts = _rcvd_pkts;
|
||||
status = _status;
|
||||
info = _info;
|
||||
url = _url;
|
||||
l7proto = _l7proto;
|
||||
uid = _uid;
|
||||
incr_id = _incr_id;
|
||||
closed = _closed;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -35,6 +35,7 @@ public class Prefs {
|
||||
public static final String PREF_PCAP_DUMP_MODE = "pcap_dump_mode";
|
||||
public static final String PREF_PCAP_URI = "pcap_path";
|
||||
public static final String DEFAULT_DUMP_MODE = DUMP_HTTP_SERVER;
|
||||
public static final String PREF_IPV6_ENABLED = "ipv6_enabled";
|
||||
|
||||
public enum DumpMode {
|
||||
NONE,
|
||||
@@ -63,4 +64,5 @@ public class Prefs {
|
||||
public static String getTlsProxyAddress(SharedPreferences p) { return(p.getString(PREF_TLS_PROXY_IP_KEY, "0.0.0.0")); }
|
||||
public static int getTlsProxyPort(SharedPreferences p) { return(Integer.parseInt(p.getString(Prefs.PREF_TLS_PROXY_PORT_KEY, "8080"))); }
|
||||
public static String getAppFilter(SharedPreferences p) { return(p.getString(PREF_APP_FILTER, "")); }
|
||||
public static boolean getIPv6Enabled(SharedPreferences p) { return(p.getBoolean(PREF_IPV6_ENABLED, false)); }
|
||||
}
|
||||
|
||||
@@ -75,7 +75,8 @@ static jint get_uid_proc(int ipver, int ipproto, const char *conn_shex,
|
||||
if(!lines++)
|
||||
continue;
|
||||
|
||||
// Search string first, only then use slower scanf
|
||||
//log_android(ANDROID_LOG_INFO, "[try] %s", line);
|
||||
|
||||
if(sscanf(line, fmt, shex, &sport, dhex, &dport, &uid) == 5) {
|
||||
//log_android(ANDROID_LOG_DEBUG, "[try] %s:%d -> %s:%d [%d]", shex, sport, dhex, dport, uid);
|
||||
|
||||
@@ -96,7 +97,24 @@ static jint get_uid_proc(int ipver, int ipproto, const char *conn_shex,
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
// TODO support ipv6
|
||||
#if 0
|
||||
static char* tohex(const uint8_t *src, int srcsize, char *dst, int dstsize) {
|
||||
static const char *hex = "0123456789ABCDEF";
|
||||
int j = 0;
|
||||
|
||||
for(int i=0; (i < srcsize) && (j+2 < dstsize); i++) {
|
||||
dst[j++] = hex[(src[i] >> 4)];
|
||||
dst[j++] = hex[(src[i] & 0x0F)];
|
||||
}
|
||||
|
||||
dst[j] = '\0';
|
||||
|
||||
return dst;
|
||||
}
|
||||
#endif
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static jint get_uid_slow(const zdtun_5tuple_t *conn_info) {
|
||||
char shex[33], dhex[33];
|
||||
jint rv;
|
||||
@@ -106,16 +124,28 @@ static jint get_uid_slow(const zdtun_5tuple_t *conn_info) {
|
||||
u_int16_t sport = ntohs(conn_info->src_port);
|
||||
u_int16_t dport = ntohs(conn_info->dst_port);
|
||||
|
||||
sprintf(shex, "%08X", conn_info->src_ip);
|
||||
sprintf(dhex, "%08X", conn_info->dst_ip);
|
||||
if(conn_info->ipver == 4) {
|
||||
sprintf(shex, "%08X", conn_info->src_ip.ip4);
|
||||
sprintf(dhex, "%08X", conn_info->dst_ip.ip4);
|
||||
|
||||
rv = get_uid_proc(4, conn_info->ipproto, shex, dhex, sport, dport);
|
||||
rv = get_uid_proc(4, conn_info->ipproto, shex, dhex, sport, dport);
|
||||
|
||||
if (rv == UID_UNKNOWN) {
|
||||
// Search for IPv4-mapped IPv6 addresses
|
||||
// https://tools.ietf.org/html/rfc3493#section-3.7
|
||||
sprintf(shex, "0000000000000000FFFF0000%08X", conn_info->src_ip);
|
||||
sprintf(dhex, "0000000000000000FFFF0000%08X", conn_info->dst_ip);
|
||||
if (rv == UID_UNKNOWN) {
|
||||
// Search for IPv4-mapped IPv6 addresses
|
||||
// https://tools.ietf.org/html/rfc3493#section-3.7
|
||||
sprintf(shex, "0000000000000000FFFF0000%08X", conn_info->src_ip.ip4);
|
||||
sprintf(dhex, "0000000000000000FFFF0000%08X", conn_info->dst_ip.ip4);
|
||||
|
||||
rv = get_uid_proc(6, conn_info->ipproto, shex, dhex, sport, dport);
|
||||
}
|
||||
} else {
|
||||
const uint32_t *src = conn_info->src_ip.ip6.in6_u.u6_addr32;
|
||||
const uint32_t *dst = conn_info->dst_ip.ip6.in6_u.u6_addr32;
|
||||
|
||||
sprintf(shex, "%08X%08X%08X%08X", src[0], src[1], src[2], src[3]);
|
||||
sprintf(dhex, "%08X%08X%08X%08X", dst[0], dst[1], dst[2], dst[3]);
|
||||
|
||||
//log_android(ANDROID_LOG_INFO, "HEX %s %s", shex, dhex);
|
||||
|
||||
rv = get_uid_proc(6, conn_info->ipproto, shex, dhex, sport, dport);
|
||||
}
|
||||
@@ -128,12 +158,14 @@ static jint get_uid_slow(const zdtun_5tuple_t *conn_info) {
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
// TODO support IPv6
|
||||
static jint get_uid_q(uid_resolver_t *resolver,
|
||||
const zdtun_5tuple_t *conn_info) {
|
||||
JNIEnv *env = resolver->env;
|
||||
jint juid = UID_UNKNOWN;
|
||||
int version = 4;
|
||||
int version = conn_info->ipver;
|
||||
int family = (version == 4) ? AF_INET : AF_INET6;
|
||||
char srcip[INET6_ADDRSTRLEN];
|
||||
char dstip[INET6_ADDRSTRLEN];
|
||||
|
||||
// getUidQ only works for TCP/UDP connections
|
||||
if((conn_info->ipproto != IPPROTO_TCP) && (conn_info->ipproto != IPPROTO_UDP))
|
||||
@@ -151,12 +183,12 @@ static jint get_uid_q(uid_resolver_t *resolver,
|
||||
|
||||
u_int16_t sport = ntohs(conn_info->src_port);
|
||||
u_int16_t dport = ntohs(conn_info->dst_port);
|
||||
struct in_addr addr;
|
||||
|
||||
addr.s_addr = conn_info->src_ip;
|
||||
jstring jsource = (*env)->NewStringUTF(env, inet_ntoa(addr));
|
||||
addr.s_addr = conn_info->dst_ip;
|
||||
jstring jdest = (*env)->NewStringUTF(env, inet_ntoa(addr));
|
||||
inet_ntop(family, &conn_info->src_ip, srcip, sizeof(srcip));
|
||||
inet_ntop(family, &conn_info->dst_ip, dstip, sizeof(dstip));
|
||||
|
||||
jstring jsource = (*env)->NewStringUTF(env, srcip);
|
||||
jstring jdest = (*env)->NewStringUTF(env, dstip);
|
||||
|
||||
if((jsource != NULL) && (jdest != NULL)) {
|
||||
juid = (*env)->CallIntMethod(
|
||||
|
||||
@@ -17,8 +17,7 @@
|
||||
* Copyright 2020-21 - Emanuele Faranda
|
||||
*/
|
||||
|
||||
#include <netinet/udp.h>
|
||||
#include <netinet/ip.h>
|
||||
#include <ndpi_api.h>
|
||||
#include <ndpi_typedefs.h>
|
||||
#include "utils.c"
|
||||
#include "ndpi_master_protos.c"
|
||||
@@ -171,7 +170,7 @@ static void conns_clear(conn_array_t *arr, bool free_all) {
|
||||
for(int i=0; i < arr->cur_items; i++) {
|
||||
vpn_conn_t *slot = &arr->items[i];
|
||||
|
||||
if(slot->data && (slot->data->closed || free_all))
|
||||
if(slot->data && ((slot->data->status >= CONN_STATUS_CLOSED) || free_all))
|
||||
free_connection_data(slot->data);
|
||||
}
|
||||
|
||||
@@ -196,7 +195,7 @@ static u_int32_t getIPv4Pref(JNIEnv *env, jobject vpn_inst, const char *key) {
|
||||
log_android(ANDROID_LOG_DEBUG, "getIPv4Pref(%s) = %s", key, value);
|
||||
|
||||
if(inet_aton(value, &addr) == 0)
|
||||
log_android(ANDROID_LOG_ERROR, "%s() returned invalid address", key);
|
||||
log_android(ANDROID_LOG_ERROR, "%s() returned invalid IPv4 address", key);
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, obj, value);
|
||||
}
|
||||
@@ -208,6 +207,29 @@ static u_int32_t getIPv4Pref(JNIEnv *env, jobject vpn_inst, const char *key) {
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static struct in6_addr getIPv6Pref(JNIEnv *env, jobject vpn_inst, const char *key) {
|
||||
struct in6_addr addr = {0};
|
||||
|
||||
jmethodID midMethod = jniGetMethodID(env, cls.vpn_service, key, "()Ljava/lang/String;");
|
||||
jstring obj = (*env)->CallObjectMethod(env, vpn_inst, midMethod);
|
||||
|
||||
if(!jniCheckException(env)) {
|
||||
const char *value = (*env)->GetStringUTFChars(env, obj, 0);
|
||||
log_android(ANDROID_LOG_DEBUG, "getIPv6Pref(%s) = %s", key, value);
|
||||
|
||||
if(inet_pton(AF_INET6, value, &addr) != 1)
|
||||
log_android(ANDROID_LOG_ERROR, "%s() returned invalid IPv6 address", key);
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, obj, value);
|
||||
}
|
||||
|
||||
(*env)->DeleteLocalRef(env, obj);
|
||||
|
||||
return(addr);
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static jint getIntPref(JNIEnv *env, jobject vpn_inst, const char *key) {
|
||||
jint value;
|
||||
jmethodID midMethod = jniGetMethodID(env, cls.vpn_service, key, "()I");
|
||||
@@ -298,9 +320,11 @@ const char *getProtoName(struct ndpi_detection_module_struct *mod, ndpi_protocol
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
static void process_ndpi_packet(conn_data_t *data, vpnproxy_data_t *proxy, const char *packet,
|
||||
int size, uint8_t from_tap) {
|
||||
static void process_ndpi_packet(conn_data_t *data, vpnproxy_data_t *proxy, const zdtun_conn_t *conn_info,
|
||||
const char *packet, int size, uint8_t from_tap) {
|
||||
bool giveup = ((data->sent_pkts + data->rcvd_pkts) >= MAX_DPI_PACKETS);
|
||||
const zdtun_5tuple_t *tuple = zdtun_conn_get_5tuple(conn_info);
|
||||
giveup = true; // todo fixme
|
||||
|
||||
data->l7proto = ndpi_detection_process_packet(proxy->ndpi, data->ndpi_flow, (const u_char *)packet,
|
||||
size, data->last_seen,
|
||||
@@ -309,7 +333,7 @@ static void process_ndpi_packet(conn_data_t *data, vpnproxy_data_t *proxy, const
|
||||
|
||||
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) {
|
||||
if(data->l7proto.app_protocol == NDPI_PROTOCOL_UNKNOWN) {
|
||||
uint8_t proto_guessed;
|
||||
|
||||
data->l7proto = ndpi_detection_giveup(proxy->ndpi, data->ndpi_flow, 1 /* Guess */,
|
||||
@@ -319,8 +343,8 @@ static void process_ndpi_packet(conn_data_t *data, vpnproxy_data_t *proxy, const
|
||||
if(data->l7proto.master_protocol == 0)
|
||||
data->l7proto.master_protocol = data->l7proto.app_protocol;
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "l7proto: app=%d, master=%d",
|
||||
data->l7proto.app_protocol, data->l7proto.master_protocol);
|
||||
log_android(ANDROID_LOG_DEBUG, "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:
|
||||
@@ -380,7 +404,7 @@ static bool shouldIgnoreConn(vpnproxy_data_t *proxy, const zdtun_5tuple_t *tuple
|
||||
#endif
|
||||
|
||||
// ignore some internal communications, e.g. DNS-over-TLS check on port 853
|
||||
if((tuple->dst_ip == proxy->vpn_dns) && (ntohs(tuple->dst_port) != 53))
|
||||
if((tuple->ipver == 4) && (tuple->dst_ip.ip4 == proxy->vpn_dns) && (ntohs(tuple->dst_port) != 53))
|
||||
return true;
|
||||
|
||||
return false;
|
||||
@@ -417,9 +441,10 @@ static void account_packet(zdtun_t *tun, const char *packet, int size, uint8_t f
|
||||
}
|
||||
|
||||
data->last_seen = time(NULL);
|
||||
data->status = zdtun_conn_get_status(conn_info);
|
||||
|
||||
if(data->ndpi_flow)
|
||||
process_ndpi_packet(data, proxy, packet, size, from_tap);
|
||||
process_ndpi_packet(data, proxy, conn_info, packet, size, from_tap);
|
||||
|
||||
if(shouldIgnoreConn(proxy, zdtun_conn_get_5tuple(conn_info), data)) {
|
||||
//log_android(ANDROID_LOG_DEBUG, "Ignoring connection: UID=%d [filter=%d]", data->uid, proxy->uid_filter);
|
||||
@@ -559,7 +584,7 @@ static void destroy_connection(zdtun_t *tun, const zdtun_conn_t *conn_info) {
|
||||
|
||||
/* Will free the other data in sendConnectionsDump */
|
||||
free_ndpi(data);
|
||||
data->closed = true;
|
||||
data->status = zdtun_conn_get_status(conn_info);
|
||||
|
||||
if(!data->pending_notification && !shouldIgnoreConn(proxy, zdtun_conn_get_5tuple(conn_info), data)) {
|
||||
// Send last notification
|
||||
@@ -572,32 +597,10 @@ static void destroy_connection(zdtun_t *tun, const zdtun_conn_t *conn_info) {
|
||||
|
||||
/*
|
||||
* If the packet contains a DNS request then rewrite server address
|
||||
* with public DNS server.
|
||||
* with public DNS server. Non UDP DNS connections are dropped to block DoH queries which do not
|
||||
* allow us to extract the requested domain name.
|
||||
*/
|
||||
static int check_dns_req_dnat(struct vpnproxy_data *proxy, zdtun_pkt_t *pkt, zdtun_conn_t *conn) {
|
||||
struct dns_packet *dns_data;
|
||||
int dns_length;
|
||||
|
||||
// TODO support TCP
|
||||
if(pkt->tuple.ipproto != IPPROTO_UDP)
|
||||
return(0);
|
||||
|
||||
dns_length = pkt->l7_len;
|
||||
dns_data = (struct dns_packet*) pkt->l7;
|
||||
|
||||
if(dns_length < sizeof(struct dns_packet))
|
||||
return(0);
|
||||
|
||||
if((ntohs(pkt->udp->uh_dport) != 53) ||
|
||||
(pkt->tuple.dst_ip != proxy->vpn_dns) || (pkt->tuple.src_ip != proxy->vpn_ipv4))
|
||||
// Not VPN DNS
|
||||
return(0);
|
||||
|
||||
if((dns_data->flags & DNS_FLAGS_MASK) != DNS_TYPE_REQUEST)
|
||||
return(0);
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "Detected DNS query[%u]", dns_length);
|
||||
|
||||
static bool check_dns_req_allowed(struct vpnproxy_data *proxy, zdtun_pkt_t *pkt, zdtun_conn_t *conn) {
|
||||
if(new_dns_server != 0) {
|
||||
// Reload DNS server
|
||||
proxy->dns_server = new_dns_server;
|
||||
@@ -606,14 +609,42 @@ static int check_dns_req_dnat(struct vpnproxy_data *proxy, zdtun_pkt_t *pkt, zdt
|
||||
log_android(ANDROID_LOG_DEBUG, "Using new DNS server");
|
||||
}
|
||||
|
||||
/*
|
||||
* Direct the packet to the public DNS server. Checksum recalculation is not strictly necessary
|
||||
* here as zdtun will proxy the connection.
|
||||
*/
|
||||
zdtun_conn_dnat(conn, proxy->dns_server, 0);
|
||||
proxy->num_dns_requests++;
|
||||
bool is_dns_server = ((pkt->tuple.ipver == 4) && (pkt->tuple.dst_ip.ip4 == proxy->vpn_dns))
|
||||
|| ((pkt->tuple.ipver == 6) && (memcmp(&pkt->tuple.dst_ip.ip6, &proxy->ipv6.dns_server, 16) == 0));
|
||||
|
||||
return(1);
|
||||
if(!is_dns_server)
|
||||
return(true);
|
||||
|
||||
if((pkt->tuple.ipproto == IPPROTO_UDP) && (ntohs(pkt->udp->uh_dport) == 53)) {
|
||||
int dns_length = pkt->l7_len;
|
||||
|
||||
if(dns_length >= sizeof(struct dns_packet)) {
|
||||
struct dns_packet *dns_data = (struct dns_packet*) pkt->l7;
|
||||
|
||||
if((dns_data->flags & DNS_FLAGS_MASK) != DNS_TYPE_REQUEST)
|
||||
return(true);
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "Detected DNS query[%u]", dns_length);
|
||||
proxy->num_dns_requests++;
|
||||
|
||||
if(pkt->tuple.ipver == 4) {
|
||||
/*
|
||||
* Direct the packet to the public DNS server. Checksum recalculation is not strictly necessary
|
||||
* here as zdtun will proxy the connection.
|
||||
*/
|
||||
zdtun_ip_t dnatip = {0};
|
||||
dnatip.ip4 = proxy->dns_server;
|
||||
zdtun_conn_dnat(conn, &dnatip, pkt->udp->uh_dport);
|
||||
}
|
||||
|
||||
return(true);
|
||||
}
|
||||
}
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "blocking packet directed to the DNS server");
|
||||
|
||||
// block everything else (e.g. DoH)
|
||||
return(false);
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
@@ -637,21 +668,24 @@ static void check_tls_mitm(zdtun_t *tun, struct vpnproxy_data *proxy, zdtun_pkt_
|
||||
uint16_t port = ntohs(pkt->tuple.dst_port);
|
||||
|
||||
if (port == 443) {
|
||||
zdtun_conn_dnat(conn, mitm_ip, mitm_port);
|
||||
zdtun_ip_t dnatip = {0};
|
||||
dnatip.ip4 = mitm_ip;
|
||||
zdtun_conn_dnat(conn, &dnatip, mitm_port);
|
||||
data->mitm_header_needed = true;
|
||||
}
|
||||
} else if(data->mitm_header_needed && (pkt->l7_len > 0)) {
|
||||
} else if(data->mitm_header_needed && (pkt->l7_len > 0) && (pkt->tuple.ipver == 4)) {
|
||||
// TODO allow IPv6 -> IPv4 redirection
|
||||
/* First L7 packet, send the mitmproxy header first */
|
||||
mitm_proxy_hdr_t *mitm = (mitm_proxy_hdr_t*) mitm_pkt.l7;
|
||||
|
||||
/* Fix the packet with the correct peers */
|
||||
mitm_pkt.tuple.src_ip = mitm_pkt.ip->saddr = pkt->ip->saddr;
|
||||
mitm_pkt.tuple.dst_ip = mitm_pkt.ip->daddr = mitm_ip;
|
||||
mitm_pkt.tuple.src_ip.ip4 = mitm_pkt.ip4->saddr = pkt->ip4->saddr;
|
||||
mitm_pkt.tuple.dst_ip.ip4 = mitm_pkt.ip4->daddr = mitm_ip;
|
||||
mitm_pkt.tuple.src_port = mitm_pkt.tcp->th_sport = pkt->tcp->th_sport;
|
||||
mitm_pkt.tuple.dst_port = mitm_pkt.tcp->th_dport = mitm_port;
|
||||
|
||||
/* Send the original (pre-nat) IP and port */
|
||||
mitm->dst_ip = pkt->tuple.dst_ip;
|
||||
mitm->dst_ip = pkt->tuple.dst_ip.ip4;
|
||||
mitm->dst_port = pkt->tuple.dst_port;
|
||||
zdtun_send_oob(tun, &mitm_pkt, conn);
|
||||
|
||||
@@ -697,17 +731,16 @@ static int net2tap(zdtun_t *tun, char *pkt_buf, int pkt_size, const zdtun_conn_t
|
||||
/* ******************************************************* */
|
||||
|
||||
static int dumpConnection(vpnproxy_data_t *proxy, const vpn_conn_t *conn, jobject arr, int idx) {
|
||||
char srcip[64], dstip[64];
|
||||
char srcip[INET6_ADDRSTRLEN], dstip[INET6_ADDRSTRLEN];
|
||||
struct in_addr addr;
|
||||
JNIEnv *env = proxy->env;
|
||||
const zdtun_5tuple_t *conn_info = &conn->tuple;
|
||||
const conn_data_t *data = conn->data;
|
||||
int rv = 0;
|
||||
int family = (conn->tuple.ipver == 4) ? AF_INET : AF_INET6;
|
||||
|
||||
addr.s_addr = conn_info->src_ip;
|
||||
strncpy(srcip, inet_ntoa(addr), sizeof(srcip));
|
||||
addr.s_addr = conn_info->dst_ip;
|
||||
strncpy(dstip, inet_ntoa(addr), sizeof(dstip));
|
||||
inet_ntop(family, &conn_info->src_ip, srcip, sizeof(srcip));
|
||||
inet_ntop(family, &conn_info->dst_ip, dstip, sizeof(dstip));
|
||||
|
||||
#if 0
|
||||
log_android(ANDROID_LOG_INFO, "DUMP: [proto=%d]: %s:%u -> %s:%u [%d]",
|
||||
@@ -717,6 +750,7 @@ static int dumpConnection(vpnproxy_data_t *proxy, const vpn_conn_t *conn, jobjec
|
||||
data->uid);
|
||||
#endif
|
||||
|
||||
jobject status_string = (*env)->NewStringUTF(env, zdtun_conn_status2str(data->status));
|
||||
jobject info_string = (*env)->NewStringUTF(env, data->info ? data->info : "");
|
||||
jobject url_string = (*env)->NewStringUTF(env, data->url ? data->url : "");
|
||||
jobject proto_string = (*env)->NewStringUTF(env, getProtoName(proxy->ndpi, data->l7proto, conn_info->ipproto));
|
||||
@@ -728,12 +762,12 @@ static int dumpConnection(vpnproxy_data_t *proxy, const vpn_conn_t *conn, jobjec
|
||||
/* NOTE: as an alternative to pass all the params into the constructor, GetFieldID and
|
||||
* SetIntField like methods could be used. */
|
||||
(*env)->CallVoidMethod(env, conn_descriptor, mids.connSetData,
|
||||
src_string, dst_string, info_string, url_string, proto_string,
|
||||
conn_info->ipproto, ntohs(conn_info->src_port),
|
||||
src_string, dst_string, status_string, info_string, url_string, proto_string,
|
||||
conn_info->ipver, conn_info->ipproto, 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, data->uid, data->incr_id, data->closed);
|
||||
data->rcvd_pkts, data->uid, data->incr_id);
|
||||
if(jniCheckException(env))
|
||||
rv = -1;
|
||||
else {
|
||||
@@ -750,6 +784,7 @@ static int dumpConnection(vpnproxy_data_t *proxy, const vpn_conn_t *conn, jobjec
|
||||
rv = -1;
|
||||
}
|
||||
|
||||
(*env)->DeleteLocalRef(env, status_string);
|
||||
(*env)->DeleteLocalRef(env, info_string);
|
||||
(*env)->DeleteLocalRef(env, url_string);
|
||||
(*env)->DeleteLocalRef(env, proto_string);
|
||||
@@ -912,7 +947,7 @@ static int run_tun(JNIEnv *env, jclass vpn, int tapfd, jint sdk) {
|
||||
mids.connInit = jniGetMethodID(env, cls.conn, "<init>", "()V");
|
||||
mids.connSetData = jniGetMethodID(env, cls.conn, "setData",
|
||||
/* NOTE: must match ConnectionDescriptor::setData */
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIJJJJIIIIZ)V");
|
||||
"(Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;Ljava/lang/String;IIIIJJJJIIII)V");
|
||||
mids.statsInit = jniGetMethodID(env, cls.stats, "<init>", "()V");
|
||||
mids.statsSetData = jniGetMethodID(env, cls.stats, "setData", "(JJIIIIIIII)V");
|
||||
|
||||
@@ -939,6 +974,10 @@ static int run_tun(JNIEnv *env, jclass vpn, int tapfd, jint sdk) {
|
||||
.enabled = (bool) getIntPref(env, vpn, "getTlsDecryptionEnabled"),
|
||||
.proxy_ip = getIPv4Pref(env, vpn, "getTlsProxyAddress"),
|
||||
.proxy_port = htons(getIntPref(env, vpn, "getTlsProxyPort")),
|
||||
},
|
||||
.ipv6 = {
|
||||
.enabled = (bool) getIntPref(env, vpn, "getIPv6Enabled"),
|
||||
.dns_server = getIPv6Pref(env, vpn, "getIpv6DnsServer"),
|
||||
}
|
||||
};
|
||||
|
||||
@@ -1046,27 +1085,49 @@ static int run_tun(JNIEnv *env, jclass vpn, int tapfd, jint sdk) {
|
||||
goto housekeeping;
|
||||
}
|
||||
|
||||
zdtun_conn_t *conn = zdtun_lookup(tun, &pkt.tuple, 1 /* create if not exists */);
|
||||
if((pkt.tuple.ipver == 6) && (!proxy.ipv6.enabled)) {
|
||||
char buf[512];
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "ignoring IPv6 packet: %s",
|
||||
zdtun_5tuple2str(&pkt.tuple, buf, sizeof(buf)));
|
||||
goto housekeeping;
|
||||
}
|
||||
|
||||
// Skip established TCP connections
|
||||
uint8_t is_tcp_established = ((pkt.tuple.ipproto == IPPROTO_TCP) &&
|
||||
(!(pkt.tcp->th_flags & TH_SYN) || (pkt.tcp->th_flags & TH_ACK)));
|
||||
|
||||
zdtun_conn_t *conn = zdtun_lookup(tun, &pkt.tuple, !is_tcp_established);
|
||||
|
||||
if (!conn) {
|
||||
proxy.num_dropped_connections++;
|
||||
log_android(ANDROID_LOG_ERROR, "zdtun_lookup failed");
|
||||
if(!is_tcp_established) {
|
||||
char buf[512];
|
||||
|
||||
proxy.num_dropped_connections++;
|
||||
log_android(ANDROID_LOG_ERROR, "zdtun_lookup failed: %s",
|
||||
zdtun_5tuple2str(&pkt.tuple, buf, sizeof(buf)));
|
||||
} else {
|
||||
char buf[512];
|
||||
|
||||
log_android(ANDROID_LOG_DEBUG, "skipping established TCP: %s",
|
||||
zdtun_5tuple2str(&pkt.tuple, buf, sizeof(buf)));
|
||||
}
|
||||
goto housekeeping;
|
||||
}
|
||||
|
||||
if((check_dns_req_dnat(&proxy, &pkt, conn) == 0)
|
||||
&& (pkt.tuple.dst_ip == proxy.vpn_dns)) {
|
||||
log_android(ANDROID_LOG_DEBUG, "ignoring packet directed to the virtual DNS server");
|
||||
if(!check_dns_req_allowed(&proxy, &pkt, conn))
|
||||
goto housekeeping;
|
||||
}
|
||||
|
||||
if(proxy.tls_decryption.enabled)
|
||||
check_tls_mitm(tun, &proxy, &pkt, conn);
|
||||
|
||||
if ((rc = zdtun_forward(tun, &pkt, conn)) != 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "zdtun_forward (proto=%d) failed with code %d", pkt.tuple.ipproto, rc);
|
||||
proxy.num_dropped_connections++;
|
||||
if((rc = zdtun_forward(tun, &pkt, conn)) != 0) {
|
||||
char buf[512];
|
||||
|
||||
log_android(ANDROID_LOG_ERROR, "zdtun_forward failed: %s",
|
||||
zdtun_5tuple2str(&pkt.tuple, buf, sizeof(buf)));
|
||||
|
||||
proxy.num_dropped_connections++;
|
||||
zdtun_destroy_conn(tun, conn);
|
||||
goto housekeeping;
|
||||
}
|
||||
@@ -1166,4 +1227,4 @@ Java_com_emanuelef_remote_1capture_CaptureService_setDnsServer(JNIEnv *env, jcla
|
||||
|
||||
if(inet_aton(value, &addr) != 0)
|
||||
new_dns_server = addr.s_addr;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -20,7 +20,7 @@
|
||||
#include <jni.h>
|
||||
#include "zdtun.h"
|
||||
#include "uid_resolver.h"
|
||||
#include "ndpi_api.h"
|
||||
#include <ndpi_api.h>
|
||||
|
||||
#ifndef REMOTE_CAPTURE_VPNPROXY_H
|
||||
#define REMOTE_CAPTURE_VPNPROXY_H
|
||||
@@ -51,10 +51,10 @@ typedef struct conn_data {
|
||||
jlong rcvd_bytes;
|
||||
jint sent_pkts;
|
||||
jint rcvd_pkts;
|
||||
zdtun_conn_status_t status;
|
||||
char *info;
|
||||
char *url;
|
||||
jint uid;
|
||||
bool closed;
|
||||
bool pending_notification;
|
||||
bool mitm_header_needed;
|
||||
} conn_data_t;
|
||||
@@ -107,6 +107,11 @@ typedef struct vpnproxy_data {
|
||||
u_int32_t proxy_port;
|
||||
} tls_decryption;
|
||||
|
||||
struct {
|
||||
bool enabled;
|
||||
struct in6_addr dns_server;
|
||||
} ipv6;
|
||||
|
||||
capture_stats_t capture_stats;
|
||||
} vpnproxy_data_t;
|
||||
|
||||
|
||||
@@ -41,11 +41,11 @@
|
||||
android:id="@+id/status_ind"
|
||||
android:textSize="12sp"
|
||||
android:layout_width="wrap_content"
|
||||
android:textColor="#28BC36"
|
||||
android:layout_height="wrap_content"
|
||||
android:layout_alignParentTop="true"
|
||||
android:layout_alignParentEnd="true"
|
||||
android:text="@string/conn_status_open" />
|
||||
tools:textColor="#FF28BC36"
|
||||
tools:text="@string/conn_status_open" />
|
||||
|
||||
<TextView
|
||||
android:id="@+id/eta"
|
||||
|
||||
@@ -108,5 +108,9 @@
|
||||
<string name="get_app">Get it:</string>
|
||||
<string name="tls_how_to">How to setup TLS Decryption</string>
|
||||
<string name="unknown_app">Unknown</string>
|
||||
<string name="vpn_prefs">VPN</string>
|
||||
<string name="enable_ipv6">Enable IPv6</string>
|
||||
<string name="enable_ipv6_summary">Allow IPv6 internet connections. This should only be enabled on networks with an IPv6 gateway.</string>
|
||||
<string name="error">Error</string>
|
||||
</resources>
|
||||
|
||||
|
||||
@@ -73,4 +73,13 @@
|
||||
app:defaultValue="@string/default_proxy_port"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/vpn_prefs" app:iconSpaceReserved="false">
|
||||
<SwitchPreference
|
||||
app:key="ipv6_enabled"
|
||||
app:title="@string/enable_ipv6"
|
||||
app:iconSpaceReserved="false"
|
||||
app:summary="@string/enable_ipv6_summary"
|
||||
app:defaultValue="false" />
|
||||
</PreferenceCategory>
|
||||
</PreferenceScreen>
|
||||
|
||||
+1
-1
Submodule submodules/zdtun updated: ec28e07f70...ec967b41d1
Reference in New Issue
Block a user