Commit 6678fd95 authored by Mygod's avatar Mygod

Revert "Replace NAT mode with local SOCKS5 mode. #1441"

This reverts commit 7fb19c40.
parent 7fb19c40
......@@ -9,6 +9,14 @@
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=".ShadowsocksLocalService"
android:name=".ShadowsocksNatService"
android:process=":bg"
android:exported="false">
</service>
......
......@@ -78,6 +78,32 @@ 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
########################################################
......@@ -174,6 +200,28 @@ 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
########################################################
......
Subproject commit 359ca847a649b9c318f9217c0755484d98ecb779
Subproject commit 274334f14839431ae003774d99c3d1de337afff4
......@@ -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="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="nat">NAT mode (deprecated)</string>
<string name="nat_summary">Use NAT mode instead of VPN mode. Requires ROOT permission.</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</string>
<string name="proxied_apps_summary">Set proxy for selected apps, requires NAT mode under Android 4.x</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,10 +57,12 @@
<!-- notification category -->
<string name="service_vpn">VPN Service</string>
<string name="service_local">SOCKS5 Service</string>
<string name="service_nat">NAT 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>
......@@ -122,7 +124,7 @@
<string name="received">Received:</string>
<string name="connecting">Connecting…</string>
<string name="vpn_connected">Connected, tap to check connection</string>
<string name="local_connected">Connected</string>
<string name="nat_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/local"
android:summary="@string/local_summary"/>
android:title="@string/nat"
android:summary="@string/nat_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.isLocalEnabled) R.string.local_connected else R.string.vpn_connected)
statusText.setText(if (app.isNatEnabled) R.string.nat_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,6 +128,7 @@ 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)
}
......@@ -156,9 +157,16 @@ 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()
......@@ -292,7 +300,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.isLocalEnabled) Utils.startSsService(this) else {
if (app.isNatEnabled) 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.isLocalEnabled)
isProxyApps.setEnabled(Utils.isLollipopOrAbove || app.isNatEnabled)
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.isLocalEnabled) classOf[ShadowsocksLocalService] else classOf[ShadowsocksVpnService]
val s = if (app.isNatEnabled) classOf[ShadowsocksNatService] 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 isLocalEnabled: Boolean = dataStore.isNAT
def isVpnEnabled: Boolean = !isLocalEnabled
def isNatEnabled: Boolean = dataStore.isNAT
def isVpnEnabled: Boolean = !isNatEnabled
// 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-local", getText(R.string.service_local), NotificationManager.IMPORTANCE_LOW)
new NotificationChannel("service-nat", getText(R.string.service_nat), NotificationManager.IMPORTANCE_LOW)
))
}
......@@ -165,9 +165,15 @@ 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-local.conf %1$s/%2$s-vpn.conf"
cmd.append("rm -f %1$s/%2$s-nat.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)
}
......
......@@ -32,22 +32,32 @@ 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 ShadowsocksLocalService extends BaseService {
class ShadowsocksNatService extends BaseService {
val TAG = "ShadowsocksLocalService"
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-local.conf"))
"-c", buildShadowsocksConfig("ss-local-nat.conf"))
if (TcpFastOpen.sendEnabled) cmd += "--fast-open"
......@@ -62,11 +72,45 @@ class ShadowsocksLocalService extends BaseService {
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 = {
......@@ -83,8 +127,71 @@ class ShadowsocksLocalService extends BaseService {
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()
......@@ -104,7 +211,7 @@ class ShadowsocksLocalService extends BaseService {
changeState(State.CONNECTED)
}
override def createNotification() = new ShadowsocksNotification(this, profile.name, "service-local", true)
override def createNotification() = new ShadowsocksNotification(this, profile.name, "service-nat", true)
override def stopRunner(stopService: Boolean, msg: String = null) {
......@@ -116,6 +223,9 @@ class ShadowsocksLocalService extends BaseService {
// 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.isLocalEnabled) {
if (app.isNatEnabled) {
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, ShadowsocksLocalService, ShadowsocksVpnService}
import com.github.shadowsocks.{BuildConfig, ShadowsocksNatService, 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[ShadowsocksLocalService] else classOf[ShadowsocksVpnService])
new Intent(context, if (app.dataStore.isNAT) classOf[ShadowsocksNatService] 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))
......
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