diff --git a/src/main/scala/com/github/shadowsocks/ShadowVpnActivity.scala b/src/main/scala/com/github/shadowsocks/ShadowVpnActivity.scala index 8973c947..22ca8714 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowVpnActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowVpnActivity.scala @@ -5,6 +5,7 @@ import android.os.Bundle import android.net.VpnService import android.content.Intent import android.util.Log +import android.preference.PreferenceManager class ShadowVpnActivity extends Activity { @@ -21,8 +22,9 @@ class ShadowVpnActivity extends Activity { override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent) { resultCode match { case Activity.RESULT_OK => { - val it: Intent = new Intent(this, classOf[ShadowVpnService]) - startService(it) + val intent: Intent = new Intent(this, classOf[ShadowVpnService]) + Extra.put(PreferenceManager.getDefaultSharedPreferences(this), intent) + startService(intent) } case _ => { Log.e(Shadowsocks.TAG, "Failed to start VpnService") diff --git a/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala index 83b4f9d5..71f793ce 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowVpnService.scala @@ -78,18 +78,9 @@ class ShadowVpnService extends VpnService { var udpgw: String = null var notificationManager: NotificationManager = null var receiver: BroadcastReceiver = null - var appHost: String = null - var remotePort: Int = 0 - var localPort: Int = 0 - var sitekey: String = null var settings: SharedPreferences = null - var isGlobalProxy: Boolean = false - var isGFWList: Boolean = false - var isBypassApps: Boolean = false - var isDNSProxy: Boolean = false - var isHTTPProxy: Boolean = false - var encMethod: String = null var apps: Array[ProxiedApp] = null + var config: Config = null private var state = State.INIT private var message: String = null @@ -152,7 +143,9 @@ class ShadowVpnService extends VpnService { val cmd: String = (BASE + "shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " + BASE + - "shadowsocks.pid").format(appHost, remotePort, localPort, sitekey, encMethod) + "shadowsocks.pid").format(config.proxy, config.remotePort, config.localPort, + config.sitekey, config.encMethod) + Log.d(TAG, cmd) System.exec(cmd) } }.start() @@ -185,34 +178,7 @@ class ShadowVpnService extends VpnService { changeState(State.CONNECTING) - appHost = settings.getString("proxy", "127.0.0.1") - if (appHost == "198.199.101.152") { - appHost = "s.maxcdn.info" - } - sitekey = settings.getString("sitekey", "default") - encMethod = settings.getString("encMethod", "table") - try { - remotePort = Integer.valueOf(settings.getString("remotePort", "1984")) - } catch { - case ex: NumberFormatException => { - remotePort = 1984 - } - } - try { - localPort = Integer.valueOf(settings.getString("port", "1984")) - } catch { - case ex: NumberFormatException => { - localPort = 1984 - } - } - isGlobalProxy = settings.getBoolean("isGlobalProxy", false) - isGFWList = settings.getBoolean("isGFWList", false) - isBypassApps = settings.getBoolean("isBypassApps", false) - isDNSProxy = settings.getBoolean("isDNSProxy", false) - isHTTPProxy = settings.getBoolean("isHTTPProxy", false) - if (isHTTPProxy) { - localPort -= 1 - } + config = Extra.get(intent) new Thread(new Runnable { def run() { @@ -221,10 +187,10 @@ class ShadowVpnService extends VpnService { // Resolve server address var resolved: Boolean = false - if (!InetAddressUtils.isIPv4Address(appHost) && !InetAddressUtils.isIPv6Address(appHost)) { - Utils.resolve(appHost, enableIPv6 = true) match { + if (!InetAddressUtils.isIPv4Address(config.proxy) && !InetAddressUtils.isIPv6Address(config.proxy)) { + Utils.resolve(config.proxy, enableIPv6 = true) match { case Some(addr) => - appHost = addr + config.proxy = addr resolved = true case None => resolved = false } @@ -273,7 +239,7 @@ class ShadowVpnService extends VpnService { def startVpn() { - val address = appHost.split('.') + val address = config.proxy.split('.') val prefix1 = address(0) val prefix2 = address.slice(0, 2).mkString(".") val prefix3 = address.slice(0, 3).mkString(".") @@ -291,9 +257,9 @@ class ShadowVpnService extends VpnService { .addDnsServer("8.8.8.8") .addDnsServer("8.8.4.4") - if (InetAddressUtils.isIPv6Address(appHost)) { + if (InetAddressUtils.isIPv6Address(config.proxy)) { builder.addRoute("0.0.0.0", 0) - } else if (isGFWList) { + } else if (config.isGFWList) { val gfwList = getResources.getStringArray(R.array.gfw_list) gfwList.foreach(addr => if (addr != prefix2) { @@ -346,8 +312,8 @@ class ShadowVpnService extends VpnService { + "--socks-server-addr 127.0.0.1:%d " + "--tunfd %d " + "--tunmtu %d " - + "--pid %stun2socks.pid").format(localAddress.format(2), udpgw, localPort, fd, VPN_MTU, BASE) - + + "--pid %stun2socks.pid").format(localAddress.format(2), udpgw, config.localPort, fd, VPN_MTU, BASE) + Log.d(TAG, cmd) System.exec(cmd) } diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index 147dc9fc..ebd1f969 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -602,8 +602,9 @@ class Shadowsocks if (isVpnEnabled) { if (ShadowVpnService.isServiceStarted(this)) return false - val it: Intent = new Intent(this, classOf[ShadowVpnService]) - startService(it) + val intent: Intent = new Intent(this, classOf[ShadowVpnService]) + Extra.put(settings, intent) + startService(intent) val style = new Style.Builder() .setBackgroundColorValue(Style.holoBlueLight) .setDuration(Style.DURATION_INFINITE) @@ -612,8 +613,9 @@ class Shadowsocks switchButton.setEnabled(false) } else { if (ShadowsocksService.isServiceStarted(this)) return false - val it: Intent = new Intent(this, classOf[ShadowsocksService]) - startService(it) + val intent: Intent = new Intent(this, classOf[ShadowsocksService]) + Extra.put(settings, intent) + startService(intent) } true } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala index fb76545e..d73f0ed2 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksReceiver.scala @@ -61,25 +61,11 @@ class ShadowsocksReceiver extends BroadcastReceiver { val isAutoConnect: Boolean = settings.getBoolean("isAutoConnect", false) val isInstalled: Boolean = settings.getBoolean(versionName, false) if (isAutoConnect && isInstalled) { - val portText: String = settings.getString("port", "") - if (portText == null || portText.length <= 0) { - return - } - try { - val port: Int = Integer.valueOf(portText) - if (port <= 1024) { - return - } - } catch { - case e: Exception => { - return - } - } - if (Utils.getRoot) { if (ShadowsocksService.isServiceStarted(context)) return - val it: Intent = new Intent(context, classOf[ShadowsocksService]) - context.startService(it) + val intent: Intent = new Intent(context, classOf[ShadowsocksService]) + Extra.put(settings, intent) + context.startService(intent) } } } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala index f740f8e4..cf34cab8 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksService.scala @@ -102,19 +102,11 @@ class ShadowsocksService extends Service { var receiver: BroadcastReceiver = null var notificationManager: NotificationManager = null - var appHost: String = null - var remotePort: Int = 0 - var localPort: Int = 0 - var sitekey: String = null - var settings: SharedPreferences = null - var hasRedirectSupport: Boolean = true - var isGlobalProxy: Boolean = false - var isGFWList: Boolean = false - var isBypassApps: Boolean = false - var isDNSProxy: Boolean = false - var isHTTPProxy: Boolean = false - var encMethod: String = null + var config: Config = null + var hasRedirectSupport = false var apps: Array[ProxiedApp] = null + var settings: SharedPreferences = null + var mSetForeground: Method = null var mStartForeground: Method = null var mStopForeground: Method = null @@ -176,7 +168,9 @@ class ShadowsocksService extends Service { val cmd: String = (BASE + "shadowsocks -s \"%s\" -p \"%d\" -l \"%d\" -k \"%s\" -m \"%s\" -f " + BASE + - "shadowsocks.pid").format(appHost, remotePort, localPort, sitekey, encMethod) + "shadowsocks.pid").format(config.proxy, config.remotePort, config.localPort, + config.sitekey, config.encMethod) + Log.d(TAG, cmd) System.exec(cmd) } }.start() @@ -187,7 +181,8 @@ class ShadowsocksService extends Service { override def run() { val cmd: String = (BASE + "polipo proxyPort=%d socksParentProxy=127.0.0.1:%d daemonise=true pidFile=\"%s\" logLevel=1 logFile=\"%s\"") - .format(localPort + 1, localPort, BASE + "polipo.pid", BASE + "polipo.log") + .format(config.localPort + 1, config.localPort, BASE + "polipo.pid", BASE + "polipo.log") + Log.d(TAG, cmd) System.exec(cmd) } }.start() @@ -219,44 +214,18 @@ class ShadowsocksService extends Service { changeState(State.CONNECTING) - appHost = settings.getString("proxy", "127.0.0.1") - if (appHost == "198.199.101.152") { - appHost = "s.maxcdn.info" - } - sitekey = settings.getString("sitekey", "default") - encMethod = settings.getString("encMethod", "table") - try { - remotePort = Integer.valueOf(settings.getString("remotePort", "1984")) - } catch { - case ex: NumberFormatException => { - remotePort = 1984 - } - } - try { - localPort = Integer.valueOf(settings.getString("port", "1984")) - } catch { - case ex: NumberFormatException => { - localPort = 1984 - } - } - isGlobalProxy = settings.getBoolean("isGlobalProxy", false) - isGFWList = settings.getBoolean("isGFWList", false) - isBypassApps = settings.getBoolean("isBypassApps", false) - isDNSProxy = settings.getBoolean("isDNSProxy", false) - isHTTPProxy = settings.getBoolean("isHTTPProxy", false) - if (isHTTPProxy) { - localPort -= 1 - } + config = Extra.get(intent) + new Thread(new Runnable { def run() { killProcesses() var resolved: Boolean = false - if (!InetAddressUtils.isIPv4Address(appHost) && !InetAddressUtils.isIPv6Address(appHost)) { - Utils.resolve(appHost, enableIPv6 = true) match { - case Some(addr) => - appHost = addr + if (!InetAddressUtils.isIPv4Address(config.proxy) && !InetAddressUtils.isIPv6Address(config.proxy)) { + Utils.resolve(config.proxy, enableIPv6 = true) match { + case Some(a) => + config.proxy = a resolved = true case None => resolved = false } @@ -282,7 +251,7 @@ class ShadowsocksService extends Service { } def startRedsocksDaemon() { - val conf = REDSOCKS_CONF.format(localPort) + val conf = REDSOCKS_CONF.format(config.localPort) val cmd = "%sredsocks -p %sredsocks.pid -c %sredsocks.conf".format(BASE, BASE, BASE) Utils.runRootCommand("echo \"" + conf + "\" > " + BASE + "redsocks.conf\n" + cmd) } @@ -308,7 +277,7 @@ class ShadowsocksService extends Service { /** Called when the activity is first created. */ def handleConnection: Boolean = { - if (isHTTPProxy) { + if (config.isHTTPProxy) { startPolipoDaemon() } startShadowsocksDaemon() @@ -453,7 +422,7 @@ class ShadowsocksService extends Service { sb ++= "kill -9 `cat /data/data/com.github.shadowsocks/shadowsocks.pid`" ++= "\n" sb ++= "killall -9 shadowsocks" ++= "\n" } - if (isHTTPProxy) { + if (config.isHTTPProxy) { if (!waitForProcess("polipo")) { sb ++= "kill -9 `cat /data/data/com.github.shadowsocks/polipo.pid`" ++= "\n" sb ++= "killall -9 polipo" ++= "\n" @@ -476,11 +445,11 @@ class ShadowsocksService extends Service { val http_sb = new StringBuilder init_sb.append(Utils.getIptables).append(" -t nat -F OUTPUT\n") val cmd_bypass = Utils.getIptables + CMD_IPTABLES_RETURN - if (!InetAddressUtils.isIPv6Address(appHost.toUpperCase)) { - init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-d " + appHost)) + if (!InetAddressUtils.isIPv6Address(config.proxy.toUpperCase)) { + init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-d " + config.proxy)) } init_sb.append(cmd_bypass.replace("0.0.0.0", "127.0.0.1")) - if (!isDNSProxy) { + if (!config.isDNSProxy) { init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "--dport " + 53)) init_sb .append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + getApplicationInfo.uid)) @@ -498,20 +467,20 @@ class ShadowsocksService extends Service { .append(DNS_PORT) .append("\n") } - if (isGFWList) { + if (config.isGFWList) { val chn_list: Array[String] = getResources.getStringArray(R.array.chn_list) for (item <- chn_list) { init_sb.append(cmd_bypass.replace("0.0.0.0", item)) } } - if (isGlobalProxy || isBypassApps) { + if (config.isGlobalProxy || config.isBypassApps) { http_sb.append(if (hasRedirectSupport) { Utils.getIptables + CMD_IPTABLES_REDIRECT_ADD_SOCKS } else { Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS }) } - if (!isGlobalProxy) { + if (!config.isGlobalProxy) { if (apps == null || apps.length <= 0) apps = AppManager.getProxiedApps(this) val uidSet: mutable.HashSet[Int] = new mutable.HashSet[Int] for (app <- apps) { @@ -520,7 +489,7 @@ class ShadowsocksService extends Service { } } for (uid <- uidSet) { - if (!isBypassApps) { + if (!config.isBypassApps) { http_sb.append((if (hasRedirectSupport) { Utils.getIptables + CMD_IPTABLES_REDIRECT_ADD_SOCKS } else { @@ -577,6 +546,4 @@ class ShadowsocksService extends Service { mSetForegroundArgs(0) = boolean2Boolean(x = false) invokeMethod(mSetForeground, mSetForegroundArgs) } - - } diff --git a/src/main/scala/com/github/shadowsocks/Utils.scala b/src/main/scala/com/github/shadowsocks/Utils.scala index 44b5683b..0879a26c 100644 --- a/src/main/scala/com/github/shadowsocks/Utils.scala +++ b/src/main/scala/com/github/shadowsocks/Utils.scala @@ -37,7 +37,7 @@ */ package com.github.shadowsocks -import android.content.Context +import android.content.{Intent, SharedPreferences, Context} import android.content.pm.ApplicationInfo import android.content.pm.PackageManager import android.graphics.drawable.{BitmapDrawable, Drawable} @@ -52,6 +52,33 @@ import scala.Some import android.graphics.{Canvas, Bitmap} import android.app.ActivityManager +object Key { + val isGlobalProxy = "isGlobalProxy" + val isGFWList = "isGFWList" + val isBypassApps = "isBypassApps" + val isDNSProxy = "isDNSProxy" + val isHTTPProxy = "isHTTPProxy" + + val proxy = "proxy" + val sitekey = "sitekey" + val encMethod = "encMethod" + val remotePort = "remotePort" + val localPort = "port" +} + +case class Config ( + isGlobalProxy: Boolean, + isGFWList: Boolean, + isBypassApps: Boolean, + isDNSProxy: Boolean, + isHTTPProxy: Boolean, + var proxy: String, + sitekey: String, + encMethod: String, + remotePort: Int, + localPort: Int +) + object State { val INIT = 0 val CONNECTING = 1 @@ -68,6 +95,64 @@ object Action { object Extra { val STATE = "state" val MESSAGE = "message" + + def get(intent: Intent): Config = { + val isGlobalProxy = intent.getBooleanExtra(Key.isGlobalProxy, false) + val isGFWList = intent.getBooleanExtra(Key.isGFWList, false) + val isBypassApps = intent.getBooleanExtra(Key.isBypassApps, false) + val isDNSProxy = intent.getBooleanExtra(Key.isDNSProxy, false) + val isHTTPProxy = intent.getBooleanExtra(Key.isHTTPProxy, false) + val proxy = intent.getStringExtra(Key.proxy) + val sitekey = intent.getStringExtra(Key.sitekey) + val encMethod = intent.getStringExtra(Key.encMethod) + val remotePort = intent.getIntExtra(Key.remotePort, 1984) + val localPort = intent.getIntExtra(Key.localPort, 1984) + + new Config(isGlobalProxy, isGFWList, isBypassApps, isDNSProxy, + isHTTPProxy, proxy, sitekey, encMethod, remotePort, localPort) + } + + def put(settings: SharedPreferences, intent: Intent) { + val isGlobalProxy = settings.getBoolean(Key.isGlobalProxy, false) + val isGFWList = settings.getBoolean(Key.isGFWList, false) + val isBypassApps = settings.getBoolean(Key.isBypassApps, false) + val isDNSProxy = settings.getBoolean(Key.isDNSProxy, false) + val isHTTPProxy = settings.getBoolean(Key.isHTTPProxy, false) + val proxy = settings.getString(Key.proxy, "127.0.0.1") match { + case "198.199.101.152" => "s.maxcdn.info" + case s: String => s + case _ => "127.0.0.1" + } + val sitekey = settings.getString(Key.sitekey, "default") + val encMethod = settings.getString(Key.encMethod, "table") + val remotePort: Int = try { + Integer.valueOf(settings.getString(Key.remotePort, "1984")) + } catch { + case ex: NumberFormatException => { + 1984 + } + } + val localProt: Int = try { + val p = Integer.valueOf(settings.getString(Key.localPort, "1984")) + if (isHTTPProxy) p - 1 else p + } catch { + case ex: NumberFormatException => { + 1984 + } + } + + intent.putExtra(Key.isGlobalProxy, isGlobalProxy) + intent.putExtra(Key.isGFWList, isGFWList) + intent.putExtra(Key.isBypassApps, isBypassApps) + intent.putExtra(Key.isDNSProxy, isDNSProxy) + intent.putExtra(Key.isHTTPProxy, isHTTPProxy) + + intent.putExtra(Key.proxy, proxy) + intent.putExtra(Key.sitekey, sitekey) + intent.putExtra(Key.encMethod, encMethod) + intent.putExtra(Key.remotePort, remotePort) + intent.putExtra(Key.localPort, localProt) + } } object Utils {