Skip to content

Commit

Permalink
Replace NAT mode with local SOCKS5 mode. shadowsocks#1441
Browse files Browse the repository at this point in the history
  • Loading branch information
madeye committed Nov 10, 2017
1 parent 377dfaf commit 08f5920
Show file tree
Hide file tree
Showing 15 changed files with 141 additions and 325 deletions.
8 changes: 0 additions & 8 deletions .gitmodules
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
4 changes: 2 additions & 2 deletions mobile/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -109,7 +109,7 @@
</activity>

<service
android:name=".ShadowsocksNatService"
android:name=".ShadowsocksLocalService"
android:process=":bg"
android:exported="false">
</service>
Expand Down
48 changes: 0 additions & 48 deletions mobile/src/main/jni/Android.mk
Original file line number Diff line number Diff line change
Expand Up @@ -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
########################################################
Expand Down Expand Up @@ -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
########################################################
Expand Down
1 change: 0 additions & 1 deletion mobile/src/main/jni/libevent
Submodule libevent deleted from 359ca8
1 change: 0 additions & 1 deletion mobile/src/main/jni/redsocks
Submodule redsocks deleted from 274334
12 changes: 5 additions & 7 deletions mobile/src/main/res/values/strings.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
Expand Down Expand Up @@ -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>
Expand All @@ -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>
Expand Down Expand Up @@ -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 -->
Expand Down
4 changes: 2 additions & 2 deletions mobile/src/main/res/xml/pref_global.xml
Original file line number Diff line number Diff line change
Expand Up @@ -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>
12 changes: 2 additions & 10 deletions mobile/src/main/scala/com/github/shadowsocks/MainActivity.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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)
}
Expand Down Expand Up @@ -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()
Expand Down Expand Up @@ -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))
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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)
))
}

Expand All @@ -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)
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
/*******************************************************************************/
/* */
/* Copyright (C) 2017 by Max Lv <[email protected]> */
/* Copyright (C) 2017 by Mygod Studio <[email protected]> */
/* */
/* 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)
}
}
Loading

0 comments on commit 08f5920

Please sign in to comment.