Compare commits

...
16 Commits
Author SHA1 Message Date
Max Lv c2c7cabbaf refine dns resolve 2015-07-30 13:35:04 +08:00
Max Lv 092d880c7d refine binary install 2015-07-29 13:49:08 +08:00
Max Lv c208c4bf50 refine profile initilizing 2015-07-28 15:49:05 +08:00
Max Lv 34af6aeef5 refine the IPC to tun2socks 2015-07-28 14:48:21 +08:00
Max Lv d9af2f93e0 fix plugin 2015-07-27 11:09:52 +08:00
Max Lv aae068b7b2 fix a UI issue 2015-07-26 10:22:15 +08:00
Max Lv 7626c1117e bump version 2015-07-26 09:36:04 +08:00
Max Lv 1188b59b05 update shadowsocks-libev 2015-07-26 09:10:46 +08:00
Max Lv cbd46d0c95 enable acl on devices before 5.0 2015-07-26 09:10:17 +08:00
Max Lv 8d02a1a6a4 Merge pull request #334 from shadowsocks/android-ipc
Improve the compatibility by using IPC
2015-07-25 06:59:37 +08:00
Max Lv 9f67636464 update shadowsocks-libev 2015-07-25 06:57:37 +08:00
Max Lv abb97e16c4 play tricks to close fd manually 2015-07-24 23:07:58 +08:00
Max Lv 541f8de2cf close socket properly 2015-07-24 22:35:39 +08:00
Max Lv 7c31f5e5b4 refine IPC handling 2015-07-24 21:38:49 +08:00
Max Lv 809f4c13e0 add management thread 2015-07-24 18:56:01 +08:00
Max Lv 04725803e4 add android ipc feature 2015-07-24 14:15:43 +08:00
12 changed files with 470 additions and 345 deletions
+1 -1
View File
@@ -7,7 +7,7 @@ function try () {
try pushd src/main
# Build
try $ANDROID_NDK_HOME/ndk-build clean
#try $ANDROID_NDK_HOME/ndk-build clean
try $ANDROID_NDK_HOME/ndk-build -j8
# copy executables
+14 -12
View File
@@ -10,20 +10,21 @@ import IPy
def main():
china_list_set = IPy.IPSet()
for line in sys.stdin:
line_params = line.split("|")
if len(line_params) < 5 or line_params[2] != "ipv4" or line_params[1] != "CN":
continue
ip_addr = line_params[3]
ip_length = float(line_params[4])
ip_mask = 32 - int(math.ceil(math.log(ip_length, 2)))
china_list_set.add(IPy.IP("%s/%d" % (ip_addr, ip_mask)))
# for line in sys.stdin:
# line_params = line.split("|")
# if len(line_params) < 5 or line_params[2] != "ipv4" or line_params[1] != "CN":
# continue
# ip_addr = line_params[3]
# ip_length = float(line_params[4])
# ip_mask = 32 - int(math.ceil(math.log(ip_length, 2)))
# china_list_set.add(IPy.IP("%s/%d" % (ip_addr, ip_mask)))
# 添加内网地址
internal_list = IPy.IPSet(map(IPy.IP, [
"0.0.0.0/8",
"10.0.0.0/8",
"100.64.0.0/10",
"114.114.114.0/24",
"127.0.0.0/8",
"169.254.0.0/16",
"172.16.0.0/12",
@@ -34,6 +35,7 @@ def main():
"198.18.0.0/15",
"198.51.100.0/24",
"203.0.113.0/24",
"223.5.5.0/24",
"224.0.0.0/4",
"240.0.0.0/4",
]))
@@ -45,10 +47,10 @@ def main():
for ip in china_list_set:
all.discard(ip)
filter = itertools.ifilter(lambda x: len(x) <= 65536, all)
for ip in filter:
all.discard(ip)
all.add(IPy.IP(ip.strNormal(0)).make_net('255.255.0.0'))
# filter = itertools.ifilter(lambda x: len(x) <= 65536, all)
# for ip in filter:
# all.discard(ip)
# all.add(IPy.IP(ip.strNormal(0)).make_net('255.255.0.0'))
# 输出结果
for ip in all:
+3 -3
View File
@@ -1,7 +1,7 @@
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")
+2 -2
View File
@@ -1,7 +1,7 @@
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.shadowsocks"
android:versionCode="108"
android:versionName="2.7.0">
android:versionCode="112"
android:versionName="2.7.4">
<uses-permission android:name="android.permission.INTERNET"/>
<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 String getABI();
public static native int sendfd(int fd);
public static native void jniclose(int fd);
}
+8 -6
View File
@@ -249,13 +249,14 @@ include $(BUILD_EXECUTABLE)
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_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL \
-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)/shadowsocks-libev/libudns \
-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/libipset/include
LOCAL_STATIC_LIBRARIES := libev libcrypto libipset libcork libudns libsodium
LOCAL_STATIC_LIBRARIES := libev libcrypto libipset libcork libudns libsodium libancillary
LOCAL_LDLIBS := -llog
@@ -275,20 +276,21 @@ include $(BUILD_EXECUTABLE)
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_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL -DUDPRELAY_TUNNEL \
-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/libcork/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include/sodium \
-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
+6
View File
@@ -43,6 +43,10 @@ void Java_com_github_shadowsocks_system_exec(JNIEnv *env, jobject thiz, jstring
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) {
int fd;
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 JNINativeMethod method_table[] = {
{ "jniclose", "(I)V",
(void*) Java_com_github_shadowsocks_system_jniclose },
{ "sendfd", "(I)I",
(void*) Java_com_github_shadowsocks_system_sendfd },
{ "exec", "(Ljava/lang/String;)V",
+39 -2
View File
@@ -85,7 +85,25 @@
<item>101.0.0.0/8</item>
<item>102.0.0.0/7</item>
<item>104.0.0.0/5</item>
<item>112.0.0.0/5</item>
<item>112.0.0.0/7</item>
<item>114.0.0.0/10</item>
<item>114.64.0.0/11</item>
<item>114.96.0.0/12</item>
<item>114.112.0.0/15</item>
<item>114.114.0.0/18</item>
<item>114.114.64.0/19</item>
<item>114.114.96.0/20</item>
<item>114.114.112.0/23</item>
<item>114.114.115.0/24</item>
<item>114.114.116.0/22</item>
<item>114.114.120.0/21</item>
<item>114.114.128.0/17</item>
<item>114.115.0.0/16</item>
<item>114.116.0.0/14</item>
<item>114.120.0.0/13</item>
<item>114.128.0.0/9</item>
<item>115.0.0.0/8</item>
<item>116.0.0.0/6</item>
<item>120.0.0.0/6</item>
<item>124.0.0.0/7</item>
<item>126.0.0.0/8</item>
@@ -190,6 +208,25 @@
<item>203.64.0.0/10</item>
<item>203.128.0.0/9</item>
<item>204.0.0.0/6</item>
<item>208.0.0.0/4</item>
<item>208.0.0.0/5</item>
<item>216.0.0.0/6</item>
<item>220.0.0.0/7</item>
<item>222.0.0.0/8</item>
<item>223.0.0.0/14</item>
<item>223.4.0.0/16</item>
<item>223.5.0.0/22</item>
<item>223.5.4.0/24</item>
<item>223.5.6.0/23</item>
<item>223.5.8.0/21</item>
<item>223.5.16.0/20</item>
<item>223.5.32.0/19</item>
<item>223.5.64.0/18</item>
<item>223.5.128.0/17</item>
<item>223.6.0.0/15</item>
<item>223.8.0.0/13</item>
<item>223.16.0.0/12</item>
<item>223.32.0.0/11</item>
<item>223.64.0.0/10</item>
<item>223.128.0.0/9</item>
</string-array>
</resources>
@@ -474,22 +474,22 @@ class Shadowsocks
addPreferencesFromResource(R.xml.pref_all)
// Initialize the profile
currentProfile = {
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
}
// Update the profile
if (!status.getBoolean(getVersionName, false)) {
val h = showProgress(getString(R.string.initializing))
status.edit.putBoolean(getVersionName, true).apply()
currentProfile = profileManager.create()
spawn {
reset()
currentProfile = profileManager.create()
install()
h.sendEmptyMessage(0)
}
}
// Initialize the profile
currentProfile = {
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
}
// Initialize drawer
menuAdapter.setActiveId(settings.getInt(Key.profileId, -1))
menuAdapter.setListener(this)
@@ -916,10 +916,7 @@ class Shadowsocks
Console.runRootCommand(ab.toArray)
}
def reset() {
crashRecovery()
def install() {
copyAssets(System.getABI)
val ab = new ArrayBuffer[String]
@@ -927,15 +924,25 @@ class Shadowsocks
ab.append("chmod 755 " + Path.BASE + executable)
}
Console.runCommand(ab.toArray)
}
def reset() {
crashRecovery()
install()
}
private def recovery() {
val h = showProgress(getString(R.string.recovering))
serviceStop()
spawn {
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 {
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
val TAG = "ShadowsocksVpnService"
val VPN_MTU = 1500
val PRIVATE_VLAN = "26.26.26.%s"
var conn: ParcelFileDescriptor = null
var notificationManager: NotificationManager = null
var receiver: BroadcastReceiver = null
var apps: Array[ProxiedApp] = null
var config: Config = null
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
var vpnThread: ShadowsocksVpnThread = null
def isByass(net: SubnetUtils): Boolean = {
val info = net.getInfo
@@ -95,81 +93,71 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
}
def startShadowsocksDaemon() {
if (Utils.isLollipopOrAbove && 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))
})
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
}
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(" "))
null
}
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"
, "-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(" "))
override def onDestroy() {
super.onDestroy()
if (vpnThread != null) vpnThread.stopThread()
}
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"
override def onCreate() {
super.onCreate()
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
Console.runCommand(cmd)
ConfigUtils.refresh(this)
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 = {
@@ -184,208 +172,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
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() {
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "tun2socks")) {
try {
@@ -471,40 +257,188 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
}
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
/** Called when the activity is first created. */
def handleConnection: Boolean = {
startShadowsocksDaemon()
if (!config.isUdpDns) {
startDnsDaemon()
startDnsTunnel()
}
// stop the service if no callback registered
if (getCallbackCount == 0) {
stopSelf()
val fd = startVpn()
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
if (receiver != null) {
unregisterReceiver(receiver)
receiver = 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", "-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
changeState(State.STOPPED)
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
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() {
@@ -514,6 +448,4 @@ class ShadowsocksVpnService extends VpnService with BaseService {
override def getTag = TAG
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)
}
}
}
}