From 9857e427767593699f651ba51b889ccdd9a249f0 Mon Sep 17 00:00:00 2001 From: Max Lv Date: Fri, 28 Nov 2014 22:12:02 +0800 Subject: [PATCH] enable NAT mode on 5.0 again --- build.sh | 2 +- src/main/jni/Android.mk | 14 +-- .../com/github/shadowsocks/Shadowsocks.scala | 15 +-- .../shadowsocks/ShadowsocksNatService.scala | 100 +++++++++++++++--- .../ShadowsocksRunnerActivity.scala | 2 +- .../shadowsocks/ShadowsocksVpnService.scala | 2 +- .../shadowsocks/utils/ConfigUtils.scala | 2 +- .../github/shadowsocks/utils/Console.scala | 82 +++++++------- .../com/github/shadowsocks/utils/Utils.scala | 4 +- 9 files changed, 145 insertions(+), 78 deletions(-) diff --git a/build.sh b/build.sh index 14cfaff6..c53756bb 100755 --- a/build.sh +++ b/build.sh @@ -15,7 +15,7 @@ rm -rf assets/armeabi-v7a rm -rf assets/x86 mkdir -p assets/armeabi-v7a mkdir -p assets/x86 -for app in pdnsd redsocks ss-local ss-tunnel tun2socks +for app in iptables pdnsd redsocks ss-local ss-tunnel tun2socks do try mv libs/armeabi-v7a/$app assets/armeabi-v7a/ try mv libs/x86/$app assets/x86/ diff --git a/src/main/jni/Android.mk b/src/main/jni/Android.mk index a8093491..fe5a8d10 100755 --- a/src/main/jni/Android.mk +++ b/src/main/jni/Android.mk @@ -300,13 +300,13 @@ openssl_subdirs := $(addprefix $(LOCAL_PATH)/openssl/,$(addsuffix /Android.mk, \ include $(openssl_subdirs) # Iptables -# LOCAL_PATH := $(ROOT_PATH) -# iptables_subdirs := $(addprefix $(LOCAL_PATH)/iptables/,$(addsuffix /Android.mk, \ -# iptables \ -# extensions \ -# libiptc \ -# )) -# include $(iptables_subdirs) +LOCAL_PATH := $(ROOT_PATH) +iptables_subdirs := $(addprefix $(LOCAL_PATH)/iptables/,$(addsuffix /Android.mk, \ + iptables \ + extensions \ + libiptc \ + )) +include $(iptables_subdirs) # Import cpufeatures $(call import-module,android/cpufeatures) diff --git a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala index aa6d93b6..5a3a48da 100644 --- a/src/main/scala/com/github/shadowsocks/Shadowsocks.scala +++ b/src/main/scala/com/github/shadowsocks/Shadowsocks.scala @@ -123,7 +123,7 @@ object Shadowsocks { val FEATRUE_PREFS = Array(Key.route, Key.isGlobalProxy, Key.proxyedApps, Key.isUdpDns, Key.isAutoConnect) - val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL, Executable.TUN2SOCKS) + val EXECUTABLES = Array(Executable.IPTABLES, Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL, Executable.TUN2SOCKS) // Helper functions def updateListPreference(pref: Preference, value: String) { @@ -335,21 +335,24 @@ class Shadowsocks Console.runCommand(ab.toArray) - if (!Utils.isLollipopOrAbove) { + { ab.clear() ab.append("kill -9 `cat /data/data/com.github.shadowsocks/redsocks.pid`") ab.append("rm /data/data/com.github.shadowsocks/redsocks.conf") ab.append("rm /data/data/com.github.shadowsocks/redsocks.pid") + ab.append("kill -9 `cat /data/data/com.github.shadowsocks/ss-tunnel.pid`") + ab.append("rm /data/data/com.github.shadowsocks/ss-tunnel.conf") + ab.append("rm /data/data/com.github.shadowsocks/ss-tunnel.pid") + ab.append("kill -9 `cat /data/data/com.github.shadowsocks/pdnsd.pid`") ab.append("rm /data/data/com.github.shadowsocks/pdnsd.pid") ab.append("rm /data/data/com.github.shadowsocks/pdnsd.conf") ab.append("rm /data/data/com.github.shadowsocks/pdnsd.cache") - ab.append(Utils.getIptables + " -t nat -F OUTPUT") - Console.runRootCommand(ab.toArray) + Console.runRootCommand(Utils.getIptables + " -t nat -F OUTPUT") } } @@ -506,7 +509,7 @@ class Shadowsocks // Bind to the service spawn { - val isRoot = !Utils.isLollipopOrAbove && Console.isRoot + val isRoot = Console.isRoot handler.post(new Runnable { override def run() { status.edit.putBoolean(Key.isRoot, isRoot).commit() @@ -776,7 +779,7 @@ class Shadowsocks if (pref != null) { if (Seq(Key.isGlobalProxy, Key.proxyedApps) .contains(name)) { - pref.setEnabled(enabled && (Utils.isLollipopOrAbove || !isVpnEnabled)) + pref.setEnabled(enabled && (!isVpnEnabled)) } else { pref.setEnabled(enabled) } diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala index b396ea27..1eb73c62 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala @@ -46,8 +46,10 @@ import java.util.{Timer, TimerTask} import android.app.{Notification, NotificationManager, PendingIntent, Service} import android.content._ import android.content.pm.{PackageInfo, PackageManager} +import android.net.ConnectivityManager import android.os._ import android.support.v4.app.NotificationCompat +import android.support.v4.net.ConnectivityManagerCompat import android.util.Log import com.github.shadowsocks.aidl.Config import com.github.shadowsocks.utils._ @@ -71,7 +73,8 @@ class ShadowsocksNatService extends Service with BaseService { private val mStopForegroundSignature = Array[Class[_]](classOf[Boolean]) private val mSetForegroundSignature = Array[Class[_]](classOf[Boolean]) - var receiver: BroadcastReceiver = null + var closeReceiver: BroadcastReceiver = null + var conReceiver: BroadcastReceiver = null var notificationManager: NotificationManager = null var config: Config = null var apps: Array[ProxiedApp] = null @@ -91,6 +94,34 @@ class ShadowsocksNatService extends Service with BaseService { private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication] + def setDns(dns: String) { + val manager = getSystemService(Context.CONNECTIVITY_SERVICE).asInstanceOf[ConnectivityManager] + val networks = manager.getAllNetworks + val cmdBuf = new ArrayBuffer[String]() + networks.foreach(network => { + val networkInfo = manager.getNetworkInfo(network) + if (networkInfo.isAvailable) { + val netId = network.getClass.getDeclaredField("netId").get(network).asInstanceOf[Int] + cmdBuf.append("ndc resolver setnetdns %d \"\" %s".format(netId, dns)) + } + }) + Console.runRootCommand(cmdBuf.toArray) + } + + def initConnectionReceiver() { + val filter = new IntentFilter(ConnectivityManager.CONNECTIVITY_ACTION) + conReceiver = new BroadcastReceiver { + override def onReceive(context: Context, intent: Intent): Unit = { + setDns("127.0.0.1") + } + } + registerReceiver(conReceiver, filter) + } + + def resetDns() = { + setDns("8.8.8.8 208.67.222.222 114.114.114.114") + } + def startShadowsocksDaemon() { if (config.route != Route.ALL) { val acl: Array[String] = config.route match { @@ -135,13 +166,24 @@ class ShadowsocksNatService extends Service with BaseService { val cmd = new ArrayBuffer[String] cmd += (Path.BASE + "ss-tunnel" , "-u" , "-b" , "127.0.0.1" - , "-l" , "8153" , "-L" , "8.8.8.8:53" , "-c" , Path.BASE + "ss-tunnel.json" , "-f" , Path.BASE + "ss-tunnel.pid") + if (Utils.isLollipopOrAbove) { + cmd += ("-l" , "53") + } else { + cmd += ("-l" , "8153") + } + if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" ")) - Console.runCommand(cmd.mkString(" ")) + + if (Utils.isLollipopOrAbove) { + Console.runRootCommand(cmd.mkString(" ")) + } else { + Console.runCommand(cmd.mkString(" ")) + } + } else { val conf = ConfigUtils .SHADOWSOCKS.format(config.proxy, config.remotePort, 8163, @@ -163,15 +205,26 @@ class ShadowsocksNatService extends Service with BaseService { } def startDnsDaemon() { - val conf = ConfigUtils - .PDNSD_BYPASS.format("127.0.0.1", getString(R.string.exclude), 8163) + val conf = if (Utils.isLollipopOrAbove) { + ConfigUtils + .PDNSD_BYPASS.format("127.0.0.1", 53, getString(R.string.exclude), 8163) + } else { + ConfigUtils + .PDNSD_BYPASS.format("127.0.0.1", 8153, getString(R.string.exclude), 8163) + } ConfigUtils.printToFile(new File(Path.BASE + "pdnsd.conf"))(p => { p.println(conf) }) val cmd = Path.BASE + "pdnsd -c " + Path.BASE + "pdnsd.conf" if (BuildConfig.DEBUG) Log.d(TAG, cmd) - Console.runCommand(cmd) + + if (Utils.isLollipopOrAbove) { + Console.runRootCommand(cmd) + } else { + Console.runCommand(cmd) + } + } def getVersionName: String = { @@ -195,7 +248,7 @@ class ShadowsocksNatService extends Service with BaseService { }) if (BuildConfig.DEBUG) Log.d(TAG, cmd) - Console.runCommand(cmd); + Console.runCommand(cmd) } /** Called when the activity is first created. */ @@ -280,15 +333,19 @@ class ShadowsocksNatService extends Service with BaseService { } def killProcesses() { + val cmd = new ArrayBuffer[String]() for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks")) { try { val pid = scala.io.Source.fromFile(Path.BASE + task + ".pid").mkString.trim.toInt Process.killProcess(pid) + cmd.append("kill -9 %d".format(pid)) + cmd.append("rm -rf %s%s.pid", Path.BASE, task) Log.d(TAG, "kill pid: " + pid) } catch { case e: Throwable => Log.e(TAG, "unable to kill " + task, e) } } + Console.runRootCommand(cmd.toArray) Console.runRootCommand(Utils.getIptables + " -t nat -F OUTPUT") } @@ -308,9 +365,13 @@ class ShadowsocksNatService extends Service with BaseService { } init_sb.append(cmd_bypass.replace("0.0.0.0", "127.0.0.1")) init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + myUid)) + init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "--dport 53")) + + if (!Utils.isLollipopOrAbove) { + init_sb.append(Utils.getIptables + + " -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:" + DNS_PORT) + } - init_sb.append(Utils.getIptables - + " -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:" + DNS_PORT) if (config.isGlobalProxy || config.isBypassApps) { http_sb.append(Utils.getIptables + CMD_IPTABLES_DNAT_ADD_SOCKS) } @@ -382,13 +443,15 @@ class ShadowsocksNatService extends Service with BaseService { val filter = new IntentFilter() filter.addAction(Intent.ACTION_SHUTDOWN) filter.addAction(Action.CLOSE) - receiver = new BroadcastReceiver() { + closeReceiver = new BroadcastReceiver() { def onReceive(p1: Context, p2: Intent) { stopRunner() } } - registerReceiver(receiver, filter) + registerReceiver(closeReceiver, filter) + // register connection receiver + if (Utils.isLollipopOrAbove) initConnectionReceiver() // send event application.tracker.send(new HitBuilders.EventBuilder() @@ -468,10 +531,17 @@ class ShadowsocksNatService extends Service with BaseService { stopSelf() } - // clean up context - if (receiver != null) { - unregisterReceiver(receiver) - receiver = null + // clean up recevier + if (closeReceiver != null) { + unregisterReceiver(closeReceiver) + closeReceiver = null + } + if (Utils.isLollipopOrAbove) { + if (conReceiver != null) { + unregisterReceiver(conReceiver) + conReceiver = null + } + resetDns() } stopForegroundCompat(1) diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala index 7db770f5..aed2a286 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala @@ -72,7 +72,7 @@ class ShadowsocksRunnerActivity extends Activity { def isVpnEnabled: Boolean = { if (vpnEnabled < 0) { - vpnEnabled = if (Utils.isLollipopOrAbove || !Console.isRoot) { + vpnEnabled = if (!Console.isRoot) { 1 } else { 0 diff --git a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala index 8cebc2a9..385b458d 100644 --- a/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala +++ b/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala @@ -151,7 +151,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { def startDnsDaemon() { val conf = { if (Utils.isLollipopOrAbove) { - ConfigUtils.PDNSD_BYPASS.format("0.0.0.0", getString(R.string.exclude), 8163) + ConfigUtils.PDNSD_BYPASS.format("0.0.0.0", 8153, getString(R.string.exclude), 8163) } else { ConfigUtils.PDNSD_LOCAL.format("0.0.0.0", 8163) } diff --git a/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala b/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala index 80e2c34a..329a8705 100644 --- a/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala +++ b/src/main/scala/com/github/shadowsocks/utils/ConfigUtils.scala @@ -130,7 +130,7 @@ object ConfigUtils { | perm_cache = 2048; | cache_dir = "/data/data/com.github.shadowsocks"; | server_ip = %s; - | server_port = 8153; + | server_port = %d; | query_method = tcp_only; | run_ipv4 = on; | min_ttl = 15m; diff --git a/src/main/scala/com/github/shadowsocks/utils/Console.scala b/src/main/scala/com/github/shadowsocks/utils/Console.scala index 947173ab..02eeeb4a 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Console.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Console.scala @@ -45,36 +45,21 @@ import java.util object Console { - private var shell: Shell.Interactive = null - private var rootShell: Shell.Interactive = null - - private def openShell() { - if (shell == null) { - val builder = new Builder() - shell = builder - .useSH() - .setWatchdogTimeout(10) - .open(new OnCommandResultListener { - override def onCommandResult(commandCode: Int, exitCode: Int, - output: util.List[String]) { - if (exitCode < 0) { - shell.close() - shell = null - } - } - }) - } + private def openShell(): Shell.Interactive = { + val builder = new Builder() + builder + .useSH() + .setWatchdogTimeout(10) + .open() } - private def openRootShell() { - if (rootShell == null) { - val builder = new Builder() - rootShell = builder - .setShell(SU.shell(0, "u:r:untrusted_app:s0")) - .setWantSTDERR(true) - .setWatchdogTimeout(10) - .open() - } + private def openRootShell(context: String): Shell.Interactive = { + val builder = new Builder() + builder + .setShell(SU.shell(0, context)) + .setWantSTDERR(true) + .setWatchdogTimeout(10) + .open() } def runCommand(command: String) { @@ -82,39 +67,48 @@ object Console { } def runCommand(commands: Array[String]) { - if (shell == null) { - openShell() - } - val ts = shell - ts.addCommand(commands) - ts.waitForIdle() + val shell = openShell() + shell.addCommand(commands, 0, new OnCommandResultListener { + override def onCommandResult(commandCode: Int, exitCode: Int, + output: util.List[String]) { + if (exitCode < 0) { + shell.close() + } + } + }) + shell.waitForIdle() + shell.close() } def runRootCommand(command: String): String = runRootCommand(Array(command)) + def runRootCommand(command: String, context: String): String = runRootCommand(Array(command), context) + def runRootCommand(commands: Array[String]): String = runRootCommand(commands, "u:r:init_shell:s0") - def runRootCommand(commands: Array[String]): String = { + def runRootCommand(commands: Array[String], context: String): String = { if (!isRoot) { return null } - if (rootShell == null) { - openRootShell() - } - val ts = rootShell + val shell = openRootShell(context) val sb = new StringBuilder - ts.addCommand(commands, 0, new OnCommandResultListener { + shell.addCommand(commands, 0, new OnCommandResultListener { override def onCommandResult(commandCode: Int, exitCode: Int, output: util.List[String]) { if (exitCode < 0) { - rootShell.close() - rootShell = null + shell.close() } else { import scala.collection.JavaConversions._ output.foreach(line => sb.append(line).append('\n')) } } }) - if (ts.waitForIdle()) sb.toString() - else null + if (shell.waitForIdle()) { + shell.close() + sb.toString() + } + else { + shell.close() + null + } } def isRoot: Boolean = SU.available() diff --git a/src/main/scala/com/github/shadowsocks/utils/Utils.scala b/src/main/scala/com/github/shadowsocks/utils/Utils.scala index fd62d047..7c2b3474 100644 --- a/src/main/scala/com/github/shadowsocks/utils/Utils.scala +++ b/src/main/scala/com/github/shadowsocks/utils/Utils.scala @@ -68,8 +68,8 @@ object Utils { val DEFAULT_SHELL: String = "/system/bin/sh" val DEFAULT_ROOT: String = "/system/bin/su" val ALTERNATIVE_ROOT: String = "/system/xbin/su" - val DEFAULT_IPTABLES: String = "/system/bin/iptables" - val ALTERNATIVE_IPTABLES: String = "/data/data/com.github.shadowsocks/iptables" + val DEFAULT_IPTABLES: String = "/data/data/com.github.shadowsocks/iptables" + val ALTERNATIVE_IPTABLES: String = "/system/bin/iptables" val TIME_OUT: Int = -99 var initialized: Boolean = false var hasRedirectSupport: Int = -1