Commit 7c31f5e5 authored by Max Lv's avatar Max Lv

refine IPC handling

parent 809f4c13
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.shadowsocks"
android:versionCode="108"
android:versionName="2.7.0">
android:versionCode="109"
android:versionName="2.7.1">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
......
......@@ -276,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
......
Subproject commit 680007589c5aaafede801081e46b013b27c93939
Subproject commit 3d2b98016149ddf4c0c6912b16f56f2a56cb658b
......@@ -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,6 +93,192 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
}
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 onDestroy() {
super.onDestroy()
if (vpnThread != null) vpnThread.stopThread()
}
override def onCreate() {
super.onCreate()
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 = {
var version: String = null
try {
val pi: PackageInfo = getPackageManager.getPackageInfo(getPackageName, 0)
version = pi.versionName
} catch {
case e: PackageManager.NameNotFoundException =>
version = "Package name not found"
}
version
}
def killProcesses() {
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "tun2socks")) {
try {
val pid = scala.io.Source.fromFile(Path.BASE + task + "-vpn.pid").mkString.trim.toInt
Process.killProcess(pid)
} catch {
case e: Throwable => Log.e(TAG, "unable to kill " + task)
}
}
}
override def startRunner(c: Config) {
config = c
// ensure the VPNService is prepared
if (VpnService.prepare(this) != null) {
val i = new Intent(this, classOf[ShadowsocksRunnerActivity])
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(i)
return
}
// send event
application.tracker.send(new HitBuilders.EventBuilder()
.setCategory(TAG)
.setAction("start")
.setLabel(getVersionName)
.build())
// register close receiver
val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN)
receiver = new BroadcastReceiver {
def onReceive(p1: Context, p2: Intent) {
Toast.makeText(p1, R.string.stopping, Toast.LENGTH_SHORT)
stopRunner()
}
}
registerReceiver(receiver, filter)
changeState(State.CONNECTING)
spawn {
if (config.proxy == "198.199.101.152") {
val holder = getApplication.asInstanceOf[ShadowsocksApplication].containerHolder
try {
config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config)
} catch {
case ex: Exception =>
changeState(State.STOPPED, getString(R.string.service_failed))
stopRunner()
config = null
}
}
if (config != null) {
// reset the context
killProcesses()
// Resolve the server address
var resolved: Boolean = false
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
!InetAddressUtils.isIPv6Address(config.proxy)) {
Utils.resolve(config.proxy, enableIPv6 = true) match {
case Some(addr) =>
config.proxy = addr
resolved = true
case None => resolved = false
}
} else {
resolved = true
}
if (resolved && handleConnection) {
changeState(State.CONNECTED)
} else {
changeState(State.STOPPED, getString(R.string.service_failed))
stopRunner()
}
}
}
}
/** 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
}
}
}
def startShadowsocksDaemon() {
if (Utils.isLollipopOrAbove && config.route != Route.ALL) {
......@@ -115,13 +299,13 @@ class ShadowsocksVpnService extends VpnService with BaseService {
})
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")
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 (Utils.isLollipopOrAbove && config.route != Route.ALL) {
if (config.route != Route.ALL) {
cmd += "--acl"
cmd += (Path.BASE + "acl.list")
}
......@@ -139,13 +323,14 @@ class ShadowsocksVpnService extends VpnService with BaseService {
})
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")
, "-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(" "))
......@@ -172,17 +357,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
Console.runCommand(cmd)
}
def getVersionName: String = {
var version: String = null
try {
val pi: PackageInfo = getPackageManager.getPackageInfo(getPackageName, 0)
version = pi.versionName
} catch {
case e: PackageManager.NameNotFoundException =>
version = "Package name not found"
}
version
}
override def getContext = getBaseContext
def startVpn(): Int = {
......@@ -265,174 +440,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
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]
new Thread(new ShadowsocksVpnThread(this)).start()
}
override def onRevoke() {
stopRunner()
}
def killProcesses() {
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "tun2socks")) {
try {
val pid = scala.io.Source.fromFile(Path.BASE + task + "-vpn.pid").mkString.trim.toInt
Process.killProcess(pid)
} catch {
case e: Throwable => Log.e(TAG, "unable to kill " + task)
}
}
}
override def startRunner(c: Config) {
config = c
// ensure the VPNService is prepared
if (VpnService.prepare(this) != null) {
val i = new Intent(this, classOf[ShadowsocksRunnerActivity])
i.addFlags(Intent.FLAG_ACTIVITY_NEW_TASK)
startActivity(i)
return
}
// send event
application.tracker.send(new HitBuilders.EventBuilder()
.setCategory(TAG)
.setAction("start")
.setLabel(getVersionName)
.build())
// register close receiver
val filter = new IntentFilter()
filter.addAction(Intent.ACTION_SHUTDOWN)
receiver = new BroadcastReceiver {
def onReceive(p1: Context, p2: Intent) {
Toast.makeText(p1, R.string.stopping, Toast.LENGTH_SHORT)
stopRunner()
}
}
registerReceiver(receiver, filter)
changeState(State.CONNECTING)
spawn {
if (config.proxy == "198.199.101.152") {
val holder = getApplication.asInstanceOf[ShadowsocksApplication].containerHolder
try {
config = ConfigUtils.getPublicConfig(getBaseContext, holder.getContainer, config)
} catch {
case ex: Exception =>
changeState(State.STOPPED, getString(R.string.service_failed))
stopRunner()
config = null
}
}
if (config != null) {
// reset the context
killProcesses()
// Resolve the server address
var resolved: Boolean = false
if (!InetAddressUtils.isIPv4Address(config.proxy) &&
!InetAddressUtils.isIPv6Address(config.proxy)) {
Utils.resolve(config.proxy, enableIPv6 = true) match {
case Some(addr) =>
config.proxy = addr
resolved = true
case None => resolved = false
}
} else {
resolved = true
}
if (resolved && handleConnection) {
changeState(State.CONNECTED)
} else {
changeState(State.STOPPED, getString(R.string.service_failed))
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)
}
override def stopBackgroundService() {
stopSelf()
}
......@@ -440,6 +447,4 @@ class ShadowsocksVpnService extends VpnService with BaseService {
override def getTag = TAG
override def getServiceMode = Mode.VPN
override def getContext = getBaseContext
}
......@@ -40,23 +40,33 @@
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 Runnable {
class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
val TAG = "ShadowsocksVpnService"
val PATH = "/data/data/com.github.shadowsocks/protect_path"
override def run(): Unit = {
var isRunning: Boolean = true
var serverSocket: LocalServerSocket = null
def stopThread() {
isRunning = false
if (serverSocket != null) {
serverSocket.close()
serverSocket = null
}
}
var serverSocket: LocalServerSocket = null
override def run(): Unit = {
try {
new File(PATH).delete()
} catch {
case _ => // ignore
case _: Exception => // ignore
}
try {
......@@ -71,42 +81,49 @@ class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Runnable {
return
}
Log.d(TAG, "start to accept connections")
val pool = Executors.newFixedThreadPool(4)
while (true) {
while (isRunning) {
try {
val socket = serverSocket.accept()
val input = socket.getInputStream
val output = socket.getOutputStream
pool.execute(new Runnable {
override def run(){
try {
val input = socket.getInputStream
val output = socket.getOutputStream
input.read()
input.read()
Log.d(TAG, "test")
val fds = socket.getAncillaryFileDescriptors
val fds = socket.getAncillaryFileDescriptors
if (fds.length > 0) {
var ret = false
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)
val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$")
val fd = getInt.invoke(fds(0)).asInstanceOf[Int]
ret = vpnService.protect(fd)
if (ret) {
output.write(0)
} else {
output.write(1)
}
if (ret) {
socket.setFileDescriptorsForSend(fds)
output.write(0)
}
input.close()
output.close()
}
input.close()
output.close()
}
socket.close()
} catch {
case e: Exception =>
Log.e(TAG, "Error when protect socket", e)
}
}})
} catch {
case e: Exception =>
Log.e(TAG, "Error when protect socket", e)
case e: IOException => Log.e(TAG, "Error when accept socket", e)
}
}
}
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment