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:
emanuele-f
2021-04-09 10:52:25 +02:00
parent fea40c708d
commit b02989598f
8 changed files with 61 additions and 112 deletions
@@ -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")));}
+15 -70
View File
@@ -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];
+1 -2
View File
@@ -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;
+5
View File
@@ -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>
+7 -7
View File
@@ -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" />