mirror of https://github.com/M66B/NetGuard.git
Basic SOCKS5 TCP support
This commit is contained in:
parent
7860e15b65
commit
aff4fb2308
16
app/app.iml
16
app/app.iml
|
@ -64,14 +64,6 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/src/all/jni" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/all/renderscript" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/all/shaders" isTestSource="false" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestAll/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestAll/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestAll/assets" type="java-test-resource" />
|
||||
|
@ -80,6 +72,14 @@
|
|||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestAll/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestAll/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/androidTestAll/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/res" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/resources" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/assets" type="java-test-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/aidl" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/java" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/jni" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/rs" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/testAll/shaders" isTestSource="true" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/res" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/resources" type="java-resource" />
|
||||
<sourceFolder url="file://$MODULE_DIR$/src/debug/assets" type="java-resource" />
|
||||
|
|
|
@ -226,6 +226,10 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
|
|||
pref_dns.getEditText().setHint(def_dns.get(0));
|
||||
pref_dns.setTitle(getString(R.string.setting_dns, prefs.getString("dns", def_dns.get(0))));
|
||||
|
||||
// SOCKS5 parameters
|
||||
screen.findPreference("socks5_addr").setTitle(getString(R.string.setting_socks5_addr, prefs.getString("socks5_addr", "-")));
|
||||
screen.findPreference("socks5_port").setTitle(getString(R.string.setting_socks5_port, prefs.getString("socks5_port", "-")));
|
||||
|
||||
// PCAP parameters
|
||||
screen.findPreference("pcap_record_size").setTitle(getString(R.string.setting_pcap_record_size, prefs.getString("pcap_record_size", "64")));
|
||||
screen.findPreference("pcap_file_size").setTitle(getString(R.string.setting_pcap_file_size, prefs.getString("pcap_file_size", "2")));
|
||||
|
@ -637,6 +641,24 @@ public class ActivitySettings extends AppCompatActivity implements SharedPrefere
|
|||
getPreferenceScreen().findPreference(name).setTitle(
|
||||
getString(R.string.setting_dns, prefs.getString("dns", Util.getDefaultDNS(this).get(0))));
|
||||
|
||||
} else if ("socks5_addr".equals(name)) {
|
||||
String socks5 = prefs.getString("socks5_addr", null);
|
||||
try {
|
||||
checkAddress(socks5);
|
||||
} catch (Throwable ex) {
|
||||
prefs.edit().remove("socks5_addr").apply();
|
||||
((EditTextPreference) getPreferenceScreen().findPreference(name)).setText(null);
|
||||
if (!TextUtils.isEmpty(socks5))
|
||||
Toast.makeText(ActivitySettings.this, ex.toString(), Toast.LENGTH_LONG).show();
|
||||
}
|
||||
ServiceSinkhole.reload("changed " + name, this);
|
||||
getPreferenceScreen().findPreference(name).setTitle(
|
||||
getString(R.string.setting_dns, prefs.getString("socks5_addr", "-")));
|
||||
|
||||
} else if ("socks5_port".equals(name)) {
|
||||
getPreferenceScreen().findPreference(name).setTitle(getString(R.string.setting_socks5_port, prefs.getString(name, "-")));
|
||||
ServiceSinkhole.reload("changed " + name, this);
|
||||
|
||||
} else if ("pcap_record_size".equals(name) || "pcap_file_size".equals(name)) {
|
||||
if ("pcap_record_size".equals(name))
|
||||
getPreferenceScreen().findPreference(name).setTitle(getString(R.string.setting_pcap_record_size, prefs.getString(name, "64")));
|
||||
|
|
|
@ -183,6 +183,8 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
|
||||
private static native void jni_pcap(String name, int record_size, int file_size);
|
||||
|
||||
private native void jni_socks5(String ip, int port);
|
||||
|
||||
private native void jni_done();
|
||||
|
||||
public static void setPcap(boolean enabled, Context context) {
|
||||
|
@ -1113,6 +1115,7 @@ public class ServiceSinkhole extends VpnService implements SharedPreferences.OnS
|
|||
|
||||
if (log || log_app || filter) {
|
||||
int prio = Integer.parseInt(prefs.getString("loglevel", Integer.toString(Log.WARN)));
|
||||
jni_socks5(prefs.getString("socks5_addr", ""), Integer.parseInt(prefs.getString("socks5_port", "0")));
|
||||
jni_start(vpn.getFd(), mapForward.containsKey(53), prio);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@ JavaVM *jvm = NULL;
|
|||
int pipefds[2];
|
||||
pthread_t thread_id = 0;
|
||||
pthread_mutex_t lock;
|
||||
char socks5_addr[INET6_ADDRSTRLEN + 1];
|
||||
int socks5_port = 0;
|
||||
int loglevel = ANDROID_LOG_WARN;
|
||||
|
||||
extern int max_tun_msg;
|
||||
|
@ -103,6 +105,8 @@ Java_eu_faircode_netguard_ServiceSinkhole_jni_1init(JNIEnv *env, jobject instanc
|
|||
args.instance = instance;
|
||||
init(&args);
|
||||
|
||||
*socks5_addr = 0;
|
||||
socks5_port = 0;
|
||||
pcap_file = NULL;
|
||||
|
||||
if (pthread_mutex_init(&lock, NULL))
|
||||
|
@ -294,6 +298,22 @@ Java_eu_faircode_netguard_ServiceSinkhole_jni_1pcap(
|
|||
log_android(ANDROID_LOG_ERROR, "pthread_mutex_unlock failed");
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_eu_faircode_netguard_ServiceSinkhole_jni_1socks5(JNIEnv *env,
|
||||
jobject instance, jstring ip_, jint port) {
|
||||
if (ip_ == NULL || port == 0) {
|
||||
*socks5_addr = 0;
|
||||
socks5_port = 0;
|
||||
} else {
|
||||
const char *ip = (*env)->GetStringUTFChars(env, ip_, 0);
|
||||
strcpy(socks5_addr, ip);
|
||||
socks5_port = port;
|
||||
|
||||
log_android(ANDROID_LOG_WARN, "SOCKS5 %s:%d", socks5_addr, socks5_port);
|
||||
|
||||
(*env)->ReleaseStringUTFChars(env, ip_, ip);
|
||||
}
|
||||
}
|
||||
|
||||
JNIEXPORT void JNICALL
|
||||
Java_eu_faircode_netguard_ServiceSinkhole_jni_1done(JNIEnv *env, jobject instance) {
|
||||
|
|
|
@ -59,6 +59,11 @@
|
|||
#define UID_DELAYTRY 10 // milliseconds
|
||||
#define UID_MAXTRY 3
|
||||
|
||||
#define SOCKS5_NONE 1
|
||||
#define SOCKS5_HELLO 2
|
||||
#define SOCKS5_CONNECT 3
|
||||
#define SOCKS5_CONNECTED 4
|
||||
|
||||
struct arguments {
|
||||
JNIEnv *env;
|
||||
jobject instance;
|
||||
|
@ -163,6 +168,7 @@ struct tcp_session {
|
|||
__be16 dest; // network notation
|
||||
|
||||
uint8_t state;
|
||||
uint8_t socks5;
|
||||
struct segment *forward;
|
||||
};
|
||||
|
||||
|
|
|
@ -20,6 +20,9 @@
|
|||
#include "netguard.h"
|
||||
|
||||
extern struct ng_session *ng_session;
|
||||
extern char socks5_addr[INET6_ADDRSTRLEN + 1];
|
||||
extern int socks5_port;
|
||||
|
||||
extern FILE *pcap_file;
|
||||
|
||||
void clear_tcp_data(struct tcp_session *cur) {
|
||||
|
@ -116,7 +119,10 @@ int monitor_tcp_session(const struct arguments *args, struct ng_session *s, int
|
|||
|
||||
if (s->tcp.state == TCP_LISTEN) {
|
||||
// Check for connected = writable
|
||||
events = events | EPOLLOUT;
|
||||
if (s->tcp.socks5 == SOCKS5_NONE)
|
||||
events = events | EPOLLOUT;
|
||||
else
|
||||
events = events | EPOLLIN;
|
||||
}
|
||||
else if (s->tcp.state == TCP_ESTABLISHED || s->tcp.state == TCP_CLOSE_WAIT) {
|
||||
|
||||
|
@ -257,9 +263,88 @@ void check_tcp_socket(const struct arguments *args,
|
|||
// Assume socket okay
|
||||
if (s->tcp.state == TCP_LISTEN) {
|
||||
// Check socket connect
|
||||
if (ev->events & EPOLLOUT) {
|
||||
log_android(ANDROID_LOG_INFO, "%s connected", session);
|
||||
if (s->tcp.socks5 == SOCKS5_NONE) {
|
||||
if (ev->events & EPOLLOUT) {
|
||||
log_android(ANDROID_LOG_INFO, "%s connected", session);
|
||||
|
||||
if (*socks5_addr && socks5_port) {
|
||||
// https://en.wikipedia.org/wiki/SOCKS
|
||||
s->tcp.socks5 = SOCKS5_HELLO;
|
||||
|
||||
uint8_t buffer[3] = {5, 1, 0};
|
||||
char *h = hex(buffer, sizeof(buffer));
|
||||
log_android(ANDROID_LOG_INFO, "%s sending SOCKS5 hello: %s",
|
||||
session, h);
|
||||
free(h);
|
||||
ssize_t sent = send(s->socket, buffer, sizeof(buffer), MSG_NOSIGNAL);
|
||||
if (sent < 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "%s send SOCKS5 hello error %d: %s",
|
||||
session, errno, strerror(errno));
|
||||
write_rst(args, &s->tcp);
|
||||
}
|
||||
} else
|
||||
s->tcp.socks5 = SOCKS5_CONNECTED;
|
||||
}
|
||||
} else {
|
||||
if (ev->events & EPOLLIN) {
|
||||
uint8_t buffer[32];
|
||||
ssize_t bytes = recv(s->socket, buffer, sizeof(buffer), 0);
|
||||
if (bytes < 0) {
|
||||
log_android(ANDROID_LOG_ERROR, "%s recv SOCKS5 error %d: %s",
|
||||
session, errno, strerror(errno));
|
||||
write_rst(args, &s->tcp);
|
||||
}
|
||||
else {
|
||||
char *h = hex(buffer, (const size_t) bytes);
|
||||
log_android(ANDROID_LOG_INFO, "%s recv SOCKS5 %s", session, h);
|
||||
free(h);
|
||||
|
||||
if (s->tcp.socks5 == SOCKS5_HELLO &&
|
||||
bytes == 2 && buffer[0] == 5 && buffer[1] == 0) {
|
||||
s->tcp.socks5 = SOCKS5_CONNECT;
|
||||
|
||||
*(buffer + 0) = 5; // version
|
||||
*(buffer + 1) = 1; // TCP/IP stream connection
|
||||
*(buffer + 2) = 0; // reserved
|
||||
*(buffer + 3) = (uint8_t) (s->tcp.version == 4 ? 1 : 4);
|
||||
if (s->tcp.version == 4) {
|
||||
memcpy(buffer + 4, &s->tcp.daddr.ip4, 4);
|
||||
*((__be16 *) (buffer + 4 + 4)) = s->tcp.dest;
|
||||
} else {
|
||||
memcpy(buffer + 4, &s->tcp.daddr.ip6, 16);
|
||||
*((__be16 *) (buffer + 4 + 16)) = s->tcp.dest;
|
||||
}
|
||||
|
||||
size_t len = (s->tcp.version == 4 ? 10 : 22);
|
||||
|
||||
h = hex(buffer, len);
|
||||
log_android(ANDROID_LOG_INFO, "%s sending SOCKS5 connect: %s",
|
||||
session, h);
|
||||
free(h);
|
||||
ssize_t sent = send(s->socket, buffer, len, MSG_NOSIGNAL);
|
||||
if (sent < 0) {
|
||||
log_android(ANDROID_LOG_ERROR,
|
||||
"%s send SOCKS5 connect error %d: %s",
|
||||
session, errno, strerror(errno));
|
||||
write_rst(args, &s->tcp);
|
||||
}
|
||||
|
||||
} else if (s->tcp.socks5 == SOCKS5_CONNECT &&
|
||||
bytes == 6 + (s->tcp.version == 4 ? 4 : 16) &&
|
||||
buffer[0] == 5 && buffer[1] == 0) {
|
||||
s->tcp.socks5 = SOCKS5_CONNECTED;
|
||||
log_android(ANDROID_LOG_WARN, "%s SOCKS5 connected", session);
|
||||
|
||||
} else {
|
||||
log_android(ANDROID_LOG_ERROR, "%s recv SOCKS5 state %d",
|
||||
session, s->tcp.socks5);
|
||||
write_rst(args, &s->tcp);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (s->tcp.socks5 == SOCKS5_CONNECTED) {
|
||||
s->tcp.remote_seq++; // remote SYN
|
||||
if (write_syn_ack(args, &s->tcp) >= 0) {
|
||||
s->tcp.time = time(NULL);
|
||||
|
@ -551,6 +636,7 @@ jboolean handle_tcp(const struct arguments *args,
|
|||
s->tcp.source = tcphdr->source;
|
||||
s->tcp.dest = tcphdr->dest;
|
||||
s->tcp.state = TCP_LISTEN;
|
||||
s->tcp.socks5 = SOCKS5_NONE;
|
||||
s->tcp.forward = NULL;
|
||||
s->next = NULL;
|
||||
|
||||
|
@ -846,9 +932,12 @@ int open_tcp_socket(const struct arguments *args,
|
|||
const struct tcp_session *cur, const struct allowed *redirect) {
|
||||
int sock;
|
||||
int version;
|
||||
if (redirect == NULL)
|
||||
version = cur->version;
|
||||
else
|
||||
if (redirect == NULL) {
|
||||
if (*socks5_addr && socks5_port)
|
||||
version = (strstr(socks5_addr, ":") == NULL ? 4 : 6);
|
||||
else
|
||||
version = cur->version;
|
||||
} else
|
||||
version = (strstr(redirect->raddr, ":") == NULL ? 4 : 6);
|
||||
|
||||
// Get TCP socket
|
||||
|
@ -873,14 +962,30 @@ int open_tcp_socket(const struct arguments *args,
|
|||
struct sockaddr_in addr4;
|
||||
struct sockaddr_in6 addr6;
|
||||
if (redirect == NULL) {
|
||||
if (version == 4) {
|
||||
addr4.sin_family = AF_INET;
|
||||
addr4.sin_addr.s_addr = (__be32) cur->daddr.ip4;
|
||||
addr4.sin_port = cur->dest;
|
||||
if (*socks5_addr && socks5_port) {
|
||||
log_android(ANDROID_LOG_WARN, "TCP%d SOCKS5 to %s/%u",
|
||||
version, socks5_addr, socks5_port);
|
||||
|
||||
if (version == 4) {
|
||||
addr4.sin_family = AF_INET;
|
||||
inet_pton(AF_INET, socks5_addr, &addr4.sin_addr);
|
||||
addr4.sin_port = htons(socks5_port);
|
||||
}
|
||||
else {
|
||||
addr6.sin6_family = AF_INET6;
|
||||
inet_pton(AF_INET6, socks5_addr, &addr6.sin6_addr);
|
||||
addr6.sin6_port = htons(socks5_port);
|
||||
}
|
||||
} else {
|
||||
addr6.sin6_family = AF_INET6;
|
||||
memcpy(&addr6.sin6_addr, &cur->daddr.ip6, 16);
|
||||
addr6.sin6_port = cur->dest;
|
||||
if (version == 4) {
|
||||
addr4.sin_family = AF_INET;
|
||||
addr4.sin_addr.s_addr = (__be32) cur->daddr.ip4;
|
||||
addr4.sin_port = cur->dest;
|
||||
} else {
|
||||
addr6.sin6_family = AF_INET6;
|
||||
memcpy(&addr6.sin6_addr, &cur->daddr.ip6, 16);
|
||||
addr6.sin6_port = cur->dest;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
log_android(ANDROID_LOG_WARN, "TCP%d redirect to %s/%u",
|
||||
|
|
|
@ -92,6 +92,8 @@ however it is impossible to guarantee NetGuard will work correctly on every devi
|
|||
<string name="setting_vpn4">VPN IPv4: %s</string>
|
||||
<string name="setting_vpn6">VPN IPv6: %s</string>
|
||||
<string name="setting_dns">VPN DNS: %s</string>
|
||||
<string name="setting_socks5_addr">SOCKS5 address: %s</string>
|
||||
<string name="setting_socks5_port">SOCKS5 port: %s</string>
|
||||
<string name="setting_pcap_record_size">PCAP record size: %s B</string>
|
||||
<string name="setting_pcap_file_size">PCAP max. file size: %s MB</string>
|
||||
|
||||
|
|
|
@ -186,6 +186,16 @@
|
|||
android:dependency="filter"
|
||||
android:inputType="text"
|
||||
android:key="dns" />
|
||||
<EditTextPreference
|
||||
android:dependency="filter"
|
||||
android:hint="127.0.0.1"
|
||||
android:inputType="text"
|
||||
android:key="socks5_addr" />
|
||||
<EditTextPreference
|
||||
android:dependency="filter"
|
||||
android:hint="1080"
|
||||
android:inputType="text"
|
||||
android:key="socks5_port" />
|
||||
<EditTextPreference
|
||||
android:defaultValue="64"
|
||||
android:inputType="number"
|
||||
|
|
|
@ -186,6 +186,16 @@
|
|||
android:dependency="filter"
|
||||
android:inputType="text"
|
||||
android:key="dns" />
|
||||
<EditTextPreference
|
||||
android:dependency="filter"
|
||||
android:hint="127.0.0.1"
|
||||
android:inputType="text"
|
||||
android:key="socks5_addr" />
|
||||
<EditTextPreference
|
||||
android:dependency="filter"
|
||||
android:hint="1080"
|
||||
android:inputType="text"
|
||||
android:key="socks5_port" />
|
||||
<EditTextPreference
|
||||
android:defaultValue="64"
|
||||
android:inputType="number"
|
||||
|
|
Loading…
Reference in New Issue