Replace NAT mode with local SOCKS5 mode. #1441
This commit is contained in:
@@ -9,14 +9,6 @@
|
||||
path = mobile/src/main/jni/libancillary
|
||||
url = https://github.com/shadowsocks/libancillary.git
|
||||
branch = shadowsocks-android
|
||||
[submodule "mobile/src/main/jni/libevent"]
|
||||
path = mobile/src/main/jni/libevent
|
||||
url = https://github.com/shadowsocks/libevent.git
|
||||
branch = shadowsocks-android
|
||||
[submodule "mobile/src/main/jni/redsocks"]
|
||||
path = mobile/src/main/jni/redsocks
|
||||
url = https://github.com/shadowsocks/redsocks.git
|
||||
branch = shadowsocks-android
|
||||
[submodule "mobile/src/main/jni/mbedtls"]
|
||||
path = mobile/src/main/jni/mbedtls
|
||||
url = https://github.com/ARMmbed/mbedtls
|
||||
|
||||
@@ -12,7 +12,7 @@
|
||||
android:required="false"/>
|
||||
<uses-feature android:name="android.software.leanback"
|
||||
android:required="false"/>
|
||||
<uses-feature android:name="android.hardware.camera"
|
||||
<uses-feature android:name="android.hardware.camera"
|
||||
android:required="false"/>
|
||||
|
||||
<uses-sdk
|
||||
@@ -109,7 +109,7 @@
|
||||
</activity>
|
||||
|
||||
<service
|
||||
android:name=".ShadowsocksNatService"
|
||||
android:name=".ShadowsocksLocalService"
|
||||
android:process=":bg"
|
||||
android:exported="false">
|
||||
</service>
|
||||
|
||||
@@ -78,32 +78,6 @@ LOCAL_SRC_FILES := $(addprefix libsodium/src/libsodium/,$(SODIUM_SOURCE))
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
########################################################
|
||||
## libevent
|
||||
########################################################
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
LIBEVENT_SOURCES := \
|
||||
buffer.c \
|
||||
bufferevent.c bufferevent_filter.c \
|
||||
bufferevent_pair.c bufferevent_ratelim.c \
|
||||
bufferevent_sock.c epoll.c \
|
||||
epoll_sub.c evdns.c event.c \
|
||||
event_tagging.c evmap.c \
|
||||
evrpc.c evthread.c \
|
||||
evthread_pthread.c evutil.c \
|
||||
evutil_rand.c http.c \
|
||||
listener.c log.c poll.c \
|
||||
select.c signal.c strlcpy.c
|
||||
|
||||
LOCAL_MODULE := event
|
||||
LOCAL_SRC_FILES := $(addprefix libevent/, $(LIBEVENT_SOURCES))
|
||||
LOCAL_CFLAGS := -O2 -D_EVENT_HAVE_ARC4RANDOM -I$(LOCAL_PATH)/libevent \
|
||||
-I$(LOCAL_PATH)/libevent/include \
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
########################################################
|
||||
## libancillary
|
||||
########################################################
|
||||
@@ -200,28 +174,6 @@ LOCAL_SRC_FILES := \
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
########################################################
|
||||
## redsocks
|
||||
########################################################
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
REDSOCKS_SOURCES := base.c http-connect.c \
|
||||
log.c md5.c socks5.c \
|
||||
base64.c http-auth.c http-relay.c main.c \
|
||||
parser.c redsocks.c socks4.c utils.c
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libevent
|
||||
|
||||
LOCAL_MODULE := redsocks
|
||||
LOCAL_SRC_FILES := $(addprefix redsocks/, $(REDSOCKS_SOURCES))
|
||||
LOCAL_CFLAGS := -O2 -std=gnu99 -DUSE_IPTABLES \
|
||||
-I$(LOCAL_PATH)/redsocks \
|
||||
-I$(LOCAL_PATH)/libevent/include \
|
||||
-I$(LOCAL_PATH)/libevent
|
||||
|
||||
include $(BUILD_SHARED_EXECUTABLE)
|
||||
|
||||
########################################################
|
||||
## shadowsocks-libev local
|
||||
########################################################
|
||||
|
||||
Submodule mobile/src/main/jni/libevent deleted from 359ca847a6
Submodule mobile/src/main/jni/redsocks deleted from 274334f148
@@ -7,8 +7,8 @@
|
||||
<!-- misc -->
|
||||
<string name="profile">Profile</string>
|
||||
<string name="profile_summary">Switch to another profile or add new profiles</string>
|
||||
<string name="nat">NAT mode (deprecated)</string>
|
||||
<string name="nat_summary">Use NAT mode instead of VPN mode. Requires ROOT permission.</string>
|
||||
<string name="local">SOCKS5 mode</string>
|
||||
<string name="local_summary">Enable SOCKS5 proxy mode instead of VPN mode to work with AFWall+ or Orbot</string>
|
||||
<string name="remote_dns">Remote DNS</string>
|
||||
<string name="stat_summary">Sent: \t\t\t\t\t%3$s\t↑\t%1$s/s\nReceived: \t%4$s\t↓\t%2$s/s</string>
|
||||
<string name="stat_profiles">%1$s↑\t%2$s↓</string>
|
||||
@@ -43,7 +43,7 @@
|
||||
<string name="route_entry_gfwlist">GFW List</string>
|
||||
<string name="route_entry_chinalist">China List</string>
|
||||
<string name="proxied_apps">Per-App Proxy</string>
|
||||
<string name="proxied_apps_summary">Set proxy for selected apps, requires NAT mode under Android 4.x</string>
|
||||
<string name="proxied_apps_summary">Set proxy for selected apps</string>
|
||||
<string name="proxied_apps_summary_v21">Set proxy for selected apps</string>
|
||||
<string name="on">On</string>
|
||||
<string name="bypass_apps">Bypass Mode</string>
|
||||
@@ -57,12 +57,10 @@
|
||||
|
||||
<!-- notification category -->
|
||||
<string name="service_vpn">VPN Service</string>
|
||||
<string name="service_nat">NAT Service</string>
|
||||
<string name="service_local">SOCKS5 Service</string>
|
||||
<string name="forward_success">Shadowsocks started.</string>
|
||||
<string name="invalid_server">Invalid server name</string>
|
||||
<string name="service_failed">Failed to connect the remote server</string>
|
||||
<string name="nat_deprecated">WARNING: NAT mode has been deprecated since Android 5.0</string>
|
||||
<string name="nat_no_root">NAT mode requires ROOT permission</string>
|
||||
<string name="switch_to_vpn">Switch to VPN mode</string>
|
||||
<string name="stop">Stop</string>
|
||||
<string name="stopping">Shutting down…</string>
|
||||
@@ -124,7 +122,7 @@
|
||||
<string name="received">Received:</string>
|
||||
<string name="connecting">Connecting…</string>
|
||||
<string name="vpn_connected">Connected, tap to check connection</string>
|
||||
<string name="nat_connected">Connected</string>
|
||||
<string name="local_connected">Connected</string>
|
||||
<string name="not_connected">Not connected</string>
|
||||
|
||||
<!-- acl -->
|
||||
|
||||
@@ -8,6 +8,6 @@
|
||||
android:summary="@string/tcp_fastopen_summary"
|
||||
android:title="TCP Fast Open"/>
|
||||
<SwitchPreference android:key="isNAT"
|
||||
android:title="@string/nat"
|
||||
android:summary="@string/nat_summary"/>
|
||||
android:title="@string/local"
|
||||
android:summary="@string/local_summary"/>
|
||||
</PreferenceScreen>
|
||||
|
||||
@@ -117,7 +117,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
|
||||
if (state == State.CONNECTING) fabProgressCircle.beginFinalAnimation()
|
||||
else fabProgressCircle.postDelayed(hideCircle, 1000)
|
||||
fab.setImageResource(R.drawable.ic_start_connected)
|
||||
statusText.setText(if (app.isNatEnabled) R.string.nat_connected else R.string.vpn_connected)
|
||||
statusText.setText(if (app.isLocalEnabled) R.string.local_connected else R.string.vpn_connected)
|
||||
case State.STOPPING =>
|
||||
fab.setImageResource(R.drawable.ic_start_busy)
|
||||
if (state == State.CONNECTED) fabProgressCircle.show() // ignore for stopped
|
||||
@@ -128,7 +128,6 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
|
||||
if (m != null) {
|
||||
val snackbar = Snackbar.make(findViewById(R.id.snackbar),
|
||||
getString(R.string.vpn_error).formatLocal(Locale.ENGLISH, m), Snackbar.LENGTH_LONG)
|
||||
if (m == getString(R.string.nat_no_root)) addDisableNatToSnackbar(snackbar)
|
||||
snackbar.show()
|
||||
Log.e(TAG, "Error to start VPN service: " + m)
|
||||
}
|
||||
@@ -157,16 +156,9 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
|
||||
|
||||
override def onServiceConnected() {
|
||||
changeState(bgService.getState)
|
||||
if (Build.VERSION.SDK_INT >= 21 && app.isNatEnabled) {
|
||||
val snackbar = Snackbar.make(findViewById(R.id.snackbar), R.string.nat_deprecated, Snackbar.LENGTH_LONG)
|
||||
addDisableNatToSnackbar(snackbar)
|
||||
snackbar.show()
|
||||
}
|
||||
}
|
||||
override def onServiceDisconnected(): Unit = changeState(State.IDLE)
|
||||
|
||||
private def addDisableNatToSnackbar(snackbar: Snackbar) = snackbar.setAction(R.string.switch_to_vpn, (_ =>
|
||||
if (state == State.STOPPED) app.dataStore.isNAT = false): View.OnClickListener)
|
||||
|
||||
override def binderDied(): Unit = handler.post(() => {
|
||||
detachService()
|
||||
@@ -300,7 +292,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
|
||||
fab = findViewById(R.id.fab).asInstanceOf[FloatingActionButton]
|
||||
fabProgressCircle = findViewById(R.id.fabProgressCircle).asInstanceOf[FABProgressCircle]
|
||||
fab.setOnClickListener(_ => if (state == State.CONNECTED) Utils.stopSsService(this) else Utils.ThrowableFuture {
|
||||
if (app.isNatEnabled) Utils.startSsService(this) else {
|
||||
if (app.isLocalEnabled) Utils.startSsService(this) else {
|
||||
val intent = VpnService.prepare(this)
|
||||
if (intent != null) startActivityForResult(intent, REQUEST_CONNECT)
|
||||
else handler.post(() => onActivityResult(REQUEST_CONNECT, Activity.RESULT_OK, null))
|
||||
|
||||
@@ -66,7 +66,7 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
|
||||
findPreference(Key.password).setSummary("\u2022" * 32)
|
||||
}
|
||||
isProxyApps = findPreference(Key.proxyApps).asInstanceOf[SwitchPreference]
|
||||
isProxyApps.setEnabled(Utils.isLollipopOrAbove || app.isNatEnabled)
|
||||
isProxyApps.setEnabled(Utils.isLollipopOrAbove || app.isLocalEnabled)
|
||||
isProxyApps.setOnPreferenceClickListener(_ => {
|
||||
startActivity(new Intent(getActivity, classOf[AppManager]))
|
||||
isProxyApps.setChecked(true)
|
||||
|
||||
@@ -85,7 +85,7 @@ trait ServiceBoundContext extends Context with IBinder.DeathRecipient {
|
||||
protected def attachService(callback: IShadowsocksServiceCallback.Stub = null) {
|
||||
this.callback = callback
|
||||
if (bgService == null) {
|
||||
val s = if (app.isNatEnabled) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService]
|
||||
val s = if (app.isLocalEnabled) classOf[ShadowsocksLocalService] else classOf[ShadowsocksVpnService]
|
||||
|
||||
val intent = new Intent(this, s)
|
||||
intent.setAction(Action.SERVICE)
|
||||
|
||||
@@ -66,8 +66,8 @@ class ShadowsocksApplication extends Application {
|
||||
lazy val profileManager = new ProfileManager(dbHelper)
|
||||
lazy val dataStore = new OrmLitePreferenceDataStore(dbHelper)
|
||||
|
||||
def isNatEnabled: Boolean = dataStore.isNAT
|
||||
def isVpnEnabled: Boolean = !isNatEnabled
|
||||
def isLocalEnabled: Boolean = dataStore.isNAT
|
||||
def isVpnEnabled: Boolean = !isLocalEnabled
|
||||
|
||||
// send event
|
||||
def track(category: String, action: String): Unit = tracker.send(new HitBuilders.EventBuilder()
|
||||
@@ -156,7 +156,7 @@ class ShadowsocksApplication extends Application {
|
||||
|
||||
if (Build.VERSION.SDK_INT >= 26) getSystemService(classOf[NotificationManager]).createNotificationChannels(List(
|
||||
new NotificationChannel("service-vpn", getText(R.string.service_vpn), NotificationManager.IMPORTANCE_MIN),
|
||||
new NotificationChannel("service-nat", getText(R.string.service_nat), NotificationManager.IMPORTANCE_LOW)
|
||||
new NotificationChannel("service-local", getText(R.string.service_local), NotificationManager.IMPORTANCE_LOW)
|
||||
))
|
||||
}
|
||||
|
||||
@@ -165,15 +165,9 @@ class ShadowsocksApplication extends Application {
|
||||
|
||||
for (task <- Executable.EXECUTABLES) {
|
||||
cmd.append("killall lib%s.so".formatLocal(Locale.ENGLISH, task))
|
||||
cmd.append("rm -f %1$s/%2$s-nat.conf %1$s/%2$s-vpn.conf"
|
||||
cmd.append("rm -f %1$s/%2$s-local.conf %1$s/%2$s-vpn.conf"
|
||||
.formatLocal(Locale.ENGLISH, getFilesDir.getAbsolutePath, task))
|
||||
}
|
||||
if (app.isNatEnabled) {
|
||||
cmd.append("iptables -t nat -F OUTPUT")
|
||||
cmd.append("echo done")
|
||||
val result = Shell.SU.run(cmd.toArray)
|
||||
if (result != null && !result.isEmpty) return // fallback to SH
|
||||
}
|
||||
Shell.SH.run(cmd.toArray)
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* 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/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks
|
||||
|
||||
import java.io.File
|
||||
import java.net.{Inet6Address, InetAddress}
|
||||
import java.util.Locale
|
||||
|
||||
import android.app.Service
|
||||
import android.content._
|
||||
import android.os._
|
||||
import android.util.Log
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.acl.{Acl, AclSyncJob}
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.utils._
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
class ShadowsocksLocalService extends BaseService {
|
||||
|
||||
val TAG = "ShadowsocksLocalService"
|
||||
|
||||
var sslocalProcess: GuardedProcess = _
|
||||
|
||||
def startShadowsocksDaemon() {
|
||||
val cmd = ArrayBuffer[String](getApplicationInfo.nativeLibraryDir + "/libss-local.so",
|
||||
"-b", "127.0.0.1",
|
||||
"-l", profile.localPort.toString,
|
||||
"-t", "600",
|
||||
"-c", buildShadowsocksConfig("ss-local-local.conf"))
|
||||
|
||||
if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
|
||||
|
||||
if (profile.route != Acl.ALL) {
|
||||
cmd += "--acl"
|
||||
cmd += Acl.getFile(profile.route match {
|
||||
case Acl.CUSTOM_RULES => Acl.CUSTOM_RULES_FLATTENED
|
||||
case route => route
|
||||
}).getAbsolutePath
|
||||
}
|
||||
|
||||
sslocalProcess = new GuardedProcess(cmd: _*).start()
|
||||
}
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
def handleConnection() {
|
||||
|
||||
startShadowsocksDaemon()
|
||||
|
||||
}
|
||||
|
||||
def onBind(intent: Intent): IBinder = {
|
||||
Log.d(TAG, "onBind")
|
||||
if (Action.SERVICE == intent.getAction) {
|
||||
binder
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
def killProcesses() {
|
||||
if (sslocalProcess != null) {
|
||||
sslocalProcess.destroy()
|
||||
sslocalProcess = null
|
||||
}
|
||||
}
|
||||
|
||||
override def connect() {
|
||||
super.connect()
|
||||
|
||||
// Clean up
|
||||
killProcesses()
|
||||
|
||||
if (!Utils.isNumeric(profile.host)) Utils.resolve(profile.host, enableIPv6 = true) match {
|
||||
case Some(a) => profile.host = a
|
||||
case None => throw NameNotResolvedException()
|
||||
}
|
||||
|
||||
handleConnection()
|
||||
|
||||
if (profile.route != Acl.ALL && profile.route != Acl.CUSTOM_RULES)
|
||||
AclSyncJob.schedule(profile.route)
|
||||
|
||||
changeState(State.CONNECTED)
|
||||
}
|
||||
|
||||
override def createNotification() = new ShadowsocksNotification(this, profile.name, "service-local", true)
|
||||
|
||||
override def stopRunner(stopService: Boolean, msg: String = null) {
|
||||
|
||||
// channge the state
|
||||
changeState(State.STOPPING)
|
||||
|
||||
app.track(TAG, "stop")
|
||||
|
||||
// reset NAT
|
||||
killProcesses()
|
||||
|
||||
super.stopRunner(stopService, msg)
|
||||
}
|
||||
}
|
||||
@@ -1,231 +0,0 @@
|
||||
/*******************************************************************************/
|
||||
/* */
|
||||
/* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> */
|
||||
/* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> */
|
||||
/* */
|
||||
/* 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/>. */
|
||||
/* */
|
||||
/*******************************************************************************/
|
||||
|
||||
package com.github.shadowsocks
|
||||
|
||||
import java.io.File
|
||||
import java.net.{Inet6Address, InetAddress}
|
||||
import java.util.Locale
|
||||
|
||||
import android.app.Service
|
||||
import android.content._
|
||||
import android.os._
|
||||
import android.util.Log
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.acl.{Acl, AclSyncJob}
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import com.github.shadowsocks.utils._
|
||||
import eu.chainfire.libsuperuser.Shell
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
import scala.collection.mutable.ArrayBuffer
|
||||
|
||||
class ShadowsocksNatService extends BaseService {
|
||||
|
||||
val TAG = "ShadowsocksNatService"
|
||||
|
||||
val CMD_IPTABLES_DNAT_ADD_SOCKS =
|
||||
"iptables -t nat -A OUTPUT -p tcp -j DNAT --to-destination 127.0.0.1:8123"
|
||||
|
||||
val myUid: Int = android.os.Process.myUid()
|
||||
|
||||
var sslocalProcess: GuardedProcess = _
|
||||
var sstunnelProcess: GuardedProcess = _
|
||||
var redsocksProcess: GuardedProcess = _
|
||||
var overtureProcess: GuardedProcess = _
|
||||
var su: Shell.Interactive = _
|
||||
|
||||
def startShadowsocksDaemon() {
|
||||
val cmd = ArrayBuffer[String](getApplicationInfo.nativeLibraryDir + "/libss-local.so",
|
||||
"-b", "127.0.0.1",
|
||||
"-l", profile.localPort.toString,
|
||||
"-t", "600",
|
||||
"-c", buildShadowsocksConfig("ss-local-nat.conf"))
|
||||
|
||||
if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
|
||||
|
||||
if (profile.route != Acl.ALL) {
|
||||
cmd += "--acl"
|
||||
cmd += Acl.getFile(profile.route match {
|
||||
case Acl.CUSTOM_RULES => Acl.CUSTOM_RULES_FLATTENED
|
||||
case route => route
|
||||
}).getAbsolutePath
|
||||
}
|
||||
|
||||
sslocalProcess = new GuardedProcess(cmd: _*).start()
|
||||
}
|
||||
|
||||
def startDNSTunnel() {
|
||||
val cmd = ArrayBuffer[String](getApplicationInfo.nativeLibraryDir + "/libss-tunnel.so",
|
||||
"-t", "10",
|
||||
"-b", "127.0.0.1",
|
||||
"-l", (profile.localPort + 63).toString,
|
||||
"-L", profile.remoteDns.split(",").head.trim + ":53",
|
||||
"-c", buildShadowsocksConfig("ss-tunnel-nat.conf"))
|
||||
|
||||
if (profile.udpdns) cmd.append("-u")
|
||||
|
||||
sstunnelProcess = new GuardedProcess(cmd: _*).start()
|
||||
}
|
||||
|
||||
def startDnsDaemon() {
|
||||
overtureProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/liboverture.so",
|
||||
"-c", buildOvertureConfig("overture-nat.conf"))
|
||||
.start()
|
||||
}
|
||||
|
||||
def startRedsocksDaemon() {
|
||||
IOUtils.writeString(new File(getFilesDir, "redsocks-nat.conf"),
|
||||
ConfigUtils.REDSOCKS.formatLocal(Locale.ENGLISH, profile.localPort))
|
||||
redsocksProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/libredsocks.so",
|
||||
"-c", "redsocks-nat.conf").start()
|
||||
}
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
def handleConnection() {
|
||||
|
||||
startRedsocksDaemon()
|
||||
startShadowsocksDaemon()
|
||||
|
||||
if (!profile.udpdns)
|
||||
startDnsDaemon()
|
||||
else
|
||||
startDNSTunnel()
|
||||
|
||||
setupIptables()
|
||||
|
||||
}
|
||||
|
||||
def onBind(intent: Intent): IBinder = {
|
||||
Log.d(TAG, "onBind")
|
||||
if (Action.SERVICE == intent.getAction) {
|
||||
binder
|
||||
} else {
|
||||
null
|
||||
}
|
||||
}
|
||||
|
||||
def killProcesses() {
|
||||
if (sslocalProcess != null) {
|
||||
sslocalProcess.destroy()
|
||||
sslocalProcess = null
|
||||
}
|
||||
if (sstunnelProcess != null) {
|
||||
sstunnelProcess.destroy()
|
||||
sstunnelProcess = null
|
||||
}
|
||||
if (redsocksProcess != null) {
|
||||
redsocksProcess.destroy()
|
||||
redsocksProcess = null
|
||||
}
|
||||
if (overtureProcess != null) {
|
||||
overtureProcess.destroy()
|
||||
overtureProcess = null
|
||||
}
|
||||
|
||||
su.addCommand("iptables -t nat -F OUTPUT")
|
||||
}
|
||||
|
||||
def setupIptables() {
|
||||
val init_sb = new ArrayBuffer[String]
|
||||
val http_sb = new ArrayBuffer[String]
|
||||
|
||||
init_sb.append("ulimit -n 4096")
|
||||
init_sb.append("iptables -t nat -F OUTPUT")
|
||||
|
||||
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]) {
|
||||
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 127.0.0.1"))
|
||||
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("iptables -t nat -A OUTPUT -p udp --dport 53 -j DNAT --to-destination 127.0.0.1:"
|
||||
+ (profile.localPort + 53))
|
||||
|
||||
if (!profile.proxyApps || profile.bypass) {
|
||||
http_sb.append(CMD_IPTABLES_DNAT_ADD_SOCKS)
|
||||
}
|
||||
if (profile.proxyApps) {
|
||||
val uidMap = getPackageManager.getInstalledApplications(0).map(ai => ai.packageName -> ai.uid).toMap
|
||||
for (pn <- profile.individual.split('\n')) uidMap.get(pn) match {
|
||||
case Some(uid) =>
|
||||
if (!profile.bypass) {
|
||||
http_sb.append(CMD_IPTABLES_DNAT_ADD_SOCKS
|
||||
.replace("-t nat", "-t nat -m owner --uid-owner " + uid))
|
||||
} else {
|
||||
init_sb.append(cmd_bypass.replace("-d 0.0.0.0", "-m owner --uid-owner " + uid))
|
||||
}
|
||||
case _ => // probably removed package, ignore
|
||||
}
|
||||
}
|
||||
su.addCommand((init_sb ++ http_sb).toArray)
|
||||
}
|
||||
|
||||
override def onStartCommand(intent: Intent, flags: Int, startId: Int): Int = if (su == null) {
|
||||
su = new Shell.Builder().useSU().setWantSTDERR(true).setWatchdogTimeout(10).open((_, exitCode, _) =>
|
||||
if (exitCode == 0) super.onStartCommand(intent, flags, startId) else {
|
||||
if (su != null) {
|
||||
su.close()
|
||||
su = null
|
||||
}
|
||||
super.stopRunner(stopService = true, getString(R.string.nat_no_root))
|
||||
})
|
||||
Service.START_NOT_STICKY
|
||||
} else super.onStartCommand(intent, flags, startId)
|
||||
|
||||
override def connect() {
|
||||
super.connect()
|
||||
|
||||
// Clean up
|
||||
killProcesses()
|
||||
|
||||
if (!Utils.isNumeric(profile.host)) Utils.resolve(profile.host, enableIPv6 = true) match {
|
||||
case Some(a) => profile.host = a
|
||||
case None => throw NameNotResolvedException()
|
||||
}
|
||||
|
||||
handleConnection()
|
||||
|
||||
if (profile.route != Acl.ALL && profile.route != Acl.CUSTOM_RULES)
|
||||
AclSyncJob.schedule(profile.route)
|
||||
|
||||
changeState(State.CONNECTED)
|
||||
}
|
||||
|
||||
override def createNotification() = new ShadowsocksNotification(this, profile.name, "service-nat", true)
|
||||
|
||||
override def stopRunner(stopService: Boolean, msg: String = null) {
|
||||
|
||||
// channge the state
|
||||
changeState(State.STOPPING)
|
||||
|
||||
app.track(TAG, "stop")
|
||||
|
||||
// reset NAT
|
||||
killProcesses()
|
||||
|
||||
su.close()
|
||||
su = null
|
||||
|
||||
super.stopRunner(stopService, msg)
|
||||
}
|
||||
}
|
||||
@@ -46,7 +46,7 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext {
|
||||
}
|
||||
|
||||
def startBackgroundService() {
|
||||
if (app.isNatEnabled) {
|
||||
if (app.isLocalEnabled) {
|
||||
Utils.startSsService(this)
|
||||
finish()
|
||||
} else {
|
||||
|
||||
@@ -33,7 +33,7 @@ import android.view.View.MeasureSpec
|
||||
import android.view.{Gravity, View, Window}
|
||||
import android.widget.Toast
|
||||
import com.github.shadowsocks.ShadowsocksApplication.app
|
||||
import com.github.shadowsocks.{BuildConfig, ShadowsocksNatService, ShadowsocksVpnService}
|
||||
import com.github.shadowsocks.{BuildConfig, ShadowsocksLocalService, ShadowsocksVpnService}
|
||||
import org.xbill.DNS._
|
||||
|
||||
import scala.collection.JavaConversions._
|
||||
@@ -138,7 +138,7 @@ object Utils {
|
||||
|
||||
def startSsService(context: Context) {
|
||||
val intent =
|
||||
new Intent(context, if (app.dataStore.isNAT) classOf[ShadowsocksNatService] else classOf[ShadowsocksVpnService])
|
||||
new Intent(context, if (app.dataStore.isNAT) classOf[ShadowsocksLocalService] else classOf[ShadowsocksVpnService])
|
||||
if (Build.VERSION.SDK_INT >= 26) context.startForegroundService(intent) else context.startService(intent)
|
||||
}
|
||||
def reloadSsService(context: Context): Unit = context.sendBroadcast(new Intent(Action.RELOAD))
|
||||
|
||||
Reference in New Issue
Block a user