mirror of
https://github.com/emanuele-f/PCAPdroid.git
synced 2026-05-08 21:12:26 +00:00
Implement SOCKS5 proxy support
When enabled, all the TCP connections will be redirected to a SOCKS5 proxy. This also removes the need to use a customized mitmproxy to perform the TLS decryption.
This commit is contained in:
@@ -29,8 +29,6 @@ import android.content.Context;
|
||||
import android.content.Intent;
|
||||
import android.content.SharedPreferences;
|
||||
import android.content.pm.PackageManager;
|
||||
import android.graphics.drawable.BitmapDrawable;
|
||||
import android.graphics.drawable.Drawable;
|
||||
import android.net.ConnectivityManager;
|
||||
import android.net.Network;
|
||||
import android.net.NetworkCapabilities;
|
||||
@@ -49,7 +47,6 @@ import androidx.annotation.NonNull;
|
||||
import androidx.annotation.RequiresApi;
|
||||
import androidx.core.app.NotificationCompat;
|
||||
import androidx.core.app.NotificationManagerCompat;
|
||||
import androidx.core.content.ContextCompat;
|
||||
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
|
||||
import androidx.preference.PreferenceManager;
|
||||
|
||||
@@ -77,13 +74,13 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
private String vpn_dns;
|
||||
private String dns_server;
|
||||
private String collector_address;
|
||||
private String tls_proxy_address;
|
||||
private String socks5_proxy_address;
|
||||
private Prefs.DumpMode dump_mode;
|
||||
private boolean tls_decryption_enabled;
|
||||
private boolean socks5_enabled;
|
||||
private boolean ipv6_enabled;
|
||||
private int collector_port;
|
||||
private int http_server_port;
|
||||
private int tls_proxy_port;
|
||||
private int socks5_proxy_port;
|
||||
private long last_bytes;
|
||||
private int last_connections;
|
||||
private static CaptureService INSTANCE;
|
||||
@@ -186,9 +183,9 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
collector_address = Prefs.getCollectorIp(prefs);
|
||||
collector_port = Prefs.getCollectorPort(prefs);
|
||||
http_server_port = Prefs.getHttpServerPort(prefs);
|
||||
tls_decryption_enabled = Prefs.getTlsDecryptionEnabled(prefs);
|
||||
tls_proxy_address = Prefs.getTlsProxyAddress(prefs);
|
||||
tls_proxy_port = Prefs.getTlsProxyPort(prefs);
|
||||
socks5_enabled = Prefs.getTlsDecryptionEnabled(prefs); // TODO rename
|
||||
socks5_proxy_address = Prefs.getSocks5ProxyAddress(prefs);
|
||||
socks5_proxy_port = Prefs.getSocks5ProxyPort(prefs);
|
||||
dump_mode = Prefs.getDumpMode(prefs);
|
||||
ipv6_enabled = Prefs.getIPv6Enabled(prefs);
|
||||
last_bytes = 0;
|
||||
@@ -541,11 +538,11 @@ public class CaptureService extends VpnService implements Runnable {
|
||||
return(collector_port);
|
||||
}
|
||||
|
||||
public String getTlsProxyAddress() { return(tls_proxy_address); }
|
||||
public String getSocks5ProxyAddress() { return(socks5_proxy_address); }
|
||||
|
||||
public int getTlsDecryptionEnabled() { return tls_decryption_enabled ? 1 : 0; }
|
||||
public int getSocks5Enabled() { return socks5_enabled ? 1 : 0; }
|
||||
|
||||
public int getTlsProxyPort() { return(tls_proxy_port); }
|
||||
public int getSocks5ProxyPort() { return(socks5_proxy_port); }
|
||||
|
||||
public int getIPv6Enabled() { return(ipv6_enabled ? 1 : 0); }
|
||||
|
||||
|
||||
@@ -82,9 +82,9 @@ public class SettingsActivity extends BaseActivity {
|
||||
}
|
||||
|
||||
public static class SettingsFragment extends PreferenceFragmentCompat {
|
||||
private SwitchPreference mTlsDecryptionEnabled;
|
||||
private EditTextPreference mTlsProxyIp;
|
||||
private EditTextPreference mTlsProxyPort;
|
||||
private SwitchPreference mTlsDecryptionEnabled; // TODO rename
|
||||
private EditTextPreference mSocks5ProxyIp;
|
||||
private EditTextPreference mSocks5ProxyPort;
|
||||
private Preference mTlsHelp;
|
||||
|
||||
@Override
|
||||
@@ -93,10 +93,10 @@ public class SettingsActivity extends BaseActivity {
|
||||
|
||||
setupUdpExporterPrefs();
|
||||
setupHttpServerPrefs();
|
||||
setupTlsProxyPrefs();
|
||||
setupSocks5ProxyPrefs();
|
||||
setupOtherPrefs();
|
||||
|
||||
tlsDecryptionHideShow(mTlsDecryptionEnabled.isChecked());
|
||||
socks5ProxyHideShow(mTlsDecryptionEnabled.isChecked());
|
||||
}
|
||||
|
||||
private boolean validatePort(String value) {
|
||||
@@ -128,32 +128,35 @@ public class SettingsActivity extends BaseActivity {
|
||||
mHttpServerPort.setOnPreferenceChangeListener((preference, newValue) -> validatePort(newValue.toString()));
|
||||
}
|
||||
|
||||
private void setupTlsProxyPrefs() {
|
||||
private void setupSocks5ProxyPrefs() {
|
||||
mTlsHelp = findPreference("tls_how_to");
|
||||
|
||||
mTlsDecryptionEnabled = findPreference(Prefs.PREF_TLS_DECRYPTION_ENABLED_KEY);
|
||||
mTlsDecryptionEnabled.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
tlsDecryptionHideShow((Boolean) newValue);
|
||||
socks5ProxyHideShow((Boolean) newValue);
|
||||
return true;
|
||||
});
|
||||
|
||||
/* TLS Proxy IP validation */
|
||||
mTlsProxyIp = findPreference(Prefs.PREF_TLS_PROXY_IP_KEY);
|
||||
mTlsProxyIp.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
mSocks5ProxyIp = findPreference(Prefs.PREF_SOCKS5_PROXY_IP_KEY);
|
||||
mSocks5ProxyIp.setOnPreferenceChangeListener((preference, newValue) -> {
|
||||
Matcher matcher = Patterns.IP_ADDRESS.matcher(newValue.toString());
|
||||
return(matcher.matches());
|
||||
});
|
||||
|
||||
/* TLS Proxy port validation */
|
||||
mTlsProxyPort = findPreference(Prefs.PREF_TLS_PROXY_PORT_KEY);
|
||||
mTlsProxyPort.setOnBindEditTextListener(editText -> editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED));
|
||||
mTlsProxyPort.setOnPreferenceChangeListener((preference, newValue) -> validatePort(newValue.toString()));
|
||||
mSocks5ProxyPort = findPreference(Prefs.PREF_SOCKS5_PROXY_PORT_KEY);
|
||||
mSocks5ProxyPort.setOnBindEditTextListener(editText -> editText.setInputType(InputType.TYPE_CLASS_NUMBER | InputType.TYPE_NUMBER_FLAG_SIGNED));
|
||||
mSocks5ProxyPort.setOnPreferenceChangeListener((preference, newValue) -> validatePort(newValue.toString()));
|
||||
}
|
||||
|
||||
private void tlsDecryptionHideShow(boolean decryptionEnabled) {
|
||||
mTlsProxyIp.setVisible(decryptionEnabled);
|
||||
mTlsProxyPort.setVisible(decryptionEnabled);
|
||||
mTlsHelp.setVisible(decryptionEnabled);
|
||||
private void socks5ProxyHideShow(boolean decryptionEnabled) {
|
||||
mSocks5ProxyIp.setVisible(decryptionEnabled);
|
||||
mSocks5ProxyPort.setVisible(decryptionEnabled);
|
||||
|
||||
// TODO
|
||||
//mTlsHelp.setVisible(decryptionEnabled);
|
||||
mTlsHelp.setVisible(false);
|
||||
}
|
||||
|
||||
private void setupOtherPrefs() {
|
||||
|
||||
@@ -29,8 +29,8 @@ public class Prefs {
|
||||
public static final String DUMP_PCAP_FILE = "pcap_file";
|
||||
public static final String PREF_COLLECTOR_IP_KEY = "collector_ip_address";
|
||||
public static final String PREF_COLLECTOR_PORT_KEY = "collector_port";
|
||||
public static final String PREF_TLS_PROXY_IP_KEY = "tls_proxy_ip_address";
|
||||
public static final String PREF_TLS_PROXY_PORT_KEY = "tls_proxy_port";
|
||||
public static final String PREF_SOCKS5_PROXY_IP_KEY = "socks5_proxy_ip_address";
|
||||
public static final String PREF_SOCKS5_PROXY_PORT_KEY = "socks5_proxy_port";
|
||||
public static final String PREF_TLS_DECRYPTION_ENABLED_KEY = "tls_decryption_enabled";
|
||||
public static final String PREF_APP_FILTER = "app_filter";
|
||||
public static final String PREF_HTTP_SERVER_PORT = "http_server_port";
|
||||
@@ -65,8 +65,8 @@ public class Prefs {
|
||||
public static DumpMode getDumpMode(SharedPreferences p) { return(getDumpMode(p.getString(PREF_PCAP_DUMP_MODE, DEFAULT_DUMP_MODE))); }
|
||||
public static int getHttpServerPort(SharedPreferences p) { return(Integer.parseInt(p.getString(Prefs.PREF_HTTP_SERVER_PORT, "8080"))); }
|
||||
public static boolean getTlsDecryptionEnabled(SharedPreferences p) { return(p.getBoolean(PREF_TLS_DECRYPTION_ENABLED_KEY, false)); }
|
||||
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 getSocks5ProxyAddress(SharedPreferences p) { return(p.getString(PREF_SOCKS5_PROXY_IP_KEY, "0.0.0.0")); }
|
||||
public static int getSocks5ProxyPort(SharedPreferences p) { return(Integer.parseInt(p.getString(Prefs.PREF_SOCKS5_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)); }
|
||||
public static boolean useEnglishLanguage(SharedPreferences p){ return("english".equals(p.getString(PREF_APP_LANGUAGE, "system")));}
|
||||
|
||||
@@ -54,13 +54,6 @@ typedef struct dns_packet {
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
typedef struct mitm_proxy_hdr {
|
||||
uint32_t dst_ip;
|
||||
uint16_t dst_port;
|
||||
} __attribute__((packed)) mitm_proxy_hdr_t;
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
typedef struct jni_methods {
|
||||
jmethodID getApplicationByUid;
|
||||
jmethodID protect;
|
||||
@@ -90,23 +83,6 @@ static bool dump_capture_stats_now = false;
|
||||
static ndpi_protocol_bitmask_struct_t masterProtos;
|
||||
static uint32_t new_dns_server = 0;
|
||||
|
||||
/* TCP/IP packet to hold the mitmproxy header */
|
||||
static char mitmproxy_pkt_buffer[] = {
|
||||
"\x45\x00" \
|
||||
|
||||
/* Total length: 52 + 6 (sizeof mitm_proxy_hdr_t) */
|
||||
"\x00\x3a" \
|
||||
|
||||
"\xb8\x9e\x40\x00\x40\x06\xf7\xe1\x00\x00\x00\x00\x00\x00" \
|
||||
"\x00\x00\x00\x00\x00\x00\x65\xbf\xc2\xaf\x08\x93\x36\x09\x80\x18" \
|
||||
"\x01\xf5\x00\x00\x00\x00\x01\x01\x08\x0a\x6c\xe2\x4f\x95\x4a\xe0" \
|
||||
"\x92\x51" \
|
||||
|
||||
/* TCP payload */
|
||||
"\x01\x02\x03\x04\x05\x06"
|
||||
};
|
||||
static zdtun_pkt_t mitm_pkt;
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
/* NOTE: these must be reset during each run, as android may reuse the service */
|
||||
@@ -742,49 +718,14 @@ static bool check_dns_req_allowed(struct vpnproxy_data *proxy, zdtun_conn_t *con
|
||||
|
||||
/* ******************************************************* */
|
||||
|
||||
/*
|
||||
* Check if the packet should be redirected to the mitmproxy
|
||||
*/
|
||||
static void check_tls_mitm(zdtun_t *tun, struct vpnproxy_data *proxy, zdtun_pkt_t *pkt, zdtun_conn_t *conn) {
|
||||
static void check_socks5_redirection(zdtun_t *tun, struct vpnproxy_data *proxy, zdtun_pkt_t *pkt, zdtun_conn_t *conn) {
|
||||
conn_data_t *data = zdtun_conn_get_userdata(conn);
|
||||
|
||||
if(shouldIgnoreConn(proxy, zdtun_conn_get_5tuple(conn), data))
|
||||
return;
|
||||
|
||||
if(pkt->tuple.ipproto == IPPROTO_TCP) {
|
||||
uint32_t mitm_ip = proxy->tls_decryption.proxy_ip;
|
||||
uint16_t mitm_port = proxy->tls_decryption.proxy_port;
|
||||
|
||||
bool is_new = ((data->sent_pkts + data->rcvd_pkts) == 0);
|
||||
|
||||
if(is_new) {
|
||||
uint16_t port = ntohs(pkt->tuple.dst_port);
|
||||
|
||||
if (port == 443) {
|
||||
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) && (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.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.ip4;
|
||||
mitm->dst_port = pkt->tuple.dst_port;
|
||||
zdtun_send_oob(tun, &mitm_pkt, conn);
|
||||
|
||||
data->mitm_header_needed = false;
|
||||
}
|
||||
}
|
||||
if((pkt->tuple.ipproto == IPPROTO_TCP) && (((data->sent_pkts + data->rcvd_pkts) == 0)))
|
||||
zdtun_conn_proxy(conn);
|
||||
}
|
||||
|
||||
/* ******************************************************* */
|
||||
@@ -1034,8 +975,6 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) {
|
||||
|
||||
init_log(ANDROID_LOG_DEBUG, env, vpn_class, vpn);
|
||||
|
||||
zdtun_parse_pkt(mitmproxy_pkt_buffer, sizeof(mitmproxy_pkt_buffer)-1, &mitm_pkt);
|
||||
|
||||
/* Classes */
|
||||
cls.vpn_service = vpn_class;
|
||||
cls.conn = jniFindClass(env, "com/emanuelef/remote_capture/model/ConnectionDescriptor");
|
||||
@@ -1076,10 +1015,10 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) {
|
||||
.tcp_socket = false,
|
||||
.enabled = (bool) getIntPref(env, vpn, "dumpPcapToUdp"),
|
||||
},
|
||||
.tls_decryption = {
|
||||
.enabled = (bool) getIntPref(env, vpn, "getTlsDecryptionEnabled"),
|
||||
.proxy_ip = getIPv4Pref(env, vpn, "getTlsProxyAddress"),
|
||||
.proxy_port = htons(getIntPref(env, vpn, "getTlsProxyPort")),
|
||||
.socks5 = {
|
||||
.enabled = (bool) getIntPref(env, vpn, "getSocks5Enabled"),
|
||||
.proxy_ip = getIPv4Pref(env, vpn, "getSocks5ProxyAddress"),
|
||||
.proxy_port = htons(getIntPref(env, vpn, "getSocks5ProxyPort")),
|
||||
},
|
||||
.ipv6 = {
|
||||
.enabled = (bool) getIntPref(env, vpn, "getIPv6Enabled"),
|
||||
@@ -1161,6 +1100,12 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) {
|
||||
}
|
||||
}
|
||||
|
||||
if(proxy.socks5.enabled) {
|
||||
zdtun_ip_t dnatip = {0};
|
||||
dnatip.ip4 = proxy.socks5.proxy_ip;
|
||||
zdtun_set_socks5_proxy(tun, &dnatip, proxy.socks5.proxy_port);
|
||||
}
|
||||
|
||||
new_dns_server = 0;
|
||||
gettimeofday(&now_tv, NULL);
|
||||
now_ms = now_tv.tv_sec * 1000 + now_tv.tv_usec / 1000;
|
||||
@@ -1235,8 +1180,8 @@ static int run_tun(JNIEnv *env, jclass vpn, int tunfd, jint sdk) {
|
||||
goto housekeeping;
|
||||
}
|
||||
|
||||
if(proxy.tls_decryption.enabled)
|
||||
check_tls_mitm(tun, &proxy, &pkt, conn);
|
||||
if(proxy.socks5.enabled)
|
||||
check_socks5_redirection(tun, &proxy, &pkt, conn);
|
||||
|
||||
if((rc = zdtun_forward(tun, &pkt, conn)) != 0) {
|
||||
char buf[512];
|
||||
|
||||
@@ -57,7 +57,6 @@ typedef struct conn_data {
|
||||
char *url;
|
||||
jint uid;
|
||||
bool pending_notification;
|
||||
bool mitm_header_needed;
|
||||
} conn_data_t;
|
||||
|
||||
typedef struct vpn_conn {
|
||||
@@ -110,7 +109,7 @@ typedef struct vpnproxy_data {
|
||||
bool enabled;
|
||||
u_int32_t proxy_ip;
|
||||
u_int32_t proxy_port;
|
||||
} tls_decryption;
|
||||
} socks5;
|
||||
|
||||
struct {
|
||||
bool enabled;
|
||||
|
||||
@@ -121,5 +121,10 @@
|
||||
<string name="theme_light">Light</string>
|
||||
<string name="theme_dark">Dark</string>
|
||||
<string name="app_theme">Theme</string>
|
||||
<string name="enable_socks5_proxy">Enable SOCKS5 Proxy</string>
|
||||
<string name="enable_socks5_proxy_summary">When enabled, all the TCP connections will be redirected to the specified SOCKS5 proxy.</string>
|
||||
<string name="proxy">Proxy</string>
|
||||
<string name="proxy_ip_address">Proxy IP Address</string>
|
||||
<string name="proxy_port">Proxy Port</string>
|
||||
</resources>
|
||||
|
||||
|
||||
@@ -43,12 +43,12 @@
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
</PreferenceCategory>
|
||||
|
||||
<PreferenceCategory app:title="@string/tls_decryption" app:iconSpaceReserved="false">
|
||||
<PreferenceCategory app:title="@string/proxy" app:iconSpaceReserved="false">
|
||||
<SwitchPreference
|
||||
app:key="tls_decryption_enabled"
|
||||
app:title="@string/enable_tls_decryption"
|
||||
app:title="@string/enable_socks5_proxy"
|
||||
app:iconSpaceReserved="false"
|
||||
app:summary="@string/enable_tls_decryption_summary"
|
||||
app:summary="@string/enable_socks5_proxy_summary"
|
||||
app:defaultValue="false" />
|
||||
|
||||
<Preference
|
||||
@@ -60,15 +60,15 @@
|
||||
</Preference>
|
||||
|
||||
<EditTextPreference
|
||||
app:key="tls_proxy_ip_address"
|
||||
app:title="@string/tls_proxy_ip_address"
|
||||
app:key="socks5_proxy_ip_address"
|
||||
app:title="@string/proxy_ip_address"
|
||||
app:defaultValue="0.0.0.0"
|
||||
app:iconSpaceReserved="false"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
<EditTextPreference
|
||||
app:key="tls_proxy_port"
|
||||
app:title="@string/tls_proxy_port"
|
||||
app:key="socks5_proxy_port"
|
||||
app:title="@string/proxy_port"
|
||||
app:iconSpaceReserved="false"
|
||||
app:defaultValue="8080"
|
||||
app:useSimpleSummaryProvider="true" />
|
||||
|
||||
+1
-1
Submodule submodules/zdtun updated: a29c548af2...e5d24d18ca
Reference in New Issue
Block a user