Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
34af6aeef5 | ||
|
|
d9af2f93e0 | ||
|
|
aae068b7b2 | ||
|
|
7626c1117e | ||
|
|
1188b59b05 | ||
|
|
cbd46d0c95 | ||
|
|
8d02a1a6a4 | ||
|
|
9f67636464 | ||
|
|
abb97e16c4 | ||
|
|
541f8de2cf | ||
|
|
7c31f5e5b4 | ||
|
|
809f4c13e0 | ||
|
|
04725803e4 |
@@ -7,7 +7,7 @@ function try () {
|
|||||||
try pushd src/main
|
try pushd src/main
|
||||||
|
|
||||||
# Build
|
# Build
|
||||||
try $ANDROID_NDK_HOME/ndk-build clean
|
#try $ANDROID_NDK_HOME/ndk-build clean
|
||||||
try $ANDROID_NDK_HOME/ndk-build -j8
|
try $ANDROID_NDK_HOME/ndk-build -j8
|
||||||
|
|
||||||
# copy executables
|
# copy executables
|
||||||
|
|||||||
+3
-3
@@ -1,7 +1,7 @@
|
|||||||
resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)
|
resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)
|
||||||
|
|
||||||
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.4.6")
|
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.4.7")
|
||||||
|
|
||||||
resolvers += Resolver.sbtPluginRepo("snapshots")
|
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||||
|
|
||||||
addSbtPlugin("com.hanhuy.sbt" % "sbt-idea" % "1.7.0-SNAPSHOT")
|
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.7.0-SNAPSHOT")
|
||||||
|
|||||||
@@ -1,7 +1,7 @@
|
|||||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||||
package="com.github.shadowsocks"
|
package="com.github.shadowsocks"
|
||||||
android:versionCode="108"
|
android:versionCode="111"
|
||||||
android:versionName="2.7.0">
|
android:versionName="2.7.3">
|
||||||
|
|
||||||
<uses-permission android:name="android.permission.INTERNET"/>
|
<uses-permission android:name="android.permission.INTERNET"/>
|
||||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||||
|
|||||||
@@ -46,4 +46,5 @@ public class System {
|
|||||||
public static native void exec(String cmd);
|
public static native void exec(String cmd);
|
||||||
public static native String getABI();
|
public static native String getABI();
|
||||||
public static native int sendfd(int fd);
|
public static native int sendfd(int fd);
|
||||||
|
public static native void jniclose(int fd);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -249,13 +249,14 @@ include $(BUILD_EXECUTABLE)
|
|||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
SHADOWSOCKS_SOURCES := local.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c acl.c
|
SHADOWSOCKS_SOURCES := local.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c acl.c android.c
|
||||||
|
|
||||||
LOCAL_MODULE := ss-local
|
LOCAL_MODULE := ss-local
|
||||||
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
|
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
|
||||||
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL \
|
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL \
|
||||||
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H \
|
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H \
|
||||||
-I$(LOCAL_PATH)/libev/ \
|
-I$(LOCAL_PATH)/libev \
|
||||||
|
-I$(LOCAL_PATH)/libancillary \
|
||||||
-I$(LOCAL_PATH)/openssl/include \
|
-I$(LOCAL_PATH)/openssl/include \
|
||||||
-I$(LOCAL_PATH)/shadowsocks-libev/libudns \
|
-I$(LOCAL_PATH)/shadowsocks-libev/libudns \
|
||||||
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
|
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
|
||||||
@@ -263,7 +264,7 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL \
|
|||||||
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include/sodium \
|
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include/sodium \
|
||||||
-I$(LOCAL_PATH)/shadowsocks-libev/libipset/include
|
-I$(LOCAL_PATH)/shadowsocks-libev/libipset/include
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := libev libcrypto libipset libcork libudns libsodium
|
LOCAL_STATIC_LIBRARIES := libev libcrypto libipset libcork libudns libsodium libancillary
|
||||||
|
|
||||||
LOCAL_LDLIBS := -llog
|
LOCAL_LDLIBS := -llog
|
||||||
|
|
||||||
@@ -275,20 +276,21 @@ include $(BUILD_EXECUTABLE)
|
|||||||
|
|
||||||
include $(CLEAR_VARS)
|
include $(CLEAR_VARS)
|
||||||
|
|
||||||
SHADOWSOCKS_SOURCES := tunnel.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c
|
SHADOWSOCKS_SOURCES := tunnel.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c android.c
|
||||||
|
|
||||||
LOCAL_MODULE := ss-tunnel
|
LOCAL_MODULE := ss-tunnel
|
||||||
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
|
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
|
||||||
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL -DUDPRELAY_TUNNEL \
|
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL -DUDPRELAY_TUNNEL \
|
||||||
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H -DSSTUNNEL_JNI \
|
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H -DSSTUNNEL_JNI \
|
||||||
-I$(LOCAL_PATH)/libev/ \
|
-I$(LOCAL_PATH)/libev \
|
||||||
|
-I$(LOCAL_PATH)/libancillary \
|
||||||
-I$(LOCAL_PATH)/shadowsocks-libev/libudns \
|
-I$(LOCAL_PATH)/shadowsocks-libev/libudns \
|
||||||
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
|
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
|
||||||
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include \
|
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include \
|
||||||
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include/sodium \
|
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include/sodium \
|
||||||
-I$(LOCAL_PATH)/openssl/include
|
-I$(LOCAL_PATH)/openssl/include
|
||||||
|
|
||||||
LOCAL_STATIC_LIBRARIES := libev libcrypto libsodium libcork libudns
|
LOCAL_STATIC_LIBRARIES := libev libcrypto libsodium libcork libudns libancillary
|
||||||
|
|
||||||
LOCAL_LDLIBS := -llog
|
LOCAL_LDLIBS := -llog
|
||||||
|
|
||||||
|
|||||||
Submodule src/main/jni/shadowsocks-libev updated: 2b1eef1197...3af591c565
@@ -43,6 +43,10 @@ void Java_com_github_shadowsocks_system_exec(JNIEnv *env, jobject thiz, jstring
|
|||||||
env->ReleaseStringUTFChars(cmd, str);
|
env->ReleaseStringUTFChars(cmd, str);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void Java_com_github_shadowsocks_system_jniclose(JNIEnv *env, jobject thiz, jint fd) {
|
||||||
|
close(fd);
|
||||||
|
}
|
||||||
|
|
||||||
jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint tun_fd) {
|
jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint tun_fd) {
|
||||||
int fd;
|
int fd;
|
||||||
struct sockaddr_un addr;
|
struct sockaddr_un addr;
|
||||||
@@ -75,6 +79,8 @@ jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint t
|
|||||||
static const char *classPathName = "com/github/shadowsocks/System";
|
static const char *classPathName = "com/github/shadowsocks/System";
|
||||||
|
|
||||||
static JNINativeMethod method_table[] = {
|
static JNINativeMethod method_table[] = {
|
||||||
|
{ "jniclose", "(I)V",
|
||||||
|
(void*) Java_com_github_shadowsocks_system_jniclose },
|
||||||
{ "sendfd", "(I)I",
|
{ "sendfd", "(I)I",
|
||||||
(void*) Java_com_github_shadowsocks_system_sendfd },
|
(void*) Java_com_github_shadowsocks_system_sendfd },
|
||||||
{ "exec", "(Ljava/lang/String;)V",
|
{ "exec", "(Ljava/lang/String;)V",
|
||||||
|
|||||||
@@ -932,10 +932,14 @@ class Shadowsocks
|
|||||||
|
|
||||||
private def recovery() {
|
private def recovery() {
|
||||||
val h = showProgress(getString(R.string.recovering))
|
val h = showProgress(getString(R.string.recovering))
|
||||||
serviceStop()
|
|
||||||
spawn {
|
spawn {
|
||||||
reset()
|
reset()
|
||||||
h.sendEmptyMessage(0)
|
handler.post(new Runnable {
|
||||||
|
override def run() {
|
||||||
|
serviceStop
|
||||||
|
h.sendEmptyMessage(0)
|
||||||
|
}
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -61,18 +61,16 @@ import scala.concurrent.ops._
|
|||||||
|
|
||||||
class ShadowsocksVpnService extends VpnService with BaseService {
|
class ShadowsocksVpnService extends VpnService with BaseService {
|
||||||
|
|
||||||
|
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
|
||||||
val TAG = "ShadowsocksVpnService"
|
val TAG = "ShadowsocksVpnService"
|
||||||
|
|
||||||
val VPN_MTU = 1500
|
val VPN_MTU = 1500
|
||||||
val PRIVATE_VLAN = "26.26.26.%s"
|
val PRIVATE_VLAN = "26.26.26.%s"
|
||||||
|
|
||||||
var conn: ParcelFileDescriptor = null
|
var conn: ParcelFileDescriptor = null
|
||||||
var notificationManager: NotificationManager = null
|
var notificationManager: NotificationManager = null
|
||||||
var receiver: BroadcastReceiver = null
|
var receiver: BroadcastReceiver = null
|
||||||
var apps: Array[ProxiedApp] = null
|
var apps: Array[ProxiedApp] = null
|
||||||
var config: Config = null
|
var config: Config = null
|
||||||
|
var vpnThread: ShadowsocksVpnThread = null
|
||||||
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
|
|
||||||
|
|
||||||
def isByass(net: SubnetUtils): Boolean = {
|
def isByass(net: SubnetUtils): Boolean = {
|
||||||
val info = net.getInfo
|
val info = net.getInfo
|
||||||
@@ -95,81 +93,71 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def startShadowsocksDaemon() {
|
override def onBind(intent: Intent): IBinder = {
|
||||||
|
val action = intent.getAction
|
||||||
if (Utils.isLollipopOrAbove && config.route != Route.ALL) {
|
if (VpnService.SERVICE_INTERFACE == action) {
|
||||||
val acl: Array[String] = config.route match {
|
return super.onBind(intent)
|
||||||
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
|
} else if (Action.SERVICE == action) {
|
||||||
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route_full)
|
return binder
|
||||||
}
|
|
||||||
ConfigUtils.printToFile(new File(Path.BASE + "acl.list"))(p => {
|
|
||||||
acl.foreach(item => p.println(item))
|
|
||||||
})
|
|
||||||
}
|
}
|
||||||
|
null
|
||||||
val conf = ConfigUtils
|
|
||||||
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
|
|
||||||
config.sitekey, config.encMethod, 10)
|
|
||||||
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-vpn.conf"))(p => {
|
|
||||||
p.println(conf)
|
|
||||||
})
|
|
||||||
|
|
||||||
val cmd = new ArrayBuffer[String]
|
|
||||||
cmd +=(Path.BASE + "ss-local", "-u"
|
|
||||||
, "-b" , "127.0.0.1"
|
|
||||||
, "-t" , "600"
|
|
||||||
, "-c" , Path.BASE + "ss-local-vpn.conf"
|
|
||||||
, "-f" , Path.BASE + "ss-local-vpn.pid")
|
|
||||||
|
|
||||||
if (Utils.isLollipopOrAbove && config.route != Route.ALL) {
|
|
||||||
cmd += "--acl"
|
|
||||||
cmd += (Path.BASE + "acl.list")
|
|
||||||
}
|
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
|
||||||
Console.runCommand(cmd.mkString(" "))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def startDnsTunnel() = {
|
override def onDestroy() {
|
||||||
val conf = ConfigUtils
|
super.onDestroy()
|
||||||
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163,
|
if (vpnThread != null) vpnThread.stopThread()
|
||||||
config.sitekey, config.encMethod, 10)
|
|
||||||
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-vpn.conf"))(p => {
|
|
||||||
p.println(conf)
|
|
||||||
})
|
|
||||||
val cmd = new ArrayBuffer[String]
|
|
||||||
cmd +=(Path.BASE + "ss-tunnel"
|
|
||||||
, "-u"
|
|
||||||
, "-t" , "10"
|
|
||||||
, "-b" , "127.0.0.1"
|
|
||||||
, "-l" , "8163"
|
|
||||||
, "-L" , "8.8.8.8:53"
|
|
||||||
, "-c" , Path.BASE + "ss-tunnel-vpn.conf"
|
|
||||||
, "-f" , Path.BASE + "ss-tunnel-vpn.pid")
|
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
|
||||||
Console.runCommand(cmd.mkString(" "))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
def startDnsDaemon() {
|
override def onCreate() {
|
||||||
val conf = {
|
super.onCreate()
|
||||||
if (config.route == Route.BYPASS_CHN) {
|
|
||||||
val reject = ConfigUtils.getRejectList(getContext, application)
|
|
||||||
val blackList = ConfigUtils.getBlackList(getContext, application)
|
|
||||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
|
|
||||||
Path.BASE + "pdnsd-vpn.pid", reject, blackList, 8163)
|
|
||||||
} else {
|
|
||||||
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
|
|
||||||
Path.BASE + "pdnsd-vpn.pid", 8163)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-vpn.conf"))(p => {
|
|
||||||
p.println(conf)
|
|
||||||
})
|
|
||||||
val cmd = Path.BASE + "pdnsd -c " + Path.BASE + "pdnsd-vpn.conf"
|
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
|
ConfigUtils.refresh(this)
|
||||||
Console.runCommand(cmd)
|
|
||||||
|
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
|
||||||
|
.asInstanceOf[NotificationManager]
|
||||||
|
|
||||||
|
vpnThread = new ShadowsocksVpnThread(this)
|
||||||
|
vpnThread.start()
|
||||||
|
}
|
||||||
|
|
||||||
|
override def onRevoke() {
|
||||||
|
stopRunner()
|
||||||
|
}
|
||||||
|
|
||||||
|
override def stopRunner() {
|
||||||
|
|
||||||
|
// channge the state
|
||||||
|
changeState(State.STOPPING)
|
||||||
|
|
||||||
|
// send event
|
||||||
|
application.tracker.send(new HitBuilders.EventBuilder()
|
||||||
|
.setCategory(TAG)
|
||||||
|
.setAction("stop")
|
||||||
|
.setLabel(getVersionName)
|
||||||
|
.build())
|
||||||
|
|
||||||
|
// reset VPN
|
||||||
|
killProcesses()
|
||||||
|
|
||||||
|
// close connections
|
||||||
|
if (conn != null) {
|
||||||
|
conn.close()
|
||||||
|
conn = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// stop the service if no callback registered
|
||||||
|
if (getCallbackCount == 0) {
|
||||||
|
stopSelf()
|
||||||
|
}
|
||||||
|
|
||||||
|
// clean up the context
|
||||||
|
if (receiver != null) {
|
||||||
|
unregisterReceiver(receiver)
|
||||||
|
receiver = null
|
||||||
|
}
|
||||||
|
|
||||||
|
// channge the state
|
||||||
|
changeState(State.STOPPED)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getVersionName: String = {
|
def getVersionName: String = {
|
||||||
@@ -184,208 +172,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
version
|
version
|
||||||
}
|
}
|
||||||
|
|
||||||
def startVpn(): Int = {
|
|
||||||
|
|
||||||
val builder = new Builder()
|
|
||||||
builder
|
|
||||||
.setSession(config.profileName)
|
|
||||||
.setMtu(VPN_MTU)
|
|
||||||
.addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
|
|
||||||
.addDnsServer("8.8.8.8")
|
|
||||||
|
|
||||||
if (Utils.isLollipopOrAbove) {
|
|
||||||
|
|
||||||
builder.allowFamily(android.system.OsConstants.AF_INET6)
|
|
||||||
|
|
||||||
if (!config.isGlobalProxy) {
|
|
||||||
val apps = AppManager.getProxiedApps(this, config.proxiedAppString)
|
|
||||||
val pkgSet: mutable.HashSet[String] = new mutable.HashSet[String]
|
|
||||||
for (app <- apps) {
|
|
||||||
if (app.proxied) {
|
|
||||||
pkgSet.add(app.packageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
for (pkg <- pkgSet) {
|
|
||||||
if (!config.isBypassApps) {
|
|
||||||
builder.addAllowedApplication(pkg)
|
|
||||||
} else {
|
|
||||||
builder.addDisallowedApplication(pkg)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (config.isBypassApps) {
|
|
||||||
builder.addDisallowedApplication(this.getPackageName)
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
builder.addDisallowedApplication(this.getPackageName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (InetAddressUtils.isIPv6Address(config.proxy)) {
|
|
||||||
builder.addRoute("0.0.0.0", 0)
|
|
||||||
} else {
|
|
||||||
if (!Utils.isLollipopOrAbove) {
|
|
||||||
config.route match {
|
|
||||||
case Route.BYPASS_LAN =>
|
|
||||||
for (i <- 1 to 223) {
|
|
||||||
if (i != 26 && i != 127) {
|
|
||||||
val addr = i.toString + ".0.0.0"
|
|
||||||
val cidr = addr + "/8"
|
|
||||||
val net = new SubnetUtils(cidr)
|
|
||||||
|
|
||||||
if (!isByass(net) && !isPrivateA(i)) {
|
|
||||||
builder.addRoute(addr, 8)
|
|
||||||
} else {
|
|
||||||
for (j <- 0 to 255) {
|
|
||||||
val subAddr = i.toString + "." + j.toString + ".0.0"
|
|
||||||
val subCidr = subAddr + "/16"
|
|
||||||
val subNet = new SubnetUtils(subCidr)
|
|
||||||
if (!isByass(subNet) && !isPrivateB(i, j)) {
|
|
||||||
builder.addRoute(subAddr, 16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
case Route.BYPASS_CHN =>
|
|
||||||
val list = {
|
|
||||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
|
|
||||||
getResources.getStringArray(R.array.simple_route)
|
|
||||||
} else {
|
|
||||||
getResources.getStringArray(R.array.gfw_route)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
list.foreach(cidr => {
|
|
||||||
val net = new SubnetUtils(cidr)
|
|
||||||
if (!isByass(net)) {
|
|
||||||
val addr = cidr.split('/')
|
|
||||||
builder.addRoute(addr(0), addr(1).toInt)
|
|
||||||
}
|
|
||||||
})
|
|
||||||
case Route.ALL =>
|
|
||||||
for (i <- 1 to 223) {
|
|
||||||
if (i != 26 && i != 127) {
|
|
||||||
val addr = i.toString + ".0.0.0"
|
|
||||||
val cidr = addr + "/8"
|
|
||||||
val net = new SubnetUtils(cidr)
|
|
||||||
|
|
||||||
if (!isByass(net)) {
|
|
||||||
builder.addRoute(addr, 8)
|
|
||||||
} else {
|
|
||||||
for (j <- 0 to 255) {
|
|
||||||
val subAddr = i.toString + "." + j.toString + ".0.0"
|
|
||||||
val subCidr = subAddr + "/16"
|
|
||||||
val subNet = new SubnetUtils(subCidr)
|
|
||||||
if (!isByass(subNet)) {
|
|
||||||
builder.addRoute(subAddr, 16)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
if (config.route == Route.ALL) {
|
|
||||||
builder.addRoute("0.0.0.0", 0)
|
|
||||||
} else {
|
|
||||||
val privateList = getResources.getStringArray(R.array.bypass_private_route)
|
|
||||||
privateList.foreach(cidr => {
|
|
||||||
val addr = cidr.split('/')
|
|
||||||
builder.addRoute(addr(0), addr(1).toInt)
|
|
||||||
})
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
builder.addRoute("8.8.0.0", 16)
|
|
||||||
|
|
||||||
try {
|
|
||||||
conn = builder.establish()
|
|
||||||
} catch {
|
|
||||||
case ex: IllegalStateException =>
|
|
||||||
changeState(State.STOPPED, ex.getMessage)
|
|
||||||
conn = null
|
|
||||||
case ex: Exception => conn = null
|
|
||||||
}
|
|
||||||
|
|
||||||
if (conn == null) {
|
|
||||||
stopRunner()
|
|
||||||
return -1
|
|
||||||
}
|
|
||||||
|
|
||||||
val fd = conn.getFd
|
|
||||||
|
|
||||||
var cmd = (Path.BASE +
|
|
||||||
"tun2socks --netif-ipaddr %s "
|
|
||||||
+ "--netif-netmask 255.255.255.0 "
|
|
||||||
+ "--socks-server-addr 127.0.0.1:%d "
|
|
||||||
+ "--tunfd %d "
|
|
||||||
+ "--tunmtu %d "
|
|
||||||
+ "--loglevel 3 "
|
|
||||||
+ "--pid %stun2socks-vpn.pid")
|
|
||||||
.formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"), config.localPort, fd, VPN_MTU, Path.BASE)
|
|
||||||
|
|
||||||
if (config.isUdpDns)
|
|
||||||
cmd += " --enable-udprelay"
|
|
||||||
else
|
|
||||||
cmd += " --dnsgw %s:8153".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"))
|
|
||||||
|
|
||||||
// if (Utils.isLollipopOrAbove) {
|
|
||||||
// cmd += " --fake-proc"
|
|
||||||
// }
|
|
||||||
|
|
||||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
|
|
||||||
|
|
||||||
Console.runCommand(cmd)
|
|
||||||
|
|
||||||
return fd;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** Called when the activity is first created. */
|
|
||||||
def handleConnection: Boolean = {
|
|
||||||
startShadowsocksDaemon()
|
|
||||||
if (!config.isUdpDns) {
|
|
||||||
startDnsDaemon()
|
|
||||||
startDnsTunnel()
|
|
||||||
}
|
|
||||||
|
|
||||||
val fd = startVpn()
|
|
||||||
|
|
||||||
if (fd == -1) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
Thread.sleep(1000)
|
|
||||||
if (System.sendfd(fd) == -1) {
|
|
||||||
false
|
|
||||||
} else {
|
|
||||||
true
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
override def onBind(intent: Intent): IBinder = {
|
|
||||||
val action = intent.getAction
|
|
||||||
if (VpnService.SERVICE_INTERFACE == action) {
|
|
||||||
return super.onBind(intent)
|
|
||||||
} else if (Action.SERVICE == action) {
|
|
||||||
return binder
|
|
||||||
}
|
|
||||||
null
|
|
||||||
}
|
|
||||||
|
|
||||||
override def onCreate() {
|
|
||||||
super.onCreate()
|
|
||||||
|
|
||||||
ConfigUtils.refresh(this)
|
|
||||||
|
|
||||||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
|
|
||||||
.asInstanceOf[NotificationManager]
|
|
||||||
}
|
|
||||||
|
|
||||||
override def onRevoke() {
|
|
||||||
stopRunner()
|
|
||||||
}
|
|
||||||
|
|
||||||
def killProcesses() {
|
def killProcesses() {
|
||||||
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "tun2socks")) {
|
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "tun2socks")) {
|
||||||
try {
|
try {
|
||||||
@@ -471,40 +257,188 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
override def stopRunner() {
|
/** Called when the activity is first created. */
|
||||||
|
def handleConnection: Boolean = {
|
||||||
// channge the state
|
startShadowsocksDaemon()
|
||||||
changeState(State.STOPPING)
|
if (!config.isUdpDns) {
|
||||||
|
startDnsDaemon()
|
||||||
// send event
|
startDnsTunnel()
|
||||||
application.tracker.send(new HitBuilders.EventBuilder()
|
|
||||||
.setCategory(TAG)
|
|
||||||
.setAction("stop")
|
|
||||||
.setLabel(getVersionName)
|
|
||||||
.build())
|
|
||||||
|
|
||||||
// reset VPN
|
|
||||||
killProcesses()
|
|
||||||
|
|
||||||
// close connections
|
|
||||||
if (conn != null) {
|
|
||||||
conn.close()
|
|
||||||
conn = null
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// stop the service if no callback registered
|
val fd = startVpn()
|
||||||
if (getCallbackCount == 0) {
|
|
||||||
stopSelf()
|
if (fd != -1) {
|
||||||
|
var tries = 1
|
||||||
|
while (tries < 5) {
|
||||||
|
Thread.sleep(1000 * tries)
|
||||||
|
if (System.sendfd(fd) != -1) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
tries += 1
|
||||||
|
}
|
||||||
|
}
|
||||||
|
false
|
||||||
|
}
|
||||||
|
|
||||||
|
def startShadowsocksDaemon() {
|
||||||
|
|
||||||
|
if (config.route != Route.ALL) {
|
||||||
|
val acl: Array[String] = config.route match {
|
||||||
|
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
|
||||||
|
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route_full)
|
||||||
|
}
|
||||||
|
ConfigUtils.printToFile(new File(Path.BASE + "acl.list"))(p => {
|
||||||
|
acl.foreach(item => p.println(item))
|
||||||
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
// clean up the context
|
val conf = ConfigUtils
|
||||||
if (receiver != null) {
|
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
|
||||||
unregisterReceiver(receiver)
|
config.sitekey, config.encMethod, 10)
|
||||||
receiver = null
|
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-vpn.conf"))(p => {
|
||||||
|
p.println(conf)
|
||||||
|
})
|
||||||
|
|
||||||
|
val cmd = new ArrayBuffer[String]
|
||||||
|
cmd +=(Path.BASE + "ss-local", "-V", "-u"
|
||||||
|
, "-b", "127.0.0.1"
|
||||||
|
, "-t", "600"
|
||||||
|
, "-c", Path.BASE + "ss-local-vpn.conf"
|
||||||
|
, "-f", Path.BASE + "ss-local-vpn.pid")
|
||||||
|
|
||||||
|
if (config.route != Route.ALL) {
|
||||||
|
cmd += "--acl"
|
||||||
|
cmd += (Path.BASE + "acl.list")
|
||||||
}
|
}
|
||||||
|
|
||||||
// channge the state
|
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||||
changeState(State.STOPPED)
|
Console.runCommand(cmd.mkString(" "))
|
||||||
|
}
|
||||||
|
|
||||||
|
def startDnsTunnel() = {
|
||||||
|
val conf = ConfigUtils
|
||||||
|
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163,
|
||||||
|
config.sitekey, config.encMethod, 10)
|
||||||
|
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-vpn.conf"))(p => {
|
||||||
|
p.println(conf)
|
||||||
|
})
|
||||||
|
val cmd = new ArrayBuffer[String]
|
||||||
|
cmd +=(Path.BASE + "ss-tunnel"
|
||||||
|
, "-V"
|
||||||
|
, "-u"
|
||||||
|
, "-t", "10"
|
||||||
|
, "-b", "127.0.0.1"
|
||||||
|
, "-l", "8163"
|
||||||
|
, "-L", "8.8.8.8:53"
|
||||||
|
, "-c", Path.BASE + "ss-tunnel-vpn.conf"
|
||||||
|
, "-f", Path.BASE + "ss-tunnel-vpn.pid")
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||||
|
Console.runCommand(cmd.mkString(" "))
|
||||||
|
}
|
||||||
|
|
||||||
|
def startDnsDaemon() {
|
||||||
|
val conf = {
|
||||||
|
if (config.route == Route.BYPASS_CHN) {
|
||||||
|
val reject = ConfigUtils.getRejectList(getContext, application)
|
||||||
|
val blackList = ConfigUtils.getBlackList(getContext, application)
|
||||||
|
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
|
||||||
|
Path.BASE + "pdnsd-vpn.pid", reject, blackList, 8163)
|
||||||
|
} else {
|
||||||
|
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
|
||||||
|
Path.BASE + "pdnsd-vpn.pid", 8163)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-vpn.conf"))(p => {
|
||||||
|
p.println(conf)
|
||||||
|
})
|
||||||
|
val cmd = Path.BASE + "pdnsd -c " + Path.BASE + "pdnsd-vpn.conf"
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
|
||||||
|
Console.runCommand(cmd)
|
||||||
|
}
|
||||||
|
|
||||||
|
override def getContext = getBaseContext
|
||||||
|
|
||||||
|
def startVpn(): Int = {
|
||||||
|
|
||||||
|
val builder = new Builder()
|
||||||
|
builder
|
||||||
|
.setSession(config.profileName)
|
||||||
|
.setMtu(VPN_MTU)
|
||||||
|
.addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
|
||||||
|
.addDnsServer("8.8.8.8")
|
||||||
|
|
||||||
|
if (Utils.isLollipopOrAbove) {
|
||||||
|
|
||||||
|
builder.allowFamily(android.system.OsConstants.AF_INET6)
|
||||||
|
|
||||||
|
if (!config.isGlobalProxy) {
|
||||||
|
val apps = AppManager.getProxiedApps(this, config.proxiedAppString)
|
||||||
|
val pkgSet: mutable.HashSet[String] = new mutable.HashSet[String]
|
||||||
|
for (app <- apps) {
|
||||||
|
if (app.proxied) {
|
||||||
|
pkgSet.add(app.packageName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
for (pkg <- pkgSet) {
|
||||||
|
if (!config.isBypassApps) {
|
||||||
|
builder.addAllowedApplication(pkg)
|
||||||
|
} else {
|
||||||
|
builder.addDisallowedApplication(pkg)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.route == Route.ALL) {
|
||||||
|
builder.addRoute("0.0.0.0", 0)
|
||||||
|
} else {
|
||||||
|
val privateList = getResources.getStringArray(R.array.bypass_private_route)
|
||||||
|
privateList.foreach(cidr => {
|
||||||
|
val addr = cidr.split('/')
|
||||||
|
builder.addRoute(addr(0), addr(1).toInt)
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
|
builder.addRoute("8.8.0.0", 16)
|
||||||
|
|
||||||
|
try {
|
||||||
|
conn = builder.establish()
|
||||||
|
} catch {
|
||||||
|
case ex: IllegalStateException =>
|
||||||
|
changeState(State.STOPPED, ex.getMessage)
|
||||||
|
conn = null
|
||||||
|
case ex: Exception => conn = null
|
||||||
|
}
|
||||||
|
|
||||||
|
if (conn == null) {
|
||||||
|
stopRunner()
|
||||||
|
return -1
|
||||||
|
}
|
||||||
|
|
||||||
|
val fd = conn.getFd
|
||||||
|
|
||||||
|
var cmd = (Path.BASE +
|
||||||
|
"tun2socks --netif-ipaddr %s "
|
||||||
|
+ "--netif-netmask 255.255.255.0 "
|
||||||
|
+ "--socks-server-addr 127.0.0.1:%d "
|
||||||
|
+ "--tunfd %d "
|
||||||
|
+ "--tunmtu %d "
|
||||||
|
+ "--loglevel 3 "
|
||||||
|
+ "--pid %stun2socks-vpn.pid")
|
||||||
|
.formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"), config.localPort, fd, VPN_MTU, Path.BASE)
|
||||||
|
|
||||||
|
if (config.isUdpDns)
|
||||||
|
cmd += " --enable-udprelay"
|
||||||
|
else
|
||||||
|
cmd += " --dnsgw %s:8153".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"))
|
||||||
|
|
||||||
|
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
|
||||||
|
|
||||||
|
Console.runCommand(cmd)
|
||||||
|
|
||||||
|
return fd
|
||||||
}
|
}
|
||||||
|
|
||||||
override def stopBackgroundService() {
|
override def stopBackgroundService() {
|
||||||
@@ -514,6 +448,4 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
|||||||
override def getTag = TAG
|
override def getTag = TAG
|
||||||
|
|
||||||
override def getServiceMode = Mode.VPN
|
override def getServiceMode = Mode.VPN
|
||||||
|
|
||||||
override def getContext = getBaseContext
|
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -0,0 +1,138 @@
|
|||||||
|
/*
|
||||||
|
* Shadowsocks - A shadowsocks client for Android
|
||||||
|
* Copyright (C) 2015 <max.c.lv@gmail.com>
|
||||||
|
*
|
||||||
|
* This program is free software: you can redistribute it and/or modify
|
||||||
|
* it under the terms of the GNU General Public License as published by
|
||||||
|
* the Free Software Foundation, either version 3 of the License, or
|
||||||
|
* (at your option) any later version.
|
||||||
|
*
|
||||||
|
* This program is distributed in the hope that it will be useful,
|
||||||
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||||
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||||
|
* GNU General Public License for more details.
|
||||||
|
*
|
||||||
|
* You should have received a copy of the GNU General Public License
|
||||||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||||
|
*
|
||||||
|
*
|
||||||
|
* ___====-_ _-====___
|
||||||
|
* _--^^^#####// \\#####^^^--_
|
||||||
|
* _-^##########// ( ) \\##########^-_
|
||||||
|
* -############// |\^^/| \\############-
|
||||||
|
* _/############// (@::@) \\############\_
|
||||||
|
* /#############(( \\// ))#############\
|
||||||
|
* -###############\\ (oo) //###############-
|
||||||
|
* -#################\\ / VV \ //#################-
|
||||||
|
* -###################\\/ \//###################-
|
||||||
|
* _#/|##########/\######( /\ )######/\##########|\#_
|
||||||
|
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
|
||||||
|
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
|
||||||
|
* ` ` ` ` / | | | | \ ' ' ' '
|
||||||
|
* ( | | | | )
|
||||||
|
* __\ | | | | /__
|
||||||
|
* (vvv(VVV)(VVV)vvv)
|
||||||
|
*
|
||||||
|
* HERE BE DRAGONS
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
|
||||||
|
package com.github.shadowsocks
|
||||||
|
|
||||||
|
import java.io.{File, FileDescriptor, IOException}
|
||||||
|
import java.util.concurrent.Executors
|
||||||
|
|
||||||
|
import android.net.{LocalServerSocket, LocalSocket, LocalSocketAddress}
|
||||||
|
import android.util.Log
|
||||||
|
|
||||||
|
class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
|
||||||
|
|
||||||
|
val TAG = "ShadowsocksVpnService"
|
||||||
|
val PATH = "/data/data/com.github.shadowsocks/protect_path"
|
||||||
|
|
||||||
|
var isRunning: Boolean = true
|
||||||
|
var serverSocket: LocalServerSocket = null
|
||||||
|
|
||||||
|
def stopThread() {
|
||||||
|
isRunning = false
|
||||||
|
if (serverSocket != null) {
|
||||||
|
serverSocket.close()
|
||||||
|
serverSocket = null
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
override def run(): Unit = {
|
||||||
|
|
||||||
|
try {
|
||||||
|
new File(PATH).delete()
|
||||||
|
} catch {
|
||||||
|
case _: Exception => // ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
val localSocket = new LocalSocket
|
||||||
|
|
||||||
|
localSocket.bind(new LocalSocketAddress(PATH, LocalSocketAddress.Namespace.FILESYSTEM))
|
||||||
|
serverSocket = new LocalServerSocket(localSocket.getFileDescriptor)
|
||||||
|
} catch {
|
||||||
|
case e: IOException =>
|
||||||
|
Log.e(TAG, "unable to bind", e)
|
||||||
|
vpnService.stopBackgroundService()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
val pool = Executors.newFixedThreadPool(4)
|
||||||
|
|
||||||
|
while (isRunning) {
|
||||||
|
|
||||||
|
try {
|
||||||
|
val socket = serverSocket.accept()
|
||||||
|
|
||||||
|
pool.execute(new Runnable {
|
||||||
|
override def run() {
|
||||||
|
try {
|
||||||
|
val input = socket.getInputStream
|
||||||
|
val output = socket.getOutputStream
|
||||||
|
|
||||||
|
input.read()
|
||||||
|
|
||||||
|
val fds = socket.getAncillaryFileDescriptors
|
||||||
|
|
||||||
|
if (fds.length > 0) {
|
||||||
|
var ret = false
|
||||||
|
|
||||||
|
val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$")
|
||||||
|
val fd = getInt.invoke(fds(0)).asInstanceOf[Int]
|
||||||
|
ret = vpnService.protect(fd)
|
||||||
|
|
||||||
|
// Trick to close file decriptor
|
||||||
|
System.jniclose(fd)
|
||||||
|
|
||||||
|
if (ret) {
|
||||||
|
output.write(0)
|
||||||
|
} else {
|
||||||
|
output.write(1)
|
||||||
|
}
|
||||||
|
|
||||||
|
input.close()
|
||||||
|
output.close()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
case e: Exception =>
|
||||||
|
Log.e(TAG, "Error when protect socket", e)
|
||||||
|
}
|
||||||
|
|
||||||
|
// close socket
|
||||||
|
try {
|
||||||
|
socket.close()
|
||||||
|
} catch {
|
||||||
|
case _: Exception => // ignore
|
||||||
|
}
|
||||||
|
|
||||||
|
}})
|
||||||
|
} catch {
|
||||||
|
case e: IOException => Log.e(TAG, "Error when accept socket", e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user