Commit 76f7306a authored by Mygod's avatar Mygod Committed by Max Lv

Use Shell.Interactive interactively and removes iptables submodule

The following action will take only one SU request with this commit:

* Start service in NAT mode;
* Reset in NAT mode;
* Flush DNS.

And stopping service in NAT mode doesn't need SU request any more.
parent 09220ee3
...@@ -5,10 +5,6 @@ ...@@ -5,10 +5,6 @@
path = src/main/jni/badvpn path = src/main/jni/badvpn
url = https://github.com/shadowsocks/badvpn.git url = https://github.com/shadowsocks/badvpn.git
branch = shadowsocks-android branch = shadowsocks-android
[submodule "src/main/jni/iptables"]
path = src/main/jni/iptables
url = https://github.com/shadowsocks/iptables.git
branch = shadowsocks-android
[submodule "src/main/jni/libancillary"] [submodule "src/main/jni/libancillary"]
path = src/main/jni/libancillary path = src/main/jni/libancillary
url = https://github.com/shadowsocks/libancillary.git url = https://github.com/shadowsocks/libancillary.git
......
...@@ -33,7 +33,7 @@ useSupportVectors ...@@ -33,7 +33,7 @@ useSupportVectors
libraryDependencies ++= Seq( libraryDependencies ++= Seq(
"dnsjava" % "dnsjava" % "2.1.7", "dnsjava" % "dnsjava" % "2.1.7",
"com.github.kevinsawicki" % "http-request" % "6.0", "com.github.kevinsawicki" % "http-request" % "6.0",
"eu.chainfire" % "libsuperuser" % "1.0.0.201602271131", "eu.chainfire" % "libsuperuser" % "1.0.0.201607041850",
"net.glxn.qrgen" % "android" % "2.0", "net.glxn.qrgen" % "android" % "2.0",
"com.google.android.gms" % "play-services-ads" % "9.2.0", "com.google.android.gms" % "play-services-ads" % "9.2.0",
"com.google.android.gms" % "play-services-analytics" % "9.2.0", "com.google.android.gms" % "play-services-analytics" % "9.2.0",
......
...@@ -552,14 +552,5 @@ openssl_subdirs := $(addprefix $(LOCAL_PATH)/openssl/,$(addsuffix /Android.mk, \ ...@@ -552,14 +552,5 @@ openssl_subdirs := $(addprefix $(LOCAL_PATH)/openssl/,$(addsuffix /Android.mk, \
)) ))
include $(openssl_subdirs) include $(openssl_subdirs)
# Iptables
# LOCAL_PATH := $(ROOT_PATH)
# iptables_subdirs := $(addprefix $(LOCAL_PATH)/iptables/,$(addsuffix /Android.mk, \
# iptables \
# extensions \
# libiptc \
# ))
# include $(iptables_subdirs)
# Import cpufeatures # Import cpufeatures
$(call import-module,android/cpufeatures) $(call import-module,android/cpufeatures)
Subproject commit 67560ab12c1ff3413f971f9f1ba59ff28cb052ce
...@@ -142,9 +142,7 @@ trait BaseService extends Service { ...@@ -142,9 +142,7 @@ trait BaseService extends Service {
profile.password = proxy(2).trim profile.password = proxy(2).trim
profile.method = proxy(3).trim profile.method = proxy(3).trim
} catch { } catch {
case ex: Exception => case ex: Exception => stopRunner(true, getString(R.string.service_failed))
changeState(State.STOPPED, getString(R.string.service_failed))
stopRunner(true)
} }
def startRunner(profile: Profile) { def startRunner(profile: Profile) {
...@@ -171,7 +169,7 @@ trait BaseService extends Service { ...@@ -171,7 +169,7 @@ trait BaseService extends Service {
Utils.ThrowableFuture(connect) Utils.ThrowableFuture(connect)
} }
def stopRunner(stopService: Boolean) { def stopRunner(stopService: Boolean, msg: String = null) {
// clean up recevier // clean up recevier
if (closeReceiverRegistered) { if (closeReceiverRegistered) {
unregisterReceiver(closeReceiver) unregisterReceiver(closeReceiver)
...@@ -188,7 +186,7 @@ trait BaseService extends Service { ...@@ -188,7 +186,7 @@ trait BaseService extends Service {
} }
// change the state // change the state
changeState(State.STOPPED) changeState(State.STOPPED, msg)
// stop the service if nothing has bound to it // stop the service if nothing has bound to it
if (stopService) stopSelf() if (stopService) stopSelf()
...@@ -244,7 +242,7 @@ trait BaseService extends Service { ...@@ -244,7 +242,7 @@ trait BaseService extends Service {
protected def changeState(s: Int, msg: String = null) { protected def changeState(s: Int, msg: String = null) {
val handler = new Handler(getMainLooper) val handler = new Handler(getMainLooper)
handler.post(() => if (state != s) { handler.post(() => if (state != s || msg != null) {
if (callbacksCount > 0) { if (callbacksCount > 0) {
val n = callbacks.beginBroadcast() val n = callbacks.beginBroadcast()
for (i <- 0 until n) { for (i <- 0 until n) {
......
...@@ -65,6 +65,7 @@ import com.github.shadowsocks.utils.CloseUtils._ ...@@ -65,6 +65,7 @@ import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.google.android.gms.ads.{AdRequest, AdSize, AdView} import com.google.android.gms.ads.{AdRequest, AdSize, AdView}
import eu.chainfire.libsuperuser.Shell
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
...@@ -110,7 +111,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { ...@@ -110,7 +111,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
// Services // Services
private val callback = new IShadowsocksServiceCallback.Stub { private val callback = new IShadowsocksServiceCallback.Stub {
def stateChanged(s: Int, m: String) { def stateChanged(s: Int, m: String) {
handler.post(() => if (state != s) { handler.post(() => {
s match { s match {
case State.CONNECTING => case State.CONNECTING =>
fab.setBackgroundTintList(greyTint) fab.setBackgroundTintList(greyTint)
...@@ -286,34 +287,17 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { ...@@ -286,34 +287,17 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
val cmd = new ArrayBuffer[String]() val cmd = new ArrayBuffer[String]()
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks")) { for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks")) {
cmd.append("chmod 666 %s/%s-nat.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task)) cmd.append("killall %s".formatLocal(Locale.ENGLISH, task))
cmd.append("chmod 666 %s/%s-vpn.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task)) cmd.append("rm -f %1$s/%2$s-nat.conf %1$s/%2$s-vpn.conf"
} .formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
if (app.isNatEnabled) Console.runRootCommand(cmd.toArray) else Console.runCommand(cmd.toArray)
cmd.clear()
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "redsocks", "tun2socks")) {
try {
val pid_nat = scala.io.Source.fromFile(getApplicationInfo.dataDir + "/" + task + "-nat.pid").mkString.trim.toInt
val pid_vpn = scala.io.Source.fromFile(getApplicationInfo.dataDir + "/" + task + "-vpn.pid").mkString.trim.toInt
cmd.append("kill -9 %d".formatLocal(Locale.ENGLISH, pid_nat))
cmd.append("kill -9 %d".formatLocal(Locale.ENGLISH, pid_vpn))
Process.killProcess(pid_nat)
Process.killProcess(pid_vpn)
} catch {
case e: Throwable => // Ignore
}
cmd.append("rm -f %s/%s-nat.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
cmd.append("rm -f %s/%s-nat.conf".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
cmd.append("rm -f %s/%s-vpn.pid".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
cmd.append("rm -f %s/%s-vpn.conf".formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
} }
if (app.isNatEnabled) { if (app.isNatEnabled) {
Console.runRootCommand(cmd.toArray) cmd.append("iptables -t nat -F OUTPUT")
Console.runRootCommand(Utils.iptables + " -t nat -F OUTPUT") cmd.append("echo done")
} else Console.runCommand(cmd.toArray) val result = Shell.SU.run(cmd.toArray)
if (result != null && !result.isEmpty) return // fallback to SH
}
Shell.SH.run(cmd.toArray)
} }
def cancelStart() { def cancelStart() {
...@@ -535,7 +519,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext { ...@@ -535,7 +519,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
for (executable <- EXECUTABLES) { for (executable <- EXECUTABLES) {
ab.append("chmod 755 " + getApplicationInfo.dataDir + "/" + executable) ab.append("chmod 755 " + getApplicationInfo.dataDir + "/" + executable)
} }
Console.runCommand(ab.toArray) Shell.SH.run(ab.toArray)
} }
def recovery() { def recovery() {
......
...@@ -50,6 +50,7 @@ import android.util.Log ...@@ -50,6 +50,7 @@ import android.util.Log
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import eu.chainfire.libsuperuser.Shell
import scala.collection.JavaConversions._ import scala.collection.JavaConversions._
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
...@@ -58,8 +59,7 @@ class ShadowsocksNatService extends BaseService { ...@@ -58,8 +59,7 @@ class ShadowsocksNatService extends BaseService {
val TAG = "ShadowsocksNatService" val TAG = "ShadowsocksNatService"
val CMD_IPTABLES_RETURN = " -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN" val CMD_IPTABLES_DNAT_ADD_SOCKS = "iptables -t nat -A OUTPUT -p tcp " +
val CMD_IPTABLES_DNAT_ADD_SOCKS = " -t nat -A OUTPUT -p tcp " +
"-j DNAT --to-destination 127.0.0.1:8123" "-j DNAT --to-destination 127.0.0.1:8123"
private var notification: ShadowsocksNotification = _ private var notification: ShadowsocksNotification = _
...@@ -69,6 +69,7 @@ class ShadowsocksNatService extends BaseService { ...@@ -69,6 +69,7 @@ class ShadowsocksNatService extends BaseService {
var sstunnelProcess: Process = _ var sstunnelProcess: Process = _
var redsocksProcess: Process = _ var redsocksProcess: Process = _
var pdnsdProcess: Process = _ var pdnsdProcess: Process = _
var su: Shell.Interactive = _
def startShadowsocksDaemon() { def startShadowsocksDaemon() {
if (profile.route != Route.ALL) { if (profile.route != Route.ALL) {
...@@ -191,7 +192,7 @@ class ShadowsocksNatService extends BaseService { ...@@ -191,7 +192,7 @@ class ShadowsocksNatService extends BaseService {
} }
/** Called when the activity is first created. */ /** Called when the activity is first created. */
def handleConnection: Boolean = { def handleConnection {
startTunnel() startTunnel()
if (!profile.udpdns) startDnsDaemon() if (!profile.udpdns) startDnsDaemon()
...@@ -199,7 +200,6 @@ class ShadowsocksNatService extends BaseService { ...@@ -199,7 +200,6 @@ class ShadowsocksNatService extends BaseService {
startShadowsocksDaemon() startShadowsocksDaemon()
setupIptables() setupIptables()
true
} }
def onBind(intent: Intent): IBinder = { def onBind(intent: Intent): IBinder = {
...@@ -229,7 +229,7 @@ class ShadowsocksNatService extends BaseService { ...@@ -229,7 +229,7 @@ class ShadowsocksNatService extends BaseService {
pdnsdProcess = null pdnsdProcess = null
} }
Console.runRootCommand(Utils.iptables + " -t nat -F OUTPUT") su.addCommand("iptables -t nat -F OUTPUT")
} }
def setupIptables() = { def setupIptables() = {
...@@ -237,9 +237,9 @@ class ShadowsocksNatService extends BaseService { ...@@ -237,9 +237,9 @@ class ShadowsocksNatService extends BaseService {
val http_sb = new ArrayBuffer[String] val http_sb = new ArrayBuffer[String]
init_sb.append("ulimit -n 4096") init_sb.append("ulimit -n 4096")
init_sb.append(Utils.iptables + " -t nat -F OUTPUT") init_sb.append("iptables -t nat -F OUTPUT")
val cmd_bypass = Utils.iptables + CMD_IPTABLES_RETURN val cmd_bypass = "iptables -t nat -A OUTPUT -p tcp -d 0.0.0.0 -j RETURN"
if (!InetAddress.getByName(profile.host.toUpperCase).isInstanceOf[Inet6Address]) { if (!InetAddress.getByName(profile.host.toUpperCase).isInstanceOf[Inet6Address]) {
init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d " + profile.host)) init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-d " + profile.host))
} }
...@@ -247,18 +247,17 @@ class ShadowsocksNatService extends BaseService { ...@@ -247,18 +247,17 @@ class ShadowsocksNatService extends BaseService {
init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-m owner --uid-owner " + myUid)) init_sb.append(cmd_bypass.replace("-p tcp -d 0.0.0.0", "-m owner --uid-owner " + myUid))
init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "--dport 53")) init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "--dport 53"))
init_sb.append(Utils.iptables init_sb.append("iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:8153")
+ " -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:8153")
if (!profile.proxyApps || profile.bypass) { if (!profile.proxyApps || profile.bypass) {
http_sb.append(Utils.iptables + CMD_IPTABLES_DNAT_ADD_SOCKS) http_sb.append(CMD_IPTABLES_DNAT_ADD_SOCKS)
} }
if (profile.proxyApps) { if (profile.proxyApps) {
val uidMap = getPackageManager.getInstalledApplications(0).map(ai => ai.packageName -> ai.uid).toMap val uidMap = getPackageManager.getInstalledApplications(0).map(ai => ai.packageName -> ai.uid).toMap
for (pn <- profile.individual.split('\n')) uidMap.get(pn) match { for (pn <- profile.individual.split('\n')) uidMap.get(pn) match {
case Some(uid) => case Some(uid) =>
if (!profile.bypass) { if (!profile.bypass) {
http_sb.append((Utils.iptables + CMD_IPTABLES_DNAT_ADD_SOCKS) http_sb.append(CMD_IPTABLES_DNAT_ADD_SOCKS
.replace("-t nat", "-t nat -m owner --uid-owner " + uid)) .replace("-t nat", "-t nat -m owner --uid-owner " + uid))
} else { } else {
init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + uid)) init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + uid))
...@@ -266,31 +265,38 @@ class ShadowsocksNatService extends BaseService { ...@@ -266,31 +265,38 @@ class ShadowsocksNatService extends BaseService {
case _ => // probably removed package, ignore case _ => // probably removed package, ignore
} }
} }
Console.runRootCommand((init_sb ++ http_sb).toArray) su.addCommand((init_sb ++ http_sb).toArray)
} }
override def startRunner(profile: Profile) { override def startRunner(profile: Profile) = if (su == null) {
if (!Console.isRoot) { su = new Shell.Builder().useSU().setWantSTDERR(true).setWatchdogTimeout(10).open((_, exitCode, _) =>
changeState(State.STOPPED, getString(R.string.nat_no_root)) if (exitCode == 0) super.startRunner(profile) else {
stopRunner(true) Log.wtf(TAG, "libsuperuser#55 has been fixed. Please remove the redundant code.")
return su.close()
su = null
super.stopRunner(true, getString(R.string.nat_no_root))
})
su.waitForIdle()
if (!su.isRunning) {
su.close()
su = null
super.stopRunner(true, getString(R.string.nat_no_root))
} }
super.startRunner(profile)
} }
override def connect() { override def connect() {
super.connect() super.connect()
if (this.profile != null) { if (profile != null) {
// Clean up // Clean up
killProcesses() killProcesses()
var resolved: Boolean = false var resolved = false
if (!Utils.isNumeric(this.profile.host)) { if (!Utils.isNumeric(profile.host)) {
Utils.resolve(this.profile.host, enableIPv6 = true) match { Utils.resolve(profile.host, enableIPv6 = true) match {
case Some(a) => case Some(a) =>
this.profile.host = a profile.host = a
resolved = true resolved = true
case None => resolved = false case None => resolved = false
} }
...@@ -298,33 +304,31 @@ class ShadowsocksNatService extends BaseService { ...@@ -298,33 +304,31 @@ class ShadowsocksNatService extends BaseService {
resolved = true resolved = true
} }
if (!resolved) { if (!resolved) stopRunner(true, getString(R.string.invalid_server)) else {
changeState(State.STOPPED, getString(R.string.invalid_server)) handleConnection
stopRunner(true)
} else if (handleConnection) {
// Set DNS // Set DNS
Utils.flushDns() su.addCommand(Utils.FLUSH_DNS)
changeState(State.CONNECTED) changeState(State.CONNECTED)
notification = new ShadowsocksNotification(this, profile.name, true) notification = new ShadowsocksNotification(this, profile.name, true)
} else {
changeState(State.STOPPED, getString(R.string.service_failed))
stopRunner(true)
} }
} }
} }
override def stopRunner(stopService: Boolean) { override def stopRunner(stopService: Boolean, msg: String = null) {
if (notification != null) notification.destroy()
// channge the state // channge the state
changeState(State.STOPPING) changeState(State.STOPPING)
if (notification != null) notification.destroy()
app.track(TAG, "stop") app.track(TAG, "stop")
// reset NAT // reset NAT
killProcesses() killProcesses()
super.stopRunner(stopService) su.close()
su = null
super.stopRunner(stopService, msg)
} }
} }
...@@ -82,7 +82,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { ...@@ -82,7 +82,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
stopRunner(true) stopRunner(true)
} }
override def stopRunner(stopService: Boolean) { override def stopRunner(stopService: Boolean, msg: String = null) {
if (vpnThread != null) { if (vpnThread != null) {
vpnThread.stopThread() vpnThread.stopThread()
...@@ -105,7 +105,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { ...@@ -105,7 +105,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
conn = null conn = null
} }
super.stopRunner(stopService) super.stopRunner(stopService, msg)
} }
def killProcesses() { def killProcesses() {
...@@ -165,10 +165,7 @@ class ShadowsocksVpnService extends VpnService with BaseService { ...@@ -165,10 +165,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
resolved = true resolved = true
} }
if (!resolved) { if (!resolved) stopRunner(true, getString(R.string.invalid_server)) else if (handleConnection) {
changeState(State.STOPPED, getString(R.string.invalid_server))
stopRunner(true)
} else if (handleConnection) {
changeState(State.CONNECTED) changeState(State.CONNECTED)
notification = new ShadowsocksNotification(this, profile.name) notification = new ShadowsocksNotification(this, profile.name)
} else { } else {
......
/*
* Shadowsocks - A shadowsocks client for Android
* Copyright (C) 2014 <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.utils
import java.util
import eu.chainfire.libsuperuser.Shell
import eu.chainfire.libsuperuser.Shell.{Builder, SU}
object Console {
private def openShell(): Shell.Interactive = {
val builder = new Builder()
builder
.useSH()
.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(commands: Array[String]) {
val shell = openShell()
shell.addCommand(commands, 0, ((commandCode: Int, exitCode: Int, output: util.List[String]) =>
if (exitCode < 0) shell.close()): Shell.OnCommandResultListener)
shell.waitForIdle()
shell.close()
}
def runRootCommand(commands: String*): String = runRootCommand(commands.toArray)
def runRootCommand(commands: Array[String]): String = {
val shell = openRootShell("u:r:init_shell:s0")
val sb = new StringBuilder
shell.addCommand(commands, 0, ((_, exitCode, output) => {
if (exitCode < 0) {
shell.close()
} else {
import scala.collection.JavaConversions._
output.foreach(line => sb.append(line).append('\n'))
}
}): Shell.OnCommandResultListener)
if (shell.waitForIdle()) {
shell.close()
sb.toString()
}
else {
shell.close()
null
}
}
def isRoot: Boolean = SU.available()
}
...@@ -53,6 +53,7 @@ import android.view.{Gravity, View, Window} ...@@ -53,6 +53,7 @@ import android.view.{Gravity, View, Window}
import android.widget.Toast import android.widget.Toast
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.{BuildConfig, ShadowsocksRunnerService} import com.github.shadowsocks.{BuildConfig, ShadowsocksRunnerService}
import eu.chainfire.libsuperuser.Shell
import org.xbill.DNS._ import org.xbill.DNS._
import scala.concurrent.ExecutionContext.Implicits.global import scala.concurrent.ExecutionContext.Implicits.global
...@@ -62,18 +63,6 @@ import scala.util.{Failure, Try} ...@@ -62,18 +63,6 @@ import scala.util.{Failure, Try}
object Utils { object Utils {
private val TAG = "Shadowsocks" private val TAG = "Shadowsocks"
/**
* Check the system's iptables
* Default to use the app's iptables
*/
private val DEFAULT_IPTABLES = "/system/bin/iptables"
private val ALTERNATIVE_IPTABLES = "iptables"
lazy val iptables = if (Console.isRoot) {
val lines = Console.runRootCommand(DEFAULT_IPTABLES + " --version", DEFAULT_IPTABLES + " -L -t nat -n")
if (Console.isRoot && lines != null && !lines.contains("OUTPUT") || !lines.contains("v1.4.")) ALTERNATIVE_IPTABLES
else DEFAULT_IPTABLES
} else DEFAULT_IPTABLES
def isLollipopOrAbove: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP def isLollipopOrAbove: Boolean = Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP
def getSignature(context: Context): String = { def getSignature(context: Context): String = {
...@@ -130,18 +119,16 @@ object Utils { ...@@ -130,18 +119,16 @@ object Utils {
} }
// Because /sys/class/net/* isn't accessible since API level 24 // Because /sys/class/net/* isn't accessible since API level 24
def flushDns() = Console.runRootCommand("for if in /sys/class/net/*; do " + final val FLUSH_DNS = "for if in /sys/class/net/*; do " +
"if [ \"down\" != $(cat $if/operstate) ]; then " + // up or unknown "if [ \"down\" != $(cat $if/operstate) ]; then " + // up or unknown
"ndc resolver flushif ${if##*/}; " + "ndc resolver flushif ${if##*/}; " +
"fi " + "fi " +
"done") "done; echo done"
// Blocked > 3 seconds // Blocked > 3 seconds
def toggleAirplaneMode(context: Context) = { def toggleAirplaneMode(context: Context) = {
if (Console.isRoot) { val result = Shell.SU.run(FLUSH_DNS)
flushDns() if (result != null && !result.isEmpty) true else if (Build.VERSION.SDK_INT < 17) {
true
} else if (Build.VERSION.SDK_INT < 17) {
toggleBelowApiLevel17(context) toggleBelowApiLevel17(context)
true true
} else false } else false
......
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