diff --git a/.gitignore b/.gitignore
index ecec4bea..87b1bdf9 100644
--- a/.gitignore
+++ b/.gitignore
@@ -9,3 +9,4 @@ local.sbt
mobile/src/main/libs
mobile/src/main/obj
+mobile/src/main/jni/overture
diff --git a/.gitmodules b/.gitmodules
index c14024db..8dafbfc2 100644
--- a/.gitmodules
+++ b/.gitmodules
@@ -17,10 +17,6 @@
path = mobile/src/main/jni/redsocks
url = https://github.com/shadowsocks/redsocks.git
branch = shadowsocks-android
-[submodule "mobile/src/main/jni/pdnsd"]
- path = mobile/src/main/jni/pdnsd
- url = https://github.com/shadowsocks/pdnsd.git
- branch = shadowsocks-android
[submodule "mobile/src/main/jni/mbedtls"]
path = mobile/src/main/jni/mbedtls
url = https://github.com/ARMmbed/mbedtls
@@ -37,3 +33,6 @@
[submodule "mobile/src/main/jni/libudns"]
path = mobile/src/main/jni/libudns
url = https://github.com/shadowsocks/libudns.git
+[submodule "mobile/src/overture"]
+ path = mobile/src/overture
+ url = https://github.com/shadowsocks/overture-android.git
diff --git a/.travis.yml b/.travis.yml
index 62338c27..a9a15ed4 100644
--- a/.travis.yml
+++ b/.travis.yml
@@ -5,8 +5,9 @@ jdk:
env:
global:
- - NDK_VERSION=r12b
+ - NDK_VERSION=r14
- NDK_CCACHE=ccache
+ - GOROOT_BOOTSTRAP=$GOROOT
- ANDROID_NDK_HOME=$HOME/.android/android-ndk-${NDK_VERSION}
- SBTPATH=$HOME/.sbt
- PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
@@ -49,4 +50,4 @@ install:
fi
script:
- - $SBTPATH/sbt android:package
+ - $SBTPATH/sbt go-build android:package
diff --git a/README.md b/README.md
index e2a825c3..a873cccd 100644
--- a/README.md
+++ b/README.md
@@ -12,15 +12,17 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
* JDK 1.8
* SBT 0.13.0+
+* Go 1.4+
* Android SDK
- Build Tools 25+
- Android Support Repository and Google Repository (see `build.sbt` for version)
-* Android NDK r12b+
+ - Android NDK r14+
### BUILD
* Set environment variable `ANDROID_HOME` to `/path/to/android-sdk`
* (optional) Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk` (default: `$ANDROID_HOME/ndk-bundle`)
+* 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
* Create `mobile/local.properties` from `mobile/local.properties.example` with your own key information
* Invoke the building like this
@@ -29,7 +31,7 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Scala.
git submodule update --init --recursive
# Build the App
- sbt clean android:package-release
+ sbt clean go-build android:package-release
```
### TRANSLATE
@@ -50,7 +52,7 @@ Translators can go to [POEditor](https://poeditor.com/join/project/u5VHO9vhSf) t
pcre: BSD
libancillary: BSD
shadowsocks-libev: GPLv3
- pdnsd: GPLv3
+ overture: MIT
libev: GPLv2
libsodium: ISC
libudns: LGPL
diff --git a/build.sbt b/build.sbt
index bd13b300..b427f9ae 100644
--- a/build.sbt
+++ b/build.sbt
@@ -18,7 +18,7 @@ lazy val commonSettings = Seq(
typedResources := false
)
-val supportLibsVersion = "25.1.1"
+val supportLibsVersion = "25.2.0"
lazy val root = Project(id = "shadowsocks-android", base = file("."))
.settings(commonSettings)
.aggregate(plugin, mobile)
diff --git a/mobile/build.sbt b/mobile/build.sbt
index c52a430b..392f5ea6 100644
--- a/mobile/build.sbt
+++ b/mobile/build.sbt
@@ -2,8 +2,8 @@ enablePlugins(AndroidApp)
android.useSupportVectors
name := "shadowsocks"
-version := "4.0.4"
-versionCode := Some(179)
+version := "4.1.3"
+versionCode := Some(183)
proguardOptions ++=
"-keep class com.github.shadowsocks.JniHelper { *; }" ::
@@ -19,7 +19,7 @@ val playServicesVersion = "10.2.0"
resolvers += Resolver.jcenterRepo
libraryDependencies ++=
"com.futuremind.recyclerfastscroll" % "fastscroll" % "0.2.5" ::
- "com.evernote" % "android-job" % "1.1.6" ::
+ "com.evernote" % "android-job" % "1.1.7" ::
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" ::
"com.google.android.gms" % "play-services-ads" % playServicesVersion ::
"com.google.android.gms" % "play-services-analytics" % playServicesVersion ::
@@ -28,7 +28,7 @@ libraryDependencies ++=
"com.mikepenz" % "crossfader" % "1.5.0" ::
"com.mikepenz" % "fastadapter" % "2.1.7" ::
"com.mikepenz" % "iconics-core" % "2.8.2" ::
- "com.mikepenz" % "materialdrawer" % "5.8.1" ::
+ "com.mikepenz" % "materialdrawer" % "5.8.2" ::
"com.mikepenz" % "materialize" % "1.0.1" ::
"com.squareup.okhttp3" % "okhttp" % "3.6.0" ::
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
@@ -36,3 +36,11 @@ libraryDependencies ++=
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" ::
"net.glxn.qrgen" % "android" % "2.0" ::
Nil
+
+lazy val goBuild = TaskKey[Unit]("go-build", "Build go and kcptun")
+goBuild := {
+ Process(Seq("mobile/src/overture/make.bash", minSdkVersion.value)) ! streams.value.log match {
+ case 0 => // Success!
+ case n => sys.error(s"Native build script exit code: $n")
+ }
+}
diff --git a/mobile/src/main/AndroidManifest.xml b/mobile/src/main/AndroidManifest.xml
index 0f77f4df..ff448140 100644
--- a/mobile/src/main/AndroidManifest.xml
+++ b/mobile/src/main/AndroidManifest.xml
@@ -144,6 +144,8 @@
android:process=":bg"/>
+
http
pcre: BSD
libancillary: BSD
shadowsocks-libev: GPLv3
- pdnsd: GPLv3
+ overture: MIT
libev: GPLv2
libsodium: ISC
libudns: LGPL
diff --git a/mobile/src/main/java/com/github/shadowsocks/JniHelper.java b/mobile/src/main/java/com/github/shadowsocks/JniHelper.java
index dfce9080..c2299e58 100755
--- a/mobile/src/main/java/com/github/shadowsocks/JniHelper.java
+++ b/mobile/src/main/java/com/github/shadowsocks/JniHelper.java
@@ -54,7 +54,19 @@ public class JniHelper {
? new ErrnoException("kill", errno) : new Exception("kill failed: " + errno);
}
+ @Deprecated // only implemented for before API 24
+ public static boolean waitForCompat(Process process, long millis) throws Exception {
+ if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
+ final Object mutex = getExitValueMutex(process);
+ synchronized (mutex) {
+ if (getExitValue(process) == null) mutex.wait(millis);
+ return getExitValue(process) != null;
+ }
+ }
+
private static native int sigterm(Process process);
+ private static native Integer getExitValue(Process process);
+ private static native Object getExitValueMutex(Process process);
public static native int sendFd(int fd, String path);
public static native void close(int fd);
}
diff --git a/mobile/src/main/jni/Android.mk b/mobile/src/main/jni/Android.mk
index 1d448261..518dc0d2 100755
--- a/mobile/src/main/jni/Android.mk
+++ b/mobile/src/main/jni/Android.mk
@@ -97,7 +97,7 @@ LIBEVENT_SOURCES := \
LOCAL_MODULE := event
LOCAL_SRC_FILES := $(addprefix libevent/, $(LIBEVENT_SOURCES))
-LOCAL_CFLAGS := -O2 -I$(LOCAL_PATH)/libevent \
+LOCAL_CFLAGS := -O2 -D_EVENT_HAVE_ARC4RANDOM -I$(LOCAL_PATH)/libevent \
-I$(LOCAL_PATH)/libevent/include \
include $(BUILD_STATIC_LIBRARY)
@@ -239,23 +239,6 @@ LOCAL_CFLAGS := -O2 -std=gnu99 -DUSE_IPTABLES \
include $(BUILD_SHARED_EXECUTABLE)
-########################################################
-## pdnsd
-########################################################
-
-include $(CLEAR_VARS)
-
-PDNSD_SOURCES := $(wildcard $(LOCAL_PATH)/pdnsd/src/*.c)
-
-LOCAL_MODULE := pdnsd
-LOCAL_SRC_FILES := $(PDNSD_SOURCES:$(LOCAL_PATH)/%=%)
-LOCAL_CFLAGS := -DANDROID -Wall -O2 -I$(LOCAL_PATH)/pdnsd \
- -I$(LOCAL_PATH)/include/pdnsd -I$(LOCAL_PATH)/libancillary
-LOCAL_STATIC_LIBRARIES := libancillary
-LOCAL_LDLIBS := -llog
-
-include $(BUILD_SHARED_EXECUTABLE)
-
########################################################
## shadowsocks-libev local
########################################################
@@ -494,6 +477,17 @@ LOCAL_SRC_FILES := $(addprefix pcre/, $(libpcre_src_files))
include $(BUILD_STATIC_LIBRARY)
+########################################################
+## overture
+########################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := overture
+LOCAL_SRC_FILES := overture/$(TARGET_ARCH_ABI)/liboverture.so
+
+include $(PREBUILT_SHARED_LIBRARY)
+
# Import cpufeatures
$(call import-module,android/cpufeatures)
diff --git a/mobile/src/main/jni/Application.mk b/mobile/src/main/jni/Application.mk
index e32dd558..e38747eb 100644
--- a/mobile/src/main/jni/Application.mk
+++ b/mobile/src/main/jni/Application.mk
@@ -1,4 +1,4 @@
-APP_ABI := armeabi-v7a x86
+APP_ABI := armeabi-v7a arm64-v8a x86
APP_PLATFORM := android-19
APP_STL := stlport_static
NDK_TOOLCHAIN_VERSION := clang
diff --git a/mobile/src/main/jni/jni-helper.cpp b/mobile/src/main/jni/jni-helper.cpp
index cbba6dbc..60bac6b1 100644
--- a/mobile/src/main/jni/jni-helper.cpp
+++ b/mobile/src/main/jni/jni-helper.cpp
@@ -22,7 +22,7 @@
#define THROW(env, clazz, msg) do { env->ThrowNew(env->FindClass(clazz), msg); } while (0)
static jclass ProcessImpl;
-static jfieldID ProcessImpl_pid;
+static jfieldID ProcessImpl_pid, ProcessImpl_exitValue, ProcessImpl_exitValueMutex;
static int sdk_version() {
char version[PROP_VALUE_MAX + 1];
@@ -41,6 +41,24 @@ jint Java_com_github_shadowsocks_jnihelper_sigterm(JNIEnv *env, jobject thiz, jo
return kill(pid, SIGTERM) == -1 && errno != ESRCH ? errno : 0;
}
+jobject Java_com_github_shadowsocks_jnihelper_getExitValue(JNIEnv *env, jobject thiz, jobject process) {
+ if (!env->IsInstanceOf(process, ProcessImpl)) {
+ THROW(env, "java/lang/ClassCastException",
+ "Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
+ return NULL;
+ }
+ return env->GetObjectField(process, ProcessImpl_exitValue);
+}
+
+jobject Java_com_github_shadowsocks_jnihelper_getExitValueMutex(JNIEnv *env, jobject thiz, jobject process) {
+ if (!env->IsInstanceOf(process, ProcessImpl)) {
+ THROW(env, "java/lang/ClassCastException",
+ "Unsupported process object. Only java.lang.ProcessManager$ProcessImpl is accepted.");
+ return NULL;
+ }
+ return env->GetObjectField(process, ProcessImpl_exitValueMutex);
+}
+
void Java_com_github_shadowsocks_jnihelper_close(JNIEnv *env, jobject thiz, jint fd) {
close(fd);
}
@@ -84,11 +102,13 @@ static JNINativeMethod method_table[] = {
{ "sendFd", "(ILjava/lang/String;)I",
(void*) Java_com_github_shadowsocks_jnihelper_sendfd },
{ "sigterm", "(Ljava/lang/Process;)I",
- (void*) Java_com_github_shadowsocks_jnihelper_sigterm }
+ (void*) Java_com_github_shadowsocks_jnihelper_sigterm },
+ { "getExitValue", "(Ljava/lang/Process;)Ljava/lang/Integer;",
+ (void*) Java_com_github_shadowsocks_jnihelper_getExitValue },
+ { "getExitValueMutex", "(Ljava/lang/Process;)Ljava/lang/Object;",
+ (void*) Java_com_github_shadowsocks_jnihelper_getExitValueMutex }
};
-
-
/*
* Register several native methods for one class.
*/
@@ -161,6 +181,14 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved) {
THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl.pid not found");
goto bail;
}
+ if (!(ProcessImpl_exitValue = env->GetFieldID(ProcessImpl, "exitValue", "Ljava/lang/Integer;"))) {
+ THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl.exitValue not found");
+ goto bail;
+ }
+ if (!(ProcessImpl_exitValueMutex = env->GetFieldID(ProcessImpl, "exitValueMutex", "Ljava/lang/Object;"))) {
+ THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl.exitValueMutex not found");
+ goto bail;
+ }
}
result = JNI_VERSION_1_6;
diff --git a/mobile/src/main/jni/pdnsd b/mobile/src/main/jni/pdnsd
deleted file mode 160000
index f6b0a56c..00000000
--- a/mobile/src/main/jni/pdnsd
+++ /dev/null
@@ -1 +0,0 @@
-Subproject commit f6b0a56c74ff33e3df4c00859bc7ccefa1354fa4
diff --git a/mobile/src/main/jni/shadowsocks-libev b/mobile/src/main/jni/shadowsocks-libev
index 857bb8de..de0cebb6 160000
--- a/mobile/src/main/jni/shadowsocks-libev
+++ b/mobile/src/main/jni/shadowsocks-libev
@@ -1 +1 @@
-Subproject commit 857bb8def744a1c158056d03194cf1ee78184985
+Subproject commit de0cebb62ef3b7854972f65834af7bb8aceb4bcc
diff --git a/mobile/src/main/res/values-ja/strings.xml b/mobile/src/main/res/values-ja/strings.xml
index 010d53a3..d79d4ca8 100644
--- a/mobile/src/main/res/values-ja/strings.xml
+++ b/mobile/src/main/res/values-ja/strings.xml
@@ -44,8 +44,8 @@
"システム起動時にバックグラウンドで本サービスを開始"
"使用するにはROOT権限が必要"
"ご利用のカーネルバージョンはサポートしておりません:%s < 3.7.1"
-"UDP転送"
-"UDPプロトコルでリモードサーバーにパケットを転送"
+"DNS転送"
+"DNSプロトコルでリモードサーバーにパケットを転送"
"バックグラウンドでサービスを開始しました"
@@ -138,4 +138,4 @@
"選択したアプリにプロキシを設定する"
"サブネット/ホストネームPCREパターン"
"ドメイン及びサブドメイン"
-
\ No newline at end of file
+
diff --git a/mobile/src/main/res/values-ru/strings.xml b/mobile/src/main/res/values-ru/strings.xml
index 3aa80de3..79e13bcf 100644
--- a/mobile/src/main/res/values-ru/strings.xml
+++ b/mobile/src/main/res/values-ru/strings.xml
@@ -52,8 +52,8 @@
"Запускать Shadowsocks при включении"
"Переключение требует наличия ROOT прав"
"Неподдерживаемая версия ядра: %s < 3.7.1"
-"Проброс UDP"
-"Пробрасывать UDP пакеты на удалённый сервер"
+"Проброс DNS"
+"Пробрасывать DNS пакеты на удалённый сервер"
"Shadowsocks запущен."
@@ -121,4 +121,4 @@
"Обход LAN и Китай"
"Список Китай"
"Установить прокси для выбранных приложений"
-
\ No newline at end of file
+
diff --git a/mobile/src/main/res/values-zh-rCN/strings.xml b/mobile/src/main/res/values-zh-rCN/strings.xml
index 40fe7a52..8979c4fe 100644
--- a/mobile/src/main/res/values-zh-rCN/strings.xml
+++ b/mobile/src/main/res/values-zh-rCN/strings.xml
@@ -45,8 +45,8 @@
"随系统启动后台服务"
"切换需要 ROOT 权限"
"不支持的内核版本:%s < 3.7.1"
-"UDP 转发"
-"由远程服务器转发 UDP 协议的数据包"
+"DNS 转发"
+"由远程服务器转发 DNS 协议的数据包"
"后台服务已开始运行。"
@@ -140,4 +140,4 @@
"为应用程序分别设置代理"
"子网/域名 PCRE 正则表达式"
"域名及其子域名"
-
\ No newline at end of file
+
diff --git a/mobile/src/main/res/values-zh-rTW/strings.xml b/mobile/src/main/res/values-zh-rTW/strings.xml
index caca7aca..7968b2df 100644
--- a/mobile/src/main/res/values-zh-rTW/strings.xml
+++ b/mobile/src/main/res/values-zh-rTW/strings.xml
@@ -44,8 +44,8 @@
"在裝置啟動時啟用 Shadowsocks"
"切換需要 ROOT 權限"
"不支援的核心版本:%s < 3.7.1"
-"UDP 轉送"
-"向遠端轉送 UDP 封包"
+"DNS 轉送"
+"向遠端轉送 DNS 封包"
"Shadowsocks 已啟動。"
@@ -139,4 +139,4 @@
"為已選擇的應用程式設定 Proxy"
"子網路/主機名稱 PCRE 模式"
"網域及其所有子網域"
-
\ No newline at end of file
+
diff --git a/mobile/src/main/res/values/strings.xml b/mobile/src/main/res/values/strings.xml
index 17d2f7fe..23ed5dc6 100644
--- a/mobile/src/main/res/values/strings.xml
+++ b/mobile/src/main/res/values/strings.xml
@@ -53,8 +53,8 @@
Enable Shadowsocks on startup
Toggling requires ROOT permission
Unsupported kernel version: %s < 3.7.1
- UDP Forwarding
- Forward UDP packets to remote
+ DNS Forwarding
+ Force to forward DNS packets to remote
Shadowsocks started.
diff --git a/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala b/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala
index 5d5ec16d..f5e6fdb1 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/BaseService.scala
@@ -39,11 +39,11 @@ import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.plugin.{PluginConfiguration, PluginManager, PluginOptions}
import com.github.shadowsocks.utils._
import okhttp3.{Dns, FormBody, OkHttpClient, Request}
-import org.json.JSONObject
+import org.json.{JSONArray, JSONObject}
+import scala.collection.JavaConversions._
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
-import scala.collection.JavaConversions._
import scala.util.Random
trait BaseService extends Service {
@@ -328,4 +328,44 @@ trait BaseService extends Service {
IOUtils.writeString(new File(getFilesDir, file), config.toString)
file
}
+
+ protected final def buildOvertureConfig(file: String): String = {
+ val config = new JSONObject()
+ .put("BindAddress", ":" + (profile.localPort + 53))
+ .put("RedirectIPv6Record", true)
+ .put("DomainBase64Decode", true)
+ .put("HostsFile", "hosts")
+ .put("MinimumTTL", 3600)
+ .put("CacheSize", 4096)
+ def makeDns(name: String, address: String, edns: Boolean = true) = {
+ val dns = new JSONObject()
+ .put("Name", name)
+ .put("Address", address + ":53")
+ .put("Timeout", 6)
+ .put("EDNSClientSubnet", new JSONObject().put("Policy", "disable"))
+ if (edns) dns
+ .put("Protocol", "tcp")
+ .put("Socks5Address", "127.0.0.1:" + profile.localPort)
+ else dns.put("Protocol", "udp")
+ dns
+ }
+ profile.route match {
+ case Acl.BYPASS_CHN | Acl.BYPASS_LAN_CHN | Acl.GFWLIST | Acl.CUSTOM_RULES => config
+ .put("PrimaryDNS", new JSONArray(Array(
+ makeDns("Primary-1", "119.29.29.29", edns = false),
+ makeDns("Primary-2", "114.114.114.114", edns = false)
+ )))
+ .put("AlternativeDNS", new JSONArray().put(makeDns("Alternative", profile.remoteDns)))
+ .put("IPNetworkFile", "china_ip_list.txt")
+ .put("DomainFile", "gfwlist.txt")
+ case Acl.CHINALIST => config
+ .put("PrimaryDNS", new JSONArray().put(makeDns("Primary", "119.29.29.29")))
+ .put("AlternativeDNS", new JSONArray().put(makeDns("Alternative", profile.remoteDns)))
+ case _ => config
+ .put("PrimaryDNS", new JSONArray().put(makeDns("Primary", profile.remoteDns)))
+ .put("AlternativeDNS", new JSONArray().put(makeDns("Alternative", "208.67.222.222")))
+ }
+ IOUtils.writeString(new File(getFilesDir, file), config.toString)
+ file
+ }
}
diff --git a/mobile/src/main/scala/com/github/shadowsocks/GuardedProcess.scala b/mobile/src/main/scala/com/github/shadowsocks/GuardedProcess.scala
index 7ff41694..bdd6cd7c 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/GuardedProcess.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/GuardedProcess.scala
@@ -115,7 +115,7 @@ class GuardedProcess(cmd: String*) {
private def destroyProcess() {
if (Build.VERSION.SDK_INT < 24) {
JniHelper.sigtermCompat(process)
- process.waitFor()
+ JniHelper.waitForCompat(process, 500)
}
process.destroy()
}
diff --git a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala
index 5d79a7c6..4b533e85 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala
@@ -197,15 +197,17 @@ class ShadowsocksApplication extends Application {
def copyAssets() {
val assetManager = getAssets
- var files: Array[String] = null
- try files = assetManager.list("acl") catch {
- case e: IOException =>
- Log.e(TAG, e.getMessage)
- app.track(e)
+ for (dir <- List("acl", "overture")) {
+ var files: Array[String] = null
+ try files = assetManager.list(dir) catch {
+ case e: IOException =>
+ Log.e(TAG, e.getMessage)
+ app.track(e)
+ }
+ if (files != null) for (file <- files) autoClose(assetManager.open(dir + "/" + file))(in =>
+ autoClose(new FileOutputStream(new File(getFilesDir, file)))(out =>
+ IOUtils.copy(in, out)))
}
- if (files != null) for (file <- files) autoClose(assetManager.open("acl/" + file))(in =>
- autoClose(new FileOutputStream(new File(getFilesDir, file)))(out =>
- IOUtils.copy(in, out)))
editor.putInt(Key.currentVersionCode, BuildConfig.VERSION_CODE).apply()
}
diff --git a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
index 4a4204b9..3ba848a5 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksNatService.scala
@@ -49,7 +49,7 @@ class ShadowsocksNatService extends BaseService {
var sslocalProcess: GuardedProcess = _
var sstunnelProcess: GuardedProcess = _
var redsocksProcess: GuardedProcess = _
- var pdnsdProcess: GuardedProcess = _
+ var overtureProcess: GuardedProcess = _
var su: Shell.Interactive = _
def startShadowsocksDaemon() {
@@ -83,21 +83,8 @@ class ShadowsocksNatService extends BaseService {
}
def startDnsDaemon() {
- val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0"
- IOUtils.writeString(new File(getFilesDir, "pdnsd-nat.conf"), profile.route match {
- case Acl.BYPASS_CHN | Acl.BYPASS_LAN_CHN | Acl.GFWLIST | Acl.CUSTOM_RULES =>
- ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getCacheDir.getAbsolutePath,
- "127.0.0.1", profile.localPort + 53, "114.114.114.114, 223.5.5.5, 1.2.4.8",
- getBlackList, reject, profile.localPort + 63, reject)
- case Acl.CHINALIST =>
- ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getCacheDir.getAbsolutePath,
- "127.0.0.1", profile.localPort + 53, "8.8.8.8, 8.8.4.4, 208.67.222.222",
- "", reject, profile.localPort + 63, reject)
- case _ =>
- ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "", getCacheDir.getAbsolutePath,
- "127.0.0.1", profile.localPort + 53, profile.localPort + 63, reject)
- })
- pdnsdProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/libpdnsd.so", "-c", "pdnsd-nat.conf")
+ overtureProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/liboverture.so",
+ "-c", buildOvertureConfig("overture-nat.conf"))
.start()
}
@@ -111,11 +98,13 @@ class ShadowsocksNatService extends BaseService {
/** Called when the activity is first created. */
def handleConnection() {
- startDNSTunnel()
startRedsocksDaemon()
startShadowsocksDaemon()
- if (!profile.udpdns) startDnsDaemon()
+ if (!profile.udpdns)
+ startDnsDaemon()
+ else
+ startDNSTunnel()
setupIptables()
@@ -143,9 +132,9 @@ class ShadowsocksNatService extends BaseService {
redsocksProcess.destroy()
redsocksProcess = null
}
- if (pdnsdProcess != null) {
- pdnsdProcess.destroy()
- pdnsdProcess = null
+ if (overtureProcess != null) {
+ overtureProcess.destroy()
+ overtureProcess = null
}
su.addCommand("iptables -t nat -F OUTPUT")
diff --git a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
index 220d8333..1efe035f 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/ShadowsocksVpnService.scala
@@ -45,8 +45,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
private var notification: ShadowsocksNotification = _
var sslocalProcess: GuardedProcess = _
- var sstunnelProcess: GuardedProcess = _
- var pdnsdProcess: GuardedProcess = _
+ var overtureProcess: GuardedProcess = _
var tun2socksProcess: GuardedProcess = _
override def onBind(intent: Intent): IBinder = {
@@ -94,17 +93,13 @@ class ShadowsocksVpnService extends VpnService with BaseService {
sslocalProcess.destroy()
sslocalProcess = null
}
- if (sstunnelProcess != null) {
- sstunnelProcess.destroy()
- sstunnelProcess = null
- }
if (tun2socksProcess != null) {
tun2socksProcess.destroy()
tun2socksProcess = null
}
- if (pdnsdProcess != null) {
- pdnsdProcess.destroy()
- pdnsdProcess = null
+ if (overtureProcess != null) {
+ overtureProcess.destroy()
+ overtureProcess = null
}
}
@@ -149,15 +144,14 @@ class ShadowsocksVpnService extends VpnService with BaseService {
/** Called when the activity is first created. */
def handleConnection() {
- val fd = startVpn()
- if (!sendFd(fd)) throw new Exception("sendFd failed")
-
startShadowsocksDaemon()
if (!profile.udpdns) {
startDnsDaemon()
- startDnsTunnel()
}
+
+ val fd = startVpn()
+ if (!sendFd(fd)) throw new Exception("sendFd failed")
}
override protected def buildPluginCommandLine(): ArrayBuffer[String] = super.buildPluginCommandLine() += "-V"
@@ -165,13 +159,12 @@ class ShadowsocksVpnService extends VpnService with BaseService {
def startShadowsocksDaemon() {
val cmd = ArrayBuffer[String](getApplicationInfo.nativeLibraryDir + "/libss-local.so",
"-V",
+ "-u",
"-b", "127.0.0.1",
"-l", profile.localPort.toString,
"-t", "600",
"-c", buildShadowsocksConfig("ss-local-vpn.conf"))
- if (profile.udpdns) cmd += "-u"
-
if (profile.route != Acl.ALL) {
cmd += "--acl"
cmd += Acl.getFile(profile.route).getAbsolutePath
@@ -182,33 +175,10 @@ class ShadowsocksVpnService extends VpnService with BaseService {
sslocalProcess = new GuardedProcess(cmd: _*).start()
}
- def startDnsTunnel(): Unit =
- sstunnelProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/libss-tunnel.so",
- "-V",
- "-t", "10",
- "-b", "127.0.0.1",
- "-l", (profile.localPort + 63).toString,
- "-L" , profile.remoteDns.trim + ":53",
- "-c", buildShadowsocksConfig("ss-tunnel-vpn.conf")).start()
-
def startDnsDaemon() {
- val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0"
- IOUtils.writeString(new File(getFilesDir, "pdnsd-vpn.conf"), profile.route match {
- case Acl.BYPASS_CHN | Acl.BYPASS_LAN_CHN | Acl.GFWLIST | Acl.CUSTOM_RULES =>
- ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "protect = \"protect_path\";", getCacheDir.getAbsolutePath,
- "0.0.0.0", profile.localPort + 53, "114.114.114.114, 223.5.5.5, 1.2.4.8",
- getBlackList, reject, profile.localPort + 63, reject)
- case Acl.CHINALIST =>
- ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "protect = \"protect_path\";", getCacheDir.getAbsolutePath,
- "0.0.0.0", profile.localPort + 53, "8.8.8.8, 8.8.4.4, 208.67.222.222",
- "", reject, profile.localPort + 63, reject)
- case _ =>
- ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "protect = \"protect_path\";", getCacheDir.getAbsolutePath,
- "0.0.0.0", profile.localPort + 53, profile.localPort + 63, reject)
- })
- val cmd = Array(getApplicationInfo.nativeLibraryDir + "/libpdnsd.so", "-c", "pdnsd-vpn.conf")
-
- pdnsdProcess = new GuardedProcess(cmd: _*).start()
+ overtureProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/liboverture.so",
+ "-c", buildOvertureConfig("overture-vpn.conf"), "-V")
+ .start()
}
def startVpn(): Int = {
@@ -271,9 +241,9 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.ipv6)
cmd += ("--netif-ip6addr", PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "2"))
- if (profile.udpdns)
- cmd += "--enable-udprelay"
- else
+ cmd += "--enable-udprelay"
+
+ if (!profile.udpdns)
cmd += ("--dnsgw", "%s:%d".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"),
profile.localPort + 53))
diff --git a/mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala b/mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala
index 53581e85..fa347065 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala
@@ -98,7 +98,8 @@ class Profile {
.scheme("ss")
.encodedAuthority("%s@%s:%d".formatLocal(Locale.ENGLISH,
Base64.encodeToString("%s:%s".formatLocal(Locale.ENGLISH, method, password).getBytes,
- Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE), host, remotePort))
+ Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE),
+ if (host.contains(':')) '[' + host + ']' else host, remotePort))
val configuration = new PluginConfiguration(plugin)
if (configuration.selected.nonEmpty)
builder.appendQueryParameter(Key.plugin, configuration.selectedOptions.toString(false))
diff --git a/mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala b/mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala
index d1d9fe21..80bd0f4a 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala
@@ -45,86 +45,6 @@ object ConfigUtils {
" port = %d;\n" +
" type = socks5;\n" +
"}\n"
-
- val PDNSD_LOCAL =
- """
- |global {
- | perm_cache = 2048;
- | %s
- | cache_dir = "%s";
- | server_ip = %s;
- | server_port = %d;
- | query_method = tcp_only;
- | min_ttl = 15m;
- | max_ttl = 1w;
- | timeout = 10;
- | daemon = off;
- |}
- |
- |server {
- | label = "local";
- | ip = 127.0.0.1;
- | port = %d;
- | reject = %s;
- | reject_policy = negate;
- | reject_recursively = on;
- |}
- |
- |rr {
- | name=localhost;
- | reverse=on;
- | a=127.0.0.1;
- | owner=localhost;
- | soa=localhost,root.localhost,42,86400,900,86400,86400;
- |}
- """.stripMargin
-
- val PDNSD_DIRECT =
- """
- |global {
- | perm_cache = 2048;
- | %s
- | cache_dir = "%s";
- | server_ip = %s;
- | server_port = %d;
- | query_method = udp_only;
- | min_ttl = 15m;
- | max_ttl = 1w;
- | timeout = 10;
- | daemon = off;
- | par_queries = 4;
- |}
- |
- |server {
- | label = "remote-servers";
- | ip = %s;
- | timeout = 3;
- | query_method = udp_only;
- | %s
- | policy = included;
- | reject = %s;
- | reject_policy = fail;
- | reject_recursively = on;
- |}
- |
- |server {
- | label = "local-server";
- | ip = 127.0.0.1;
- | query_method = tcp_only;
- | port = %d;
- | reject = %s;
- | reject_policy = negate;
- | reject_recursively = on;
- |}
- |
- |rr {
- | name=localhost;
- | reverse=on;
- | a=127.0.0.1;
- | owner=localhost;
- | soa=localhost,root.localhost,42,86400,900,86400,86400;
- |}
- """.stripMargin
}
object Key {
diff --git a/mobile/src/main/scala/com/github/shadowsocks/utils/Parser.scala b/mobile/src/main/scala/com/github/shadowsocks/utils/Parser.scala
index 64d1ed88..3742af60 100644
--- a/mobile/src/main/scala/com/github/shadowsocks/utils/Parser.scala
+++ b/mobile/src/main/scala/com/github/shadowsocks/utils/Parser.scala
@@ -20,13 +20,15 @@
package com.github.shadowsocks.utils
+import java.net.URI
+
import android.net.Uri
import android.util.{Base64, Log}
import com.github.shadowsocks.database.Profile
object Parser {
val TAG = "ShadowParser"
- private val pattern = "(?i)ss://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]".r
+ private val pattern = "(?i)ss://[-a-zA-Z0-9+&@#/%?=~_|!:,.;\\[\\]]*[-a-zA-Z0-9+&@#/%=~_|\\[\\]]".r
private val userInfoPattern = "^(.+?):(.*)$".r
private val legacyPattern = "^(.+?):(.*)@(.+?):(\\d+?)$".r
@@ -54,8 +56,12 @@ object Parser {
val profile = new Profile
profile.method = method
profile.password = password
- profile.host = uri.getHost
- profile.remotePort = uri.getPort
+ // bug in Android: https://code.google.com/p/android/issues/detail?id=192855
+ val javaURI = new URI(m.matched)
+ profile.host = javaURI.getHost
+ if (profile.host.headOption.contains('[') && profile.host.lastOption.contains(']'))
+ profile.host = profile.host.substring(1, profile.host.length - 1)
+ profile.remotePort = javaURI.getPort
profile.plugin = uri.getQueryParameter(Key.plugin)
profile.name = uri.getFragment
profile
diff --git a/mobile/src/overture b/mobile/src/overture
new file mode 160000
index 00000000..862ce347
--- /dev/null
+++ b/mobile/src/overture
@@ -0,0 +1 @@
+Subproject commit 862ce347e19483fdd6f0a5c84f173c2efdc7d920
diff --git a/plugin/CHANGES.md b/plugin/CHANGES.md
index 78ee27d1..9e277fee 100644
--- a/plugin/CHANGES.md
+++ b/plugin/CHANGES.md
@@ -1,3 +1,5 @@
+* 0.0.3:
+ * Update support library version to 25.2.0.
* 0.0.2:
* Add `getOrDefault` to `PluginOptions`;
* Update support library version to 25.1.1.
diff --git a/plugin/build.sbt b/plugin/build.sbt
index ca3bc9b4..93e5f588 100644
--- a/plugin/build.sbt
+++ b/plugin/build.sbt
@@ -2,7 +2,7 @@ enablePlugins(AndroidLib)
android.useSupportVectors
name := "plugin"
-version := "0.0.2"
+version := "0.0.3"
pomExtra in Global := {
https://github.com/shadowsocks/shadowsocks-android
diff --git a/project/plugins.sbt b/project/plugins.sbt
index 27caeb6d..f48545c8 100644
--- a/project/plugins.sbt
+++ b/project/plugins.sbt
@@ -1,4 +1,4 @@
-addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.5")
+addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.6")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10")