Commit a6e87cf6 authored by Mygod's avatar Mygod

Merge branch 'master' of github.com:shadowsocks/shadowsocks-android

parents 81295e4a 4817539c
......@@ -14,6 +14,7 @@ gen_*
*.class
*.o
local.sbt
.deps
#Intellij IDEA
*.iml
......@@ -26,8 +27,6 @@ out/
#NDK
src/main/obj
src/main/libs
src/main/assets/armeabi
src/main/assets/armeabi-v7a
src/main/assets/x86
src/main/assets/api-16/
src/main/assets/armeabi-v7a/*
src/main/assets/x86/*
jni/Application.mk
......@@ -24,3 +24,9 @@
path = src/main/jni/pdnsd
url = https://github.com/shadowsocks/pdnsd.git
branch = shadowsocks-android
[submodule "kcptun/kcptun"]
path = kcptun/kcptun
url = https://github.com/shadowsocks/kcptun
[submodule "kcptun/go"]
path = kcptun/go
url = https://github.com/shadowsocks/go
......@@ -31,6 +31,7 @@ install:
- ./travis-ci/setup.sh
- export NDK_CCACHE=ccache
- export ARCH=`uname -m`
- export GOROOT_BOOTSTRAP=$HOME/.android/go
- export ANDROID_NDK_HOME=$HOME/.android/android-ndk-r12b
- export ANDROID_HOME=$HOME/.android/android-sdk-linux
- export PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
......
......@@ -12,6 +12,7 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* JDK 1.8
* SBT 0.13.0+
* GO 1.4+
* Android SDK r25+
* Android NDK r12b+
......@@ -19,6 +20,7 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* Set environment variable `ANDROID_HOME` to `/path/to/android-sdk`
* Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk`
* Set environment variable `GOROOT_BOOTSTRAP` to `/path/to/go`
* Create your key following the instructions at https://developer.android.com/studio/publish/app-signing.html
* Put your key in ~/.keystore
* Create `local.properties` from `local.properties.example` with your own key information
......@@ -31,28 +33,6 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
sbt native-build clean android:package-release
```
#### BUILD on Mac OS X (with HomeBrew)
* Install Android SDK and NDK by run `brew install android-ndk android-sdk`
* Add `export ANDROID_HOME=/usr/local/Cellar/android-sdk/$version` to your .bashrc , then reopen the shell to load it.
* Add `export ANDROID_NDK_HOME=/usr/local/Cellar/android-ndk/$version` to your .bashrc , then reopen the shell to load it.
* echo "y" | android update sdk --filter tools,platform-tools,build-tools-23.0.2,android-23,extra-google-m2repository --no-ui -a
* echo "y" | android update sdk --filter extra-android-m2repository --no-ui --no-https -a
* Create your key following the instructions at http://developer.android.com/guide/publishing/app-signing.html#cert
* Put your key in ~/.keystore
* Create `local.properties` from `local.properties.example` with your own key information .
* Invoke the building like this
```bash
git submodule update --init
# Build native binaries
./build.sh
# Build the apk
sbt clean android:package-release
```
## OPEN SOURCE LICENSES
* shadowsocks-libev: [GPLv3](https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE)
......@@ -62,6 +42,7 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* pdnsd: [GPLv3](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING)
* libev: [GPLv2](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libev/LICENSE)
* libevent: [BSD](https://github.com/shadowsocks/libevent/blob/master/LICENSE)
* kcptun: [MIT](https://github.com/xtaci/kcptun)
### LICENSE
......
......@@ -55,6 +55,8 @@ proguardCache in Android := Seq()
proguardOptions in Android ++= Seq(
"-keep class com.github.shadowsocks.System { *; }",
"-dontwarn com.google.android.gms.**",
"-keep class com.google.android.gms.**",
"-dontnote com.google.android.gms.internal.**",
"-dontnote com.j256.ormlite.**",
"-dontnote me.dm7.barcodescannerview.**",
......
......@@ -4,21 +4,25 @@ function try () {
"$@" || exit -1
}
try pushd src/main
# Build
#try $ANDROID_NDK_HOME/ndk-build clean
# Build native binaries
pushd src/main
try $ANDROID_NDK_HOME/ndk-build -j8
# copy executables
rm -rf assets/armeabi-v7a
rm -rf assets/x86
mkdir -p assets/armeabi-v7a
mkdir -p assets/x86
#copy executables
for app in pdnsd redsocks ss-local ss-tunnel tun2socks
do
echo $app
try mv libs/armeabi-v7a/$app assets/armeabi-v7a/
try mv libs/x86/$app assets/x86/
done
popd
try popd
# Build kcptun
pushd kcptun
try ./make.bash
popd
Subproject commit f3e7e7b69f0fd0c22858d09b4dd3764ed703d140
Subproject commit eced3949b2f33e1801aec6124a01922482358f3e
#!/bin/bash
function try () {
"$@" || exit -1
}
[ -z "$ANDROID_NDK_HOME" ] && ANDROID_NDK_HOME=~/android-ndk-r12b
DIR="$( cd "$( dirname "${BASH_SOURCE[0]}" )" && pwd )"
DEPS=$DIR/.deps
ANDROID_ARM_TOOLCHAIN=$DEPS/android-toolchain-16-arm
ANDROID_X86_TOOLCHAIN=$DEPS/android-toolchain-16-x86
ANDROID_ARM_CC=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-gcc
ANDROID_ARM_STRIP=$ANDROID_ARM_TOOLCHAIN/bin/arm-linux-androideabi-strip
ANDROID_X86_CC=$ANDROID_X86_TOOLCHAIN/bin/i686-linux-android-gcc
ANDROID_X86_STRIP=$ANDROID_X86_TOOLCHAIN/bin/i686-linux-android-strip
if [ ! -d "$DEPS" ]; then
mkdir -p $DEPS
fi
if [ ! -d "$ANDROID_ARM_TOOLCHAIN" ]; then
echo "Make standalone toolchain for ARM arch"
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch arm \
--api 16 --install-dir $ANDROID_ARM_TOOLCHAIN
fi
if [ ! -d "$ANDROID_X86_TOOLCHAIN" ]; then
echo "Make standalone toolchain for X86 arch"
$ANDROID_NDK_HOME/build/tools/make_standalone_toolchain.py --arch x86 \
--api 16 --install-dir $ANDROID_X86_TOOLCHAIN
fi
if [ ! -d "$DIR/go/bin" ]; then
echo "Build the custom go"
pushd $DIR/go/src
try ./make.bash
popd
fi
export GOROOT=$DIR/go
export GOPATH=$DEPS
export PATH=$GOROOT/bin:$PATH
pushd kcptun/client
echo "Get dependences for kcptun"
go get
echo "Cross compile kcptun for arm"
try env CGO_ENABLED=1 CC=$ANDROID_ARM_CC GOOS=android GOARCH=arm GOARM=7 go build
try $ANDROID_ARM_STRIP client
try mv client $DIR/../src/main/assets/armeabi-v7a/kcptun
echo "Cross compile kcptun for x86"
try env CGO_ENABLED=1 CC=$ANDROID_X86_CC GOOS=android GOARCH=386 go build
try $ANDROID_X86_STRIP client
try mv client $DIR/../src/main/assets/x86/kcptun
popd
echo "Successfully build kcptun"
addSbtPlugin("org.scala-android" % "sbt-android" % "1.6.8")
addSbtPlugin("org.scala-android" % "sbt-android" % "1.6.10")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10")
......
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools" package="com.github.shadowsocks"
android:versionCode="141"
android:versionName="2.10.8">
android:versionCode="144"
android:versionName="3.0.2">
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
......
......@@ -296,6 +296,7 @@ along with this program. If not, see <a href="http://www.gnu.org/licenses/">http
<li>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li>
<li>libev: <a href="https://github.com/shadowsocks/libevent/blob/shadowsocks-android/LICENSE">GPLv2</a></li>
<li>libevent: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libevent/LICENSE">BSD</a></li>
<li>kcptun: <a href="https://github.com/xtaci/kcptun/commits/master/LICENSE.md">MIT</a></li>
</ul>
</body>
......
Subproject commit d4aabcb40b4bcbca352269a1474a19de691950d0
Subproject commit 915047e957d5bd581be8e144ed92a1bb05441af5
......@@ -134,4 +134,11 @@
<!-- status -->
<string name="sent">Отправлено:</string>
<string name="received">Получено:</string>
<!-- kcp -->
<string name="kcp_summary">Включить Shadowsocks над KCPTUN</string>
<string name="kcp_port">KCP порт</string>
<string name="kcp_port_summary">%d (KCPTUN омер порта удалённого сервера)</string>
<string name="kcpcli">KCP параметры</string>
<string name="kcpcli_summary">Командная строка KCPTUN</string>
</resources>
......@@ -134,4 +134,11 @@
<string name="sent">发送:</string>
<string name="received">接收:</string>
<!-- kcp -->
<string name="kcp_summary">启用 KCP 协议</string>
<string name="kcp_port">KCP 端口</string>
<string name="kcp_port_summary">%d(远端 KCPTUN 服务器的端口号)</string>
<string name="kcpcli">KCP 参数</string>
<string name="kcpcli_summary">KCPTUN 的命令行参数</string>
</resources>
......@@ -136,4 +136,11 @@
<string name="sent">傳送:</string>
<string name="received">接收:</string>
<!-- kcp -->
<string name="kcp_summary">啟用 KCP 協定</string>
<string name="kcp_port">KCP 連接埠</string>
<string name="kcp_port_summary">%d(遠端 KCPTUN 伺服器的連接埠號碼)</string>
<string name="kcpcli">KCP 參數</string>
<string name="kcpcli_summary">KCPTUN 的命令行參數</string>
</resources>
......@@ -139,4 +139,12 @@
<!-- status -->
<string name="sent">Sent:</string>
<string name="received">Received:</string>
<!-- kcp -->
<string name="kcp" translatable="false">KCP</string>
<string name="kcp_summary">Enable Shadowsocks over KCP</string>
<string name="kcp_port">KCP Port</string>
<string name="kcp_port_summary">%d (port number of the remote KCPTUN server)</string>
<string name="kcpcli">KCP Parameters (CLI)</string>
<string name="kcpcli_summary">The command line of KCPTUN</string>
</resources>
......@@ -77,6 +77,31 @@
</PreferenceCategory>
<PreferenceCategory
android:title="@string/kcp">
<SwitchPreference
android:key="kcp"
android:persistent="false"
android:summary="@string/kcp_summary"
android:title="@string/kcp"/>
<com.github.shadowsocks.preferences.NumberPickerPreference
app:min="1"
app:max="65535"
android:key="kcpPort"
android:persistent="false"
android:summary="@string/kcp_port_summary"
android:title="@string/kcp_port"/>
<com.github.shadowsocks.preferences.SummaryEditTextPreference
android:key="kcpcli"
android:persistent="false"
android:summary="@string/kcpcli_summary"
android:title="@string/kcpcli"/>
</PreferenceCategory>
<PreferenceCategory android:title="@string/misc_cat">
<SwitchPreference
......
......@@ -43,10 +43,12 @@ import java.util.{Timer, TimerTask}
import android.app.Service
import android.content.{BroadcastReceiver, Context, Intent, IntentFilter}
import android.net.ConnectivityManager
import android.os.{Handler, RemoteCallbackList}
import android.text.TextUtils
import android.util.Log
import android.widget.Toast
import com.github.kevinsawicki.http.HttpRequest
import com.github.shadowsocks.aidl.{IShadowsocksService, IShadowsocksServiceCallback}
import com.github.shadowsocks.utils._
......@@ -71,6 +73,16 @@ trait BaseService extends Service {
}
var closeReceiverRegistered: Boolean = _
var kcptunProcess: GuardedProcess = _
private val networkReceiver: BroadcastReceiver = (context: Context, intent: Intent) => {
val cm = context.getSystemService(Context.CONNECTIVITY_SERVICE).asInstanceOf[ConnectivityManager]
val activeNetwork = cm.getActiveNetworkInfo()
val isConnected = activeNetwork != null && activeNetwork.isConnected()
if (isConnected && profile.kcp && kcptunProcess != null) kcptunProcess.restart()
}
var networkReceiverRegistered: Boolean = _
val binder = new IShadowsocksService.Stub {
override def getState: Int = {
state
......@@ -162,6 +174,14 @@ trait BaseService extends Service {
closeReceiverRegistered = true
}
if (profile.kcp && !networkReceiverRegistered) {
// register network change receiver
val filter = new IntentFilter()
filter.addAction(ConnectivityManager.CONNECTIVITY_ACTION)
registerReceiver(networkReceiver, filter)
networkReceiverRegistered = true
}
app.track(getClass.getSimpleName, "start")
changeState(State.CONNECTING)
......@@ -177,6 +197,10 @@ trait BaseService extends Service {
unregisterReceiver(closeReceiver)
closeReceiverRegistered = false
}
if (networkReceiverRegistered) {
unregisterReceiver(networkReceiver)
networkReceiverRegistered = false
}
// Make sure update total traffic when stopping the runner
updateTrafficTotal(TrafficMonitor.txTotal, TrafficMonitor.rxTotal)
......
......@@ -56,6 +56,7 @@ class GuardedProcess(cmd: Seq[String]) extends Process {
@volatile private var guardThread: Thread = _
@volatile private var isDestroyed: Boolean = _
@volatile private var process: Process = _
@volatile private var isRestart = false
def start(onRestartCallback: () => Unit = null): GuardedProcess = {
val semaphore = new Semaphore(1)
......@@ -76,11 +77,15 @@ class GuardedProcess(cmd: Seq[String]) extends Process {
semaphore.release
process.waitFor
if (isRestart) {
isRestart = false
} else {
if (currentTimeMillis - startTime < 1000) {
Log.w(TAG, "process exit too fast, stop guard: " + cmd)
isDestroyed = true
}
}
}
} catch {
case ignored: InterruptedException =>
Log.i(TAG, "thread interrupt, destroy process: " + cmd)
......@@ -108,6 +113,11 @@ class GuardedProcess(cmd: Seq[String]) extends Process {
}
}
def restart() {
isRestart = true
process.destroy()
}
def exitValue: Int = throw new UnsupportedOperationException
def getErrorStream: InputStream = throw new UnsupportedOperationException
def getInputStream: InputStream = throw new UnsupportedOperationException
......
......@@ -94,7 +94,7 @@ object Shadowsocks {
private final val TAG = "Shadowsocks"
private final val REQUEST_CONNECT = 1
private val EXECUTABLES = Array(Executable.PDNSD, Executable.REDSOCKS, Executable.SS_TUNNEL, Executable.SS_LOCAL,
Executable.TUN2SOCKS)
Executable.TUN2SOCKS, Executable.KCPTUN)
}
class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
......@@ -286,7 +286,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
def crashRecovery() {
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", "kcptun")) {
cmd.append("killall %s".formatLocal(Locale.ENGLISH, task))
cmd.append("rm -f %1$s/%2$s-nat.conf %1$s/%2$s-vpn.conf"
.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir, task))
......@@ -481,7 +481,7 @@ class Shadowsocks extends AppCompatActivity with ServiceBoundContext {
private def updatePreferenceScreen(profile: Profile) {
if (profile.host == "198.199.101.152") if (adView == null) {
adView = new AdView(this)
adView.setAdUnitId("ca-app-pub-9097031975646651/7760346322")
adView.setAdUnitId("ca-app-pub-4984938374218635/1433320509")
adView.setAdSize(AdSize.SMART_BANNER)
preferences.getView.asInstanceOf[ViewGroup].addView(adView, 1)
adView.loadAd(new AdRequest.Builder().build())
......
......@@ -84,9 +84,15 @@ class ShadowsocksNatService extends BaseService {
})
}
val conf = ConfigUtils
val conf = if (profile.kcp) {
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort,
profile.password, profile.method, 600)
} else {
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort,
profile.password, profile.method, 600)
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-nat.conf"))(p => {
p.println(conf)
})
......@@ -109,10 +115,23 @@ class ShadowsocksNatService extends BaseService {
sslocalProcess = new GuardedProcess(cmd).start()
}
def startTunnel() {
def startKcptunDaemon() {
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/kcptun"
, "-r", profile.host + ":" + profile.kcpPort
, "-l", "127.0.0.1:" + (profile.localPort + 90)
, profile.kcpcli)
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
kcptunProcess = new GuardedProcess(cmd.mkString(" ").split(" ").toSeq).start()
}
def startDNSTunnel() {
if (profile.udpdns) {
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, 8153,
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort + 53,
profile.password, profile.method, 10)
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => {
p.println(conf)
......@@ -123,11 +142,10 @@ class ShadowsocksNatService extends BaseService {
, "-t" , "10"
, "-b" , "127.0.0.1"
, "-L" , "8.8.8.8:53"
, "-l" , (profile.localPort + 53).toString
, "-P" , getApplicationInfo.dataDir
, "-c" , getApplicationInfo.dataDir + "/ss-tunnel-nat.conf")
cmd += ("-l" , "8153")
if (profile.auth) cmd += "-A"
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
......@@ -135,18 +153,23 @@ class ShadowsocksNatService extends BaseService {
sstunnelProcess = new GuardedProcess(cmd).start()
} else {
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, 8163,
val conf = if (profile.kcp) {
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90,
profile.localPort + 63, profile.password, profile.method, 10)
} else {
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort + 63,
profile.password, profile.method, 10)
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-nat.conf"))(p => {
p.println(conf)
})
val cmdBuf = new ArrayBuffer[String]
cmdBuf += (getApplicationInfo.dataDir + "/ss-tunnel"
, "-u"
, "-t" , "10"
, "-b" , "127.0.0.1"
, "-l" , "8163"
, "-l" , (profile.localPort + 63).toString
, "-L" , "8.8.8.8:53"
, "-P", getApplicationInfo.dataDir
, "-c" , getApplicationInfo.dataDir + "/ss-tunnel-nat.conf")
......@@ -163,10 +186,10 @@ class ShadowsocksNatService extends BaseService {
val conf = if (profile.route == Route.BYPASS_CHN || profile.route == Route.BYPASS_LAN_CHN) {
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", 8153, getBlackList, 8163, "")
"127.0.0.1", profile.localPort + 53, getBlackList, profile.localPort + 63, "")
} else {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"127.0.0.1", 8153, 8163, "")
"127.0.0.1", profile.localPort + 53, profile.localPort + 63, "")
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-nat.conf"))(p => {
......@@ -194,10 +217,13 @@ class ShadowsocksNatService extends BaseService {
/** Called when the activity is first created. */
def handleConnection {
startTunnel()
if (!profile.udpdns) startDnsDaemon()
startDNSTunnel()
startRedsocksDaemon()
startShadowsocksDaemon()
if (!profile.udpdns) startDnsDaemon()
if (profile.kcp) startKcptunDaemon()
setupIptables()
}
......@@ -212,6 +238,10 @@ class ShadowsocksNatService extends BaseService {
}
def killProcesses() {
if (kcptunProcess != null) {
kcptunProcess.destroy()
kcptunProcess = null
}
if (sslocalProcess != null) {
sslocalProcess.destroy()
sslocalProcess = null
......@@ -247,7 +277,8 @@ 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("-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:8153")
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)
......
......@@ -18,7 +18,7 @@ object ShadowsocksSettings {
// Constants
private final val TAG = "ShadowsocksSettings"
private val PROXY_PREFS = Array(Key.name, Key.host, Key.remotePort, Key.localPort, Key.password, Key.method,
Key.auth)
Key.auth, Key.kcp, Key.kcpPort, Key.kcpcli)
private val FEATURE_PREFS = Array(Key.route, Key.proxyApps, Key.udpdns, Key.ipv6)
// Helper functions
......@@ -36,9 +36,11 @@ object ShadowsocksSettings {
}
def updateSummaryEditTextPreference(pref: Preference, value: String) {
if (value != null) {
pref.setSummary(value)
pref.asInstanceOf[SummaryEditTextPreference].setText(value)
}
}
def updateSwitchPreference(pref: Preference, value: Boolean) {
pref.asInstanceOf[SwitchPreference].setChecked(value)
......@@ -57,6 +59,9 @@ object ShadowsocksSettings {
case Key.udpdns => updateSwitchPreference(pref, profile.udpdns)
case Key.auth => updateSwitchPreference(pref, profile.auth)
case Key.ipv6 => updateSwitchPreference(pref, profile.ipv6)
case Key.kcp => updateSwitchPreference(pref, profile.kcp)
case Key.kcpPort => updateNumberPickerPreference(pref, profile.kcpPort)
case Key.kcpcli => updateSummaryEditTextPreference(pref, profile.kcpcli)
}
}
}
......@@ -103,6 +108,19 @@ class ShadowsocksSettings extends PreferenceFragment with OnSharedPreferenceChan
app.profileManager.updateProfile(profile)
})
findPreference(Key.kcp).setOnPreferenceChangeListener((_, value) => {
profile.kcp = value.asInstanceOf[Boolean]
app.profileManager.updateProfile(profile)
})
findPreference(Key.kcpPort).setOnPreferenceChangeListener((_, value) => {
profile.kcpPort = value.asInstanceOf[Int]
app.profileManager.updateProfile(profile)
})
findPreference(Key.kcpcli).setOnPreferenceChangeListener((_, value) => {
profile.kcpcli = value.asInstanceOf[String]
app.profileManager.updateProfile(profile)
})
isProxyApps = findPreference(Key.proxyApps).asInstanceOf[SwitchPreference]
isProxyApps.setOnPreferenceClickListener(_ => {
startActivity(new Intent(activity, classOf[AppManager]))
......
......@@ -109,6 +109,10 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
def killProcesses() {
if (kcptunProcess != null) {
kcptunProcess.destroy()
kcptunProcess = null
}
if (sslocalProcess != null) {
sslocalProcess.destroy()
sslocalProcess = null
......@@ -177,18 +181,65 @@ class ShadowsocksVpnService extends VpnService with BaseService {
/** Called when the activity is first created. */
def handleConnection: Boolean = {
val fd = startVpn()
if (!sendFd(fd)) return false
if (profile.kcp) {
startKcptunDaemon()
}
startShadowsocksDaemon()
if (profile.udpdns && profile.kcp) {
startShadowsocksUDPDaemon()
}
if (!profile.udpdns) {
startDnsDaemon()
startDnsTunnel()
}
val fd = startVpn()
sendFd(fd)
true
}
def startShadowsocksDaemon() {
def startKcptunDaemon() {
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/kcptun"
, "-r", profile.host + ":" + profile.kcpPort
, "-l", "127.0.0.1:" + (profile.localPort + 90)
, "--path", getApplicationInfo.dataDir + "/protect_path"
, profile.kcpcli)
if (BuildConfig.DEBUG)
Log.d(TAG, cmd.mkString(" "))
kcptunProcess = new GuardedProcess(cmd.mkString(" ").split(" ").toSeq).start()
}
def startShadowsocksUDPDaemon() {
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort,
profile.password, profile.method, 600)
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-udp-vpn.conf"))(p => {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/ss-local", "-V", "-U"
, "-b", "127.0.0.1"
, "-t", "600"
, "-P", getApplicationInfo.dataDir
, "-c", getApplicationInfo.dataDir + "/ss-local-udp-vpn.conf")
if (profile.auth) cmd += "-A"
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
sstunnelProcess = new GuardedProcess(cmd).start()
}
def startShadowsocksDaemon() {
if (profile.route != Route.ALL) {
val acl: Array[Array[String]] = profile.route match {
case Route.BYPASS_LAN => Array(getResources.getStringArray(R.array.private_route))
......@@ -201,15 +252,21 @@ class ShadowsocksVpnService extends VpnService with BaseService {
})
}
val conf = ConfigUtils
val conf = if (profile.kcp) {
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort,
profile.password, profile.method, 600)
} else {
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort,
profile.password, profile.method, 600)
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-local-vpn.conf"))(p => {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/ss-local", "-V", "-u"
cmd += (getApplicationInfo.dataDir + "/ss-local", "-V"
, "-b", "127.0.0.1"
, "-t", "600"
, "-P", getApplicationInfo.dataDir
......@@ -217,6 +274,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.auth) cmd += "-A"
if (profile.udpdns && !profile.kcp) cmd += "-u"
if (profile.route != Route.ALL) {
cmd += "--acl"
cmd += (getApplicationInfo.dataDir + "/acl.list")
......@@ -228,19 +287,23 @@ class ShadowsocksVpnService extends VpnService with BaseService {
}
def startDnsTunnel() = {
val conf = ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, 8163,
val conf = if (profile.kcp) {
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, "127.0.0.1", profile.localPort + 90, profile.localPort + 63,
profile.password, profile.method, 10)
} else {
ConfigUtils
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, profile.host, profile.remotePort, profile.localPort + 63,
profile.password, profile.method, 10)
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf"))(p => {
p.println(conf)
})
val cmd = new ArrayBuffer[String]
cmd += (getApplicationInfo.dataDir + "/ss-tunnel"
, "-V"
, "-u"
, "-t", "10"
, "-b", "127.0.0.1"
, "-l", "8163"
, "-L", "8.8.8.8:53"
, "-P", getApplicationInfo.dataDir
, "-c", getApplicationInfo.dataDir + "/ss-tunnel-vpn.conf")
......@@ -257,10 +320,10 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val conf = {
if (profile.route == Route.BYPASS_CHN || profile.route == Route.BYPASS_LAN_CHN) {
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"0.0.0.0", 8153, getBlackList, 8163, ipv6)
"0.0.0.0", profile.localPort + 53, getBlackList, profile.localPort + 63, ipv6)
} else {
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, getApplicationInfo.dataDir,
"0.0.0.0", 8153, 8163, ipv6)
"0.0.0.0", profile.localPort + 53, profile.localPort + 63, ipv6)
}
}
Utils.printToFile(new File(getApplicationInfo.dataDir + "/pdnsd-vpn.conf"))(p => {
......@@ -354,7 +417,8 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.udpdns)
cmd += " --enable-udprelay"
else
cmd += " --dnsgw %s:8153".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"))
cmd += " --dnsgw %s:%d".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"),
profile.localPort + 53)
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
......
......@@ -64,7 +64,7 @@ object DBHelper {
}
class DBHelper(val context: Context)
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 16) {
extends OrmLiteSqliteOpenHelper(context, DBHelper.PROFILE, null, 18) {
import DBHelper._
lazy val profileDao: Dao[Profile, Int] = getDao(classOf[Profile])
......@@ -125,6 +125,15 @@ class DBHelper(val context: Context)
if (oldVersion < 16) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'bypass-lan-china' WHERE route = 'bypass-china'")
}
if (oldVersion < 17) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcp SMALLINT;")
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpcli VARCHAR;")
}
if (oldVersion < 18) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN kcpPort INTEGER;")
}
}
}
}
......@@ -99,6 +99,15 @@ class Profile {
@DatabaseField
var userOrder: Long = _
@DatabaseField
var kcp: Boolean = false
@DatabaseField
var kcpPort: Int = 8399
@DatabaseField
var kcpcli: String = ""
override def toString = "ss://" + Base64.encodeToString("%s%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
method, if (auth) "-auth" else "", password, host, remotePort).getBytes, Base64.NO_PADDING | Base64.NO_WRAP)
......
......@@ -45,6 +45,7 @@ object Executable {
val SS_LOCAL = "ss-local"
val SS_TUNNEL = "ss-tunnel"
val TUN2SOCKS = "tun2socks"
val KCPTUN = "kcptun"
}
object ConfigUtils {
......@@ -166,6 +167,10 @@ object Key {
val localPort = "localPortNum"
val profileTip = "profileTip"
val kcp = "kcp"
val kcpPort = "kcpPort"
val kcpcli = "kcpcli"
}
object State {
......
#!/bin/bash
export ARCH=`uname -m`
export GOROOT_BOOTSTRAP=$HOME/.android/go
export ANDROID_NDK_HOME=$HOME/.android/android-ndk-r12b
export ANDROID_HOME=$HOME/.android/android-sdk-linux
export PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
......@@ -21,5 +22,13 @@ if [ ! -d "$ANDROID_NDK_HOME" ]; then
popd
fi
if [ ! -d "$GOROOT_BOOTSTRAP" ]; then
mkdir -p $GOROOT_BOOTSTRAP
pushd $HOME/.android
wget https://storage.googleapis.com/golang/go1.6.3.linux-amd64.tar.gz
tar xf go1.6.3.linux-amd64.tar.gz
popd
fi
( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk --filter tools,platform-tools,build-tools-24.0.0,android-23,android-24,extra-google-m2repository --no-ui -a
( sleep 5 && while [ 1 ]; do sleep 1; echo y; done ) | android update sdk --filter extra-android-m2repository --no-ui -a
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