diff --git a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java index 7442eaa7..10aba4aa 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java +++ b/app/src/main/java/com/emanuelef/remote_capture/CaptureService.java @@ -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) { diff --git a/app/src/main/java/com/emanuelef/remote_capture/ConnectionsRegister.java b/app/src/main/java/com/emanuelef/remote_capture/ConnectionsRegister.java index 680758c1..7d84e04f 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/ConnectionsRegister.java +++ b/app/src/main/java/com/emanuelef/remote_capture/ConnectionsRegister.java @@ -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(","); diff --git a/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java b/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java index ee68fcff..2401a699 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java +++ b/app/src/main/java/com/emanuelef/remote_capture/activities/ConnectionDetailsActivity.java @@ -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); } } diff --git a/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java b/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java index 29afe76a..5b503023 100644 --- a/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java +++ b/app/src/main/java/com/emanuelef/remote_capture/adapters/ConnectionsAdapter.java @@ -53,12 +53,15 @@ public class ConnectionsAdapter extends RecyclerView.Adapter %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( diff --git a/app/src/main/jni/vpnproxy-jni/vpnproxy.c b/app/src/main/jni/vpnproxy-jni/vpnproxy.c index 2b99e0d2..05cdc065 100644 --- a/app/src/main/jni/vpnproxy-jni/vpnproxy.c +++ b/app/src/main/jni/vpnproxy-jni/vpnproxy.c @@ -17,8 +17,7 @@ * Copyright 2020-21 - Emanuele Faranda */ -#include -#include +#include #include #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, "", "()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, "", "()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; -} \ No newline at end of file +} diff --git a/app/src/main/jni/vpnproxy-jni/vpnproxy.h b/app/src/main/jni/vpnproxy-jni/vpnproxy.h index 54447de7..3273f7eb 100644 --- a/app/src/main/jni/vpnproxy-jni/vpnproxy.h +++ b/app/src/main/jni/vpnproxy-jni/vpnproxy.h @@ -20,7 +20,7 @@ #include #include "zdtun.h" #include "uid_resolver.h" -#include "ndpi_api.h" +#include #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; diff --git a/app/src/main/res/layout/connection_item.xml b/app/src/main/res/layout/connection_item.xml index 12252a6d..c035fefa 100644 --- a/app/src/main/res/layout/connection_item.xml +++ b/app/src/main/res/layout/connection_item.xml @@ -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" /> Get it: How to setup TLS Decryption Unknown + VPN + Enable IPv6 + Allow IPv6 internet connections. This should only be enabled on networks with an IPv6 gateway. + Error diff --git a/app/src/main/res/xml/root_preferences.xml b/app/src/main/res/xml/root_preferences.xml index 4fd02b3e..038f0c7c 100644 --- a/app/src/main/res/xml/root_preferences.xml +++ b/app/src/main/res/xml/root_preferences.xml @@ -73,4 +73,13 @@ app:defaultValue="@string/default_proxy_port" app:useSimpleSummaryProvider="true" /> + + + + diff --git a/submodules/zdtun b/submodules/zdtun index ec28e07f..ec967b41 160000 --- a/submodules/zdtun +++ b/submodules/zdtun @@ -1 +1 @@ -Subproject commit ec28e07f705f9b68851228673d9e663cefebe556 +Subproject commit ec967b41d132a6d59d6754f0290113f81f0d7119