Compare commits

...
79 Commits
Author SHA1 Message Date
Max Lv 96320896aa Update licenses 2017-03-02 14:53:53 +08:00
Max Lv 54efe36ffc Add missing -V option 2017-03-02 09:28:12 +08:00
Max Lv d5d6740a5a Fix typos 2017-03-02 09:22:40 +08:00
Mygod 2e69eafdf8 Refine timeout for getting external IP 2017-03-01 22:49:10 +08:00
Mygod 86773085c3 Wait for process to terminate with a better way
Refine #1122.
2017-03-01 22:28:35 +08:00
Mygod b6667b9e0f Use more readable variable name 2017-03-01 17:25:23 +08:00
Max Lv 0ee7e9d40e Build overture from source code 2017-03-01 16:39:04 +08:00
Mygod 5f2ce1804f Refine Overture configuration generation
* Respect remoteDns settings more;
* Make code more readable;
* Handle illegal characters in remoteDns.
2017-03-01 16:08:19 +08:00
Mygod a5e316999a Update dependencies 2017-03-01 15:30:03 +08:00
Max Lv 64a3d75ba2 Update shadowsocks-libev 2017-03-01 12:02:36 +08:00
Max Lv 9d09c9a097 Bump version 2017-03-01 09:59:00 +08:00
Max Lv b78f48504f Merge branch 'develop' into beta 2017-03-01 09:57:45 +08:00
Max Lv a61b03c86c Add 600ms timeout for waitpid 2017-03-01 09:53:35 +08:00
Max Lv f43362fb18 Fix overture with NAT mode 2017-02-25 16:58:31 +08:00
Max Lv 4f0e527df7 Replace PDNSD with overture 2017-02-24 21:00:40 +08:00
Max Lv a8878298cc Bump version 2017-02-24 19:44:46 +08:00
Max Lv 890be54403 Bump version 2017-02-24 19:36:44 +08:00
Max Lv 31984d1001 Add prebuilt overture libraries 2017-02-24 17:51:06 +08:00
Mygod f30c31ae3e Bump plugin lib version 2017-02-23 14:56:23 +08:00
Mygod 05963b6c82 Update support library 2017-02-23 14:55:32 +08:00
Mygod 98da4cd6e5 Update dependencies 2017-02-23 14:55:07 +08:00
Max Lv 64da35c59b Update shadowsocks-libev 2017-02-22 14:56:31 +08:00
Max Lv 458a3473b1 Trim remoteDns 2017-02-17 20:13:13 +08:00
Max Lv 66db347215 Fix #1130 2017-02-17 19:14:33 +08:00
Max Lv f9e32c995b Update shadowsocks-libev 2017-02-17 13:36:41 +08:00
Max Lv 0626ce8787 Fix a memory leak 2017-02-17 12:35:15 +08:00
Max Lv 11d827c6ba Bump version 2017-02-13 09:29:11 +08:00
Max Lv dc46cd971a Merge branch 'beta' 2017-02-13 09:21:18 +08:00
Max Lv 876e92f275 Bump version 2017-02-13 09:17:59 +08:00
Max Lv 41747c976b Update shadowsocks-libev 2017-02-12 20:24:56 +08:00
Max Lv 8277b07d24 Bump version 2017-02-10 10:16:27 +08:00
Max Lv b982867130 Merge branch 'beta' 2017-02-10 10:05:18 +08:00
Max Lv 491710679a Fix #1111 2017-02-09 10:50:44 +08:00
Max Lv 24c6aca85c Add remote DNS to route 2017-02-09 10:02:02 +08:00
Mygod 293e9b5dff Fix equals missing in Subnet 2017-02-08 18:47:53 +08:00
Mygod 3ac77e1f8d Fix crash on back pressed after MainActivity recreated 2017-02-08 18:47:23 +08:00
Mygod fd2c67d0b8 Add missing file 2017-02-08 17:19:22 +08:00
Mygod 684698c8e4 Refine URLs 2017-02-08 17:18:04 +08:00
Mygod 6a02d4b985 Add change log to plugin library 2017-02-08 17:13:17 +08:00
Mygod 7219ce429c Bump version 2017-02-08 17:07:56 +08:00
Max Lv 9975b135d0 Remove chacha20-poly1305 2017-02-08 12:19:51 +08:00
Max Lv 5d46ef45e3 Bump version 2017-02-08 12:03:16 +08:00
Max Lv 018aa781d3 Merge branch 'develop' into beta 2017-02-08 12:01:05 +08:00
Max Lv f7e2f8694d Add missing rules of base64.c 2017-02-08 11:58:15 +08:00
Max Lv 2d86122297 Update shadowsocks-libev 2017-02-08 11:58:15 +08:00
Max Lv ad0578d073 Fix UDP relay issues 2017-02-08 11:58:15 +08:00
Max Lv b671bc2c50 Update shadowsocks-libev 2017-02-08 11:58:15 +08:00
Mygod be5036183d Fix issues about SIP002 2017-02-08 11:58:15 +08:00
Mygod 212c3f666b Update shadowsocks-libev 2017-02-08 11:58:15 +08:00
Mygod deba1028c0 Remove unused sources in libsodium 2017-02-08 11:58:15 +08:00
Mygod 5559295beb Remove useless files in libsodium makefile 2017-02-08 11:58:15 +08:00
Mygod 41eec3e351 Use stable branch of libsodium 2017-02-08 11:58:15 +08:00
Mygod 56709385b1 Add missing AEAD ciphers 2017-02-08 11:58:15 +08:00
Mygod dc13249a1e Implement SIP002
https://github.com/shadowsocks/shadowsocks-org/issues/27
2017-02-08 11:58:15 +08:00
Mygod d579660682 Remove obsolete ciphers 2017-02-08 11:58:15 +08:00
Mygod 1e96a13ab1 Implement SIP004
https://github.com/shadowsocks/shadowsocks-org/issues/30
2017-02-08 11:58:15 +08:00
Mygod b71b27fbdf Use official repo of libsodium 2017-02-08 11:58:15 +08:00
Mygod 3fa8a0c90c Refine roundings for TrafficMonitor 2017-02-04 11:46:13 +08:00
Mygod f121ea2549 Prevent crashing when no market is installed 2017-02-02 21:51:52 +08:00
Mygod df1e40d60b Refine Subnet 2017-02-02 21:51:13 +08:00
Mygod ee08f02660 Use remote DNS for VPN 2017-02-02 21:49:31 +08:00
Mygod 7af26d1a4b Increase minimum sync time to let shadowsocks set up 2017-02-02 21:49:24 +08:00
Mygod 88319ecac7 Suggest to use fully qualified name for authorities in doc 2017-02-02 21:43:01 +08:00
Mygod b397ab2bbd Support pasting multiple lines 2017-02-02 21:27:35 +08:00
Mygod 33c087f13f Refine FAQ 2017-02-02 20:58:30 +08:00
Mygod 4996d4fc52 Add a known issue for Huawei's buggy ROM 2017-02-02 17:22:27 +08:00
Mygod 14fd86c716 Update android support library 2017-01-31 20:15:25 +08:00
Mygod 3b9a35d3e6 Merge branch 'beta' into develop 2017-01-31 19:30:37 +08:00
Mygod 3b93302539 Update dependencies 2017-01-31 01:37:25 +08:00
Max Lv 8de1dc1f7a Add signature 2017-01-24 22:59:26 +08:00
Mygod 55667c9e76 Add getOrDefault method for API 23- in plugin lib 2017-01-24 22:53:24 +08:00
Mygod e81c4f257e Bump plugin lib version 2017-01-24 22:52:54 +08:00
Mygod 61a926ff5e Update plugin doc 2017-01-24 22:42:24 +08:00
Mygod 8d1f2da1f0 Implement SIGTERM in jni-helper 2017-01-24 18:44:31 +08:00
Mygod 30c8c7e6eb Fix wrong thread error in binderDied 2017-01-24 16:58:55 +08:00
Mygod dadeffa903 Rename System to JniHelper to rename naming conflicts with
`java.lang.System`
2017-01-24 16:58:20 +08:00
Max Lv caf5731ff6 Update GFWList 2017-01-21 16:48:50 +08:00
Max Lv 437d8c3026 Revert "Revert "Speed up ndk-build from sbt-android""
This reverts commit bcbd860457.
2017-01-17 21:30:54 +08:00
Max Lv 44b47e6cd3 Update background for #1082 2017-01-16 16:18:18 +08:00
51 changed files with 8034 additions and 624 deletions
+2 -1
View File
@@ -29,7 +29,8 @@ The exclamation mark in the Wi-Fi/cellular icon appears because the system fails
1. Broken VPNService implementation, especially for IPv6; 1. Broken VPNService implementation, especially for IPv6;
2. Aggressive (or called broken) background service killing policy. 2. Aggressive (or called broken) background service killing policy.
Fixes for MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772) * Fixes for MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772)
* Fixes for Huawei: [#1091 (comment)](https://github.com/shadowsocks/shadowsocks-android/issues/1091#issuecomment-276949836)
### How to pause Shadowsocks service? ### How to pause Shadowsocks service?
+1
View File
@@ -9,3 +9,4 @@ local.sbt
mobile/src/main/libs mobile/src/main/libs
mobile/src/main/obj mobile/src/main/obj
mobile/src/main/jni/overture
+5 -5
View File
@@ -17,10 +17,6 @@
path = mobile/src/main/jni/redsocks path = mobile/src/main/jni/redsocks
url = https://github.com/shadowsocks/redsocks.git url = https://github.com/shadowsocks/redsocks.git
branch = shadowsocks-android 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"] [submodule "mobile/src/main/jni/mbedtls"]
path = mobile/src/main/jni/mbedtls path = mobile/src/main/jni/mbedtls
url = https://github.com/ARMmbed/mbedtls url = https://github.com/ARMmbed/mbedtls
@@ -29,10 +25,14 @@
url = https://android.googlesource.com/platform/external/pcre url = https://android.googlesource.com/platform/external/pcre
[submodule "mobile/src/main/jni/libsodium"] [submodule "mobile/src/main/jni/libsodium"]
path = mobile/src/main/jni/libsodium path = mobile/src/main/jni/libsodium
url = https://github.com/shadowsocks/libsodium.git url = https://github.com/jedisct1/libsodium.git
branch = stable
[submodule "mobile/src/main/jni/libev"] [submodule "mobile/src/main/jni/libev"]
path = mobile/src/main/jni/libev path = mobile/src/main/jni/libev
url = https://github.com/shadowsocks/libev.git url = https://github.com/shadowsocks/libev.git
[submodule "mobile/src/main/jni/libudns"] [submodule "mobile/src/main/jni/libudns"]
path = mobile/src/main/jni/libudns path = mobile/src/main/jni/libudns
url = https://github.com/shadowsocks/libudns.git url = https://github.com/shadowsocks/libudns.git
[submodule "mobile/src/overture"]
path = mobile/src/overture
url = https://github.com/shadowsocks/overture-android.git
+2 -1
View File
@@ -7,6 +7,7 @@ env:
global: global:
- NDK_VERSION=r12b - NDK_VERSION=r12b
- NDK_CCACHE=ccache - NDK_CCACHE=ccache
- GOROOT_BOOTSTRAP=$GOROOT
- ANDROID_NDK_HOME=$HOME/.android/android-ndk-${NDK_VERSION} - ANDROID_NDK_HOME=$HOME/.android/android-ndk-${NDK_VERSION}
- SBTPATH=$HOME/.sbt - SBTPATH=$HOME/.sbt
- PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH} - PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
@@ -49,4 +50,4 @@ install:
fi fi
script: script:
- $SBTPATH/sbt android:package - $SBTPATH/sbt go-build android:package
+1 -1
View File
@@ -50,7 +50,7 @@ Translators can go to [POEditor](https://poeditor.com/join/project/u5VHO9vhSf) t
<li>pcre: <a href="https://android.googlesource.com/platform/external/pcre/+/master/dist2/LICENCE">BSD</a></li> <li>pcre: <a href="https://android.googlesource.com/platform/external/pcre/+/master/dist2/LICENCE">BSD</a></li>
<li>libancillary: <a href="https://github.com/shadowsocks/libancillary/blob/shadowsocks-android/COPYING">BSD</a></li> <li>libancillary: <a href="https://github.com/shadowsocks/libancillary/blob/shadowsocks-android/COPYING">BSD</a></li>
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li> <li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li>
<li>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li> <li>overture: <a href="https://github.com/shawn1m/overture/blob/master/LICENSE">MIT</a></li>
<li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li> <li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li>
<li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</a></li> <li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</a></li>
<li>libudns: <a href="https://github.com/shadowsocks/libudns/blob/master/COPYING.LGPL">LGPL</a></li> <li>libudns: <a href="https://github.com/shadowsocks/libudns/blob/master/COPYING.LGPL">LGPL</a></li>
+1 -1
View File
@@ -18,7 +18,7 @@ lazy val commonSettings = Seq(
typedResources := false typedResources := false
) )
val supportLibsVersion = "25.1.0" val supportLibsVersion = "25.2.0"
lazy val root = Project(id = "shadowsocks-android", base = file(".")) lazy val root = Project(id = "shadowsocks-android", base = file("."))
.settings(commonSettings) .settings(commonSettings)
.aggregate(plugin, mobile) .aggregate(plugin, mobile)
+18 -10
View File
@@ -2,11 +2,11 @@ enablePlugins(AndroidApp)
android.useSupportVectors android.useSupportVectors
name := "shadowsocks" name := "shadowsocks"
version := "4.0.0" version := "4.1.0"
versionCode := Some(175) versionCode := Some(180)
proguardOptions ++= proguardOptions ++=
"-keep class com.github.shadowsocks.System { *; }" :: "-keep class com.github.shadowsocks.JniHelper { *; }" ::
"-dontwarn com.google.android.gms.internal.**" :: "-dontwarn com.google.android.gms.internal.**" ::
"-dontwarn com.j256.ormlite.**" :: "-dontwarn com.j256.ormlite.**" ::
"-dontwarn okio.**" :: "-dontwarn okio.**" ::
@@ -15,24 +15,32 @@ proguardOptions ++=
resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW") resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW")
val playServicesVersion = "10.0.1" val playServicesVersion = "10.2.0"
resolvers += Resolver.jcenterRepo resolvers += Resolver.jcenterRepo
libraryDependencies ++= libraryDependencies ++=
"com.futuremind.recyclerfastscroll" % "fastscroll" % "0.2.5" :: "com.futuremind.recyclerfastscroll" % "fastscroll" % "0.2.5" ::
"com.evernote" % "android-job" % "1.1.4" :: "com.evernote" % "android-job" % "1.1.7" ::
"com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" :: "com.github.jorgecastilloprz" % "fabprogresscircle" % "1.01" ::
"com.google.android.gms" % "play-services-ads" % playServicesVersion :: "com.google.android.gms" % "play-services-ads" % playServicesVersion ::
"com.google.android.gms" % "play-services-analytics" % playServicesVersion :: "com.google.android.gms" % "play-services-analytics" % playServicesVersion ::
"com.google.android.gms" % "play-services-gcm" % playServicesVersion :: "com.google.android.gms" % "play-services-gcm" % playServicesVersion ::
"com.j256.ormlite" % "ormlite-android" % "5.0" :: "com.j256.ormlite" % "ormlite-android" % "5.0" ::
"com.mikepenz" % "crossfader" % "1.5.0" :: "com.mikepenz" % "crossfader" % "1.5.0" ::
"com.mikepenz" % "fastadapter" % "2.1.5" :: "com.mikepenz" % "fastadapter" % "2.1.7" ::
"com.mikepenz" % "iconics-core" % "2.8.2" :: "com.mikepenz" % "iconics-core" % "2.8.2" ::
"com.mikepenz" % "materialdrawer" % "5.8.1" :: "com.mikepenz" % "materialdrawer" % "5.8.2" ::
"com.mikepenz" % "materialize" % "1.0.0" :: "com.mikepenz" % "materialize" % "1.0.1" ::
"com.squareup.okhttp3" % "okhttp" % "3.5.0" :: "com.squareup.okhttp3" % "okhttp" % "3.6.0" ::
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" :: "com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
"dnsjava" % "dnsjava" % "2.1.7" :: "dnsjava" % "dnsjava" % "2.1.8" ::
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" :: "eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" ::
"net.glxn.qrgen" % "android" % "2.0" :: "net.glxn.qrgen" % "android" % "2.0" ::
Nil 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")
}
}
+2
View File
@@ -144,6 +144,8 @@
android:process=":bg"/> android:process=":bg"/>
<service android:name="com.evernote.android.job.gcm.PlatformGcmService" <service android:name="com.evernote.android.job.gcm.PlatformGcmService"
android:process=":bg"/> android:process=":bg"/>
<service android:name="com.evernote.android.job.JobRescheduleService"
android:process=":bg"/>
<receiver android:name="com.evernote.android.job.v14.PlatformAlarmReceiver" <receiver android:name="com.evernote.android.job.v14.PlatformAlarmReceiver"
android:process=":bg"/> android:process=":bg"/>
<receiver android:name="com.evernote.android.job.JobBootReceiver" <receiver android:name="com.evernote.android.job.JobBootReceiver"
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
+1
View File
@@ -0,0 +1 @@
127.0.0.1 localhost
+1 -1
View File
@@ -296,7 +296,7 @@ along with this program. If not, see <a href="http://www.gnu.org/licenses/">http
<li>pcre: <a href="https://android.googlesource.com/platform/external/pcre/+/master/dist2/LICENCE">BSD</a></li> <li>pcre: <a href="https://android.googlesource.com/platform/external/pcre/+/master/dist2/LICENCE">BSD</a></li>
<li>libancillary: <a href="https://github.com/shadowsocks/libancillary/blob/shadowsocks-android/COPYING">BSD</a></li> <li>libancillary: <a href="https://github.com/shadowsocks/libancillary/blob/shadowsocks-android/COPYING">BSD</a></li>
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li> <li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li>
<li>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li> <li>overture: <a href="https://github.com/shawn1m/overture/blob/master/LICENSE">MIT</a></li>
<li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li> <li>libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/libev/LICENSE">GPLv2</a></li>
<li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</a></li> <li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</a></li>
<li>libudns: <a href="https://github.com/shadowsocks/libudns/blob/master/COPYING.LGPL">LGPL</a></li> <li>libudns: <a href="https://github.com/shadowsocks/libudns/blob/master/COPYING.LGPL">LGPL</a></li>
@@ -38,12 +38,35 @@
package com.github.shadowsocks; package com.github.shadowsocks;
public class System { import android.os.Build;
static { import android.system.ErrnoException;
java.lang.System.loadLibrary("system");
}
public static native int exec(String cmd); public class JniHelper {
public static native int sendfd(int fd, String path); static {
public static native void jniclose(int fd); System.loadLibrary("jni-helper");
}
@Deprecated // Use Process.destroy() since API 24
public static void sigtermCompat(Process process) throws Exception {
if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
int errno = sigterm(process);
if (errno != 0) throw Build.VERSION.SDK_INT >= 21
? 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);
} }
+56 -121
View File
@@ -26,115 +26,32 @@ include $(CLEAR_VARS)
SODIUM_SOURCE := \ SODIUM_SOURCE := \
crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c \ crypto_aead/chacha20poly1305/sodium/aead_chacha20poly1305.c \
crypto_auth/crypto_auth.c \
crypto_auth/hmacsha256/auth_hmacsha256_api.c \
crypto_auth/hmacsha256/cp/hmac_hmacsha256.c \
crypto_auth/hmacsha256/cp/verify_hmacsha256.c \
crypto_auth/hmacsha512/auth_hmacsha512_api.c \
crypto_auth/hmacsha512/cp/hmac_hmacsha512.c \
crypto_auth/hmacsha512/cp/verify_hmacsha512.c \
crypto_auth/hmacsha512256/auth_hmacsha512256_api.c \
crypto_auth/hmacsha512256/cp/hmac_hmacsha512256.c \
crypto_auth/hmacsha512256/cp/verify_hmacsha512256.c \
crypto_box/crypto_box.c \
crypto_box/crypto_box_easy.c \
crypto_box/crypto_box_seal.c \
crypto_box/curve25519xsalsa20poly1305/box_curve25519xsalsa20poly1305_api.c \
crypto_box/curve25519xsalsa20poly1305/ref/after_curve25519xsalsa20poly1305.c \
crypto_box/curve25519xsalsa20poly1305/ref/before_curve25519xsalsa20poly1305.c \
crypto_box/curve25519xsalsa20poly1305/ref/box_curve25519xsalsa20poly1305.c \
crypto_box/curve25519xsalsa20poly1305/ref/keypair_curve25519xsalsa20poly1305.c \
crypto_core/hsalsa20/ref2/core_hsalsa20.c \
crypto_core/hsalsa20/core_hsalsa20_api.c \
crypto_core/salsa20/ref/core_salsa20.c \ crypto_core/salsa20/ref/core_salsa20.c \
crypto_core/salsa20/core_salsa20_api.c \
crypto_generichash/crypto_generichash.c \
crypto_generichash/blake2/generichash_blake2_api.c \
crypto_generichash/blake2/ref/blake2-impl.h \
crypto_generichash/blake2/ref/blake2.h \
crypto_generichash/blake2/ref/blake2b-compress-ref.c \ crypto_generichash/blake2/ref/blake2b-compress-ref.c \
crypto_generichash/blake2/ref/blake2b-load-sse2.h \
crypto_generichash/blake2/ref/blake2b-load-sse41.h \
crypto_generichash/blake2/ref/blake2b-ref.c \ crypto_generichash/blake2/ref/blake2b-ref.c \
crypto_generichash/blake2/ref/blake2b-round.h \
crypto_generichash/blake2/ref/generichash_blake2b.c \ crypto_generichash/blake2/ref/generichash_blake2b.c \
crypto_hash/crypto_hash.c \
crypto_hash/sha256/hash_sha256_api.c \
crypto_hash/sha256/cp/hash_sha256.c \
crypto_hash/sha512/hash_sha512_api.c \
crypto_hash/sha512/cp/hash_sha512.c \
crypto_onetimeauth/crypto_onetimeauth.c \
crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \ crypto_onetimeauth/poly1305/onetimeauth_poly1305.c \
crypto_onetimeauth/poly1305/onetimeauth_poly1305.h \
crypto_onetimeauth/poly1305/donna/poly1305_donna.h \
crypto_onetimeauth/poly1305/donna/poly1305_donna32.h \
crypto_onetimeauth/poly1305/donna/poly1305_donna64.h \
crypto_onetimeauth/poly1305/donna/poly1305_donna.c \ crypto_onetimeauth/poly1305/donna/poly1305_donna.c \
crypto_pwhash/scryptsalsa208sha256/crypto_scrypt-common.c \ crypto_pwhash/crypto_pwhash.c \
crypto_pwhash/scryptsalsa208sha256/crypto_scrypt.h \ crypto_pwhash/argon2/argon2-core.c \
crypto_pwhash/scryptsalsa208sha256/scrypt_platform.c \ crypto_pwhash/argon2/argon2.c \
crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.c \ crypto_pwhash/argon2/argon2-encoding.c \
crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.h \ crypto_pwhash/argon2/argon2-fill-block-ref.c \
crypto_pwhash/scryptsalsa208sha256/pwhash_scryptsalsa208sha256.c \ crypto_pwhash/argon2/blake2b-long.c \
crypto_pwhash/scryptsalsa208sha256/sysendian.h \ crypto_pwhash/argon2/pwhash_argon2i.c \
crypto_pwhash/scryptsalsa208sha256/nosse/pwhash_scryptsalsa208sha256_nosse.c \
crypto_scalarmult/crypto_scalarmult.c \
crypto_scalarmult/curve25519/scalarmult_curve25519.c \ crypto_scalarmult/curve25519/scalarmult_curve25519.c \
crypto_scalarmult/curve25519/scalarmult_curve25519.h \
crypto_secretbox/crypto_secretbox.c \
crypto_secretbox/crypto_secretbox_easy.c \
crypto_secretbox/xsalsa20poly1305/secretbox_xsalsa20poly1305_api.c \
crypto_shorthash/crypto_shorthash.c \
crypto_shorthash/siphash24/shorthash_siphash24_api.c \
crypto_shorthash/siphash24/ref/shorthash_siphash24.c \
crypto_sign/crypto_sign.c \
crypto_sign/ed25519/ref10/base.h \
crypto_sign/ed25519/ref10/base2.h \
crypto_sign/ed25519/sign_ed25519_api.c \
crypto_sign/ed25519/ref10/keypair.c \
crypto_sign/ed25519/ref10/open.c \
crypto_sign/ed25519/ref10/obsolete.c \
crypto_sign/ed25519/ref10/sign.c \
crypto_stream/crypto_stream.c \
crypto_stream/chacha20/stream_chacha20.c \ crypto_stream/chacha20/stream_chacha20.c \
crypto_stream/chacha20/stream_chacha20.h \
crypto_stream/chacha20/ref/stream_chacha20_ref.h \
crypto_stream/chacha20/ref/stream_chacha20_ref.c \ crypto_stream/chacha20/ref/stream_chacha20_ref.c \
crypto_stream/salsa20/stream_salsa20_api.c \ crypto_stream/salsa20/ref/stream_salsa20_ref.c \
crypto_stream/xsalsa20/stream_xsalsa20_api.c \ crypto_stream/salsa20/ref/xor_salsa20_ref.c \
crypto_stream/xsalsa20/ref/stream_xsalsa20.c \
crypto_stream/xsalsa20/ref/xor_xsalsa20.c \
crypto_verify/16/verify_16_api.c \
crypto_verify/16/ref/verify_16.c \ crypto_verify/16/ref/verify_16.c \
crypto_verify/32/verify_32_api.c \
crypto_verify/32/ref/verify_32.c \
crypto_verify/64/verify_64_api.c \
crypto_verify/64/ref/verify_64.c \
randombytes/randombytes.c \ randombytes/randombytes.c \
randombytes/sysrandom/randombytes_sysrandom.c \
sodium/core.c \ sodium/core.c \
sodium/runtime.c \ sodium/runtime.c \
sodium/utils.c \ sodium/utils.c \
sodium/version.c sodium/version.c
SODIUM_SOURCE += \
crypto_scalarmult/curve25519/ref10/x25519_ref10.c
SODIUM_SOURCE += \
crypto_pwhash/argon2/argon2-core.c \
crypto_pwhash/argon2/argon2.c \
crypto_pwhash/argon2/argon2-encoding.c \
crypto_pwhash/argon2/blake2b-long.c \
crypto_pwhash/argon2/argon2-fill-block-ref.c \
crypto_pwhash/argon2/pwhash_argon2i.c \
crypto_pwhash/argon2/argon2-fill-block-ssse3.c
SODIUM_SOURCE += \
crypto_stream/salsa20/ref/stream_salsa20_ref.c \
crypto_stream/salsa20/ref/xor_salsa20_ref.c
SODIUM_SOURCE += \
randombytes/sysrandom/randombytes_sysrandom.c
LOCAL_MODULE := sodium LOCAL_MODULE := sodium
LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/libsodium/src/libsodium/include \ LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/include \ -I$(LOCAL_PATH)/include \
@@ -200,6 +117,22 @@ LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE))
include $(BUILD_STATIC_LIBRARY) include $(BUILD_STATIC_LIBRARY)
########################################################
## libbloom
########################################################
include $(CLEAR_VARS)
BLOOM_SOURCE := bloom.c murmur2/MurmurHash2.c
LOCAL_MODULE := libbloom
LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/shadowsocks-libev/libbloom \
-I$(LOCAL_PATH)/shadowsocks-libev/libbloom/murmur2
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/libbloom/, $(BLOOM_SOURCE))
include $(BUILD_STATIC_LIBRARY)
######################################################## ########################################################
## libipset ## libipset
######################################################## ########################################################
@@ -306,32 +239,17 @@ LOCAL_CFLAGS := -O2 -std=gnu99 -DUSE_IPTABLES \
include $(BUILD_SHARED_EXECUTABLE) 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 ## shadowsocks-libev local
######################################################## ########################################################
include $(CLEAR_VARS) include $(CLEAR_VARS)
SHADOWSOCKS_SOURCES := local.c cache.c udprelay.c encrypt.c \ SHADOWSOCKS_SOURCES := local.c \
utils.c netutils.c json.c jconf.c acl.c http.c tls.c rule.c \ cache.c udprelay.c utils.c netutils.c json.c jconf.c \
plugin.c \ acl.c http.c tls.c rule.c \
crypto.c aead.c stream.c base64.c \
plugin.c ppbloom.c \
android.c android.c
LOCAL_MODULE := ss-local LOCAL_MODULE := ss-local
@@ -345,13 +263,14 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_LOCAL \
-I$(LOCAL_PATH)/mbedtls/include \ -I$(LOCAL_PATH)/mbedtls/include \
-I$(LOCAL_PATH)/pcre \ -I$(LOCAL_PATH)/pcre \
-I$(LOCAL_PATH)/libudns \ -I$(LOCAL_PATH)/libudns \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/libsodium/src/libsodium/include \ -I$(LOCAL_PATH)/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \ -I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libbloom \
-I$(LOCAL_PATH)/libev -I$(LOCAL_PATH)/libev
LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libudns \ LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libbloom libudns \
libsodium libancillary libpcre libsodium libancillary libpcre
LOCAL_LDLIBS := -llog LOCAL_LDLIBS := -llog
@@ -364,7 +283,11 @@ include $(BUILD_SHARED_EXECUTABLE)
include $(CLEAR_VARS) include $(CLEAR_VARS)
SHADOWSOCKS_SOURCES := tunnel.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c android.c plugin.c SHADOWSOCKS_SOURCES := tunnel.c \
cache.c udprelay.c utils.c netutils.c json.c jconf.c \
crypto.c aead.c stream.c base64.c \
plugin.c ppbloom.c \
android.c
LOCAL_MODULE := ss-tunnel LOCAL_MODULE := ss-tunnel
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES)) LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
@@ -374,30 +297,31 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DMODULE_TUNNEL \
-I$(LOCAL_PATH)/libancillary \ -I$(LOCAL_PATH)/libancillary \
-I$(LOCAL_PATH)/include \ -I$(LOCAL_PATH)/include \
-I$(LOCAL_PATH)/libudns \ -I$(LOCAL_PATH)/libudns \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/libsodium/src/libsodium/include \ -I$(LOCAL_PATH)/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \ -I$(LOCAL_PATH)/libsodium/src/libsodium/include/sodium \
-I$(LOCAL_PATH)/mbedtls/include \ -I$(LOCAL_PATH)/mbedtls/include \
-I$(LOCAL_PATH)/libev \ -I$(LOCAL_PATH)/libev \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libbloom \
-I$(LOCAL_PATH)/include/shadowsocks-libev -I$(LOCAL_PATH)/include/shadowsocks-libev
LOCAL_STATIC_LIBRARIES := libev libmbedtls libsodium libcork libudns libancillary LOCAL_STATIC_LIBRARIES := libev libmbedtls libsodium libcork libbloom libudns libancillary
LOCAL_LDLIBS := -llog LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_EXECUTABLE) include $(BUILD_SHARED_EXECUTABLE)
######################################################## ########################################################
## system ## jni-helper
######################################################## ########################################################
include $(CLEAR_VARS) include $(CLEAR_VARS)
LOCAL_MODULE:= system LOCAL_MODULE:= jni-helper
LOCAL_C_INCLUDES:= $(LOCAL_PATH)/libancillary LOCAL_C_INCLUDES:= $(LOCAL_PATH)/libancillary
LOCAL_SRC_FILES:= system.cpp LOCAL_SRC_FILES:= jni-helper.cpp
LOCAL_LDLIBS := -ldl -llog LOCAL_LDLIBS := -ldl -llog
@@ -553,6 +477,17 @@ LOCAL_SRC_FILES := $(addprefix pcre/, $(libpcre_src_files))
include $(BUILD_STATIC_LIBRARY) 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 # Import cpufeatures
$(call import-module,android/cpufeatures) $(call import-module,android/cpufeatures)
+198
View File
@@ -0,0 +1,198 @@
#define LOG_TAG "JniHelper"
#include "jni.h"
#include <android/log.h>
#include <sys/system_properties.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ancillary.h>
#define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0)
#define THROW(env, clazz, msg) do { env->ThrowNew(env->FindClass(clazz), msg); } while (0)
static jclass ProcessImpl;
static jfieldID ProcessImpl_pid, ProcessImpl_exitValue, ProcessImpl_exitValueMutex;
static int sdk_version() {
char version[PROP_VALUE_MAX + 1];
__system_property_get("ro.build.version.sdk", version);
return atoi(version);
}
jint Java_com_github_shadowsocks_jnihelper_sigterm(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 -1;
}
jint pid = env->GetIntField(process, ProcessImpl_pid);
// Suppress "No such process" errors. We just want the process killed. It's fine if it's already killed.
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);
}
jint Java_com_github_shadowsocks_jnihelper_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) {
int fd;
struct sockaddr_un addr;
const char *sock_str = env->GetStringUTFChars(path, 0);
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
LOGE("socket() failed: %s (socket fd = %d)\n", strerror(errno), fd);
return (jint)-1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock_str, sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
LOGE("connect() failed: %s (fd = %d)\n", strerror(errno), fd);
close(fd);
return (jint)-1;
}
if (ancil_send_fd(fd, tun_fd)) {
LOGE("ancil_send_fd: %s", strerror(errno));
close(fd);
return (jint)-1;
}
close(fd);
env->ReleaseStringUTFChars(path, sock_str);
return 0;
}
static const char *classPathName = "com/github/shadowsocks/JniHelper";
static JNINativeMethod method_table[] = {
{ "close", "(I)V",
(void*) Java_com_github_shadowsocks_jnihelper_close },
{ "sendFd", "(ILjava/lang/String;)I",
(void*) Java_com_github_shadowsocks_jnihelper_sendfd },
{ "sigterm", "(Ljava/lang/Process;)I",
(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.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName, method_table,
sizeof(method_table) / sizeof(method_table[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* This is called by the VM when the shared library is first loaded.
*/
typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_6) != JNI_OK) {
THROW(env, "java/lang/RuntimeException", "GetEnv failed");
goto bail;
}
env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
THROW(env, "java/lang/RuntimeException", "registerNativeMethods failed");
goto bail;
}
if (sdk_version() < 24) {
if (!(ProcessImpl = env->FindClass("java/lang/ProcessManager$ProcessImpl"))) {
THROW(env, "java/lang/RuntimeException", "ProcessManager$ProcessImpl not found");
goto bail;
}
ProcessImpl = (jclass) env->NewGlobalRef((jobject) ProcessImpl);
if (!(ProcessImpl_pid = env->GetFieldID(ProcessImpl, "pid", "I"))) {
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;
bail:
return result;
}
-159
View File
@@ -1,159 +0,0 @@
#define LOG_TAG "Shadowsocks"
#include "jni.h"
#include <android/log.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <sys/un.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <ancillary.h>
#define LOGI(...) do { __android_log_print(ANDROID_LOG_INFO, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGW(...) do { __android_log_print(ANDROID_LOG_WARN, LOG_TAG, __VA_ARGS__); } while(0)
#define LOGE(...) do { __android_log_print(ANDROID_LOG_ERROR, LOG_TAG, __VA_ARGS__); } while(0)
jint Java_com_github_shadowsocks_system_exec(JNIEnv *env, jobject thiz, jstring cmd) {
const char *cmd_str = env->GetStringUTFChars(cmd, 0);
pid_t pid;
/* Fork off the parent process */
pid = fork();
if (pid < 0) {
env->ReleaseStringUTFChars(cmd, cmd_str);
return -1;
}
if (pid > 0) {
env->ReleaseStringUTFChars(cmd, cmd_str);
return pid;
}
execl("/system/bin/sh", "sh", "-c", cmd_str, NULL);
env->ReleaseStringUTFChars(cmd, cmd_str);
return 1;
}
void Java_com_github_shadowsocks_system_jniclose(JNIEnv *env, jobject thiz, jint fd) {
close(fd);
}
jint Java_com_github_shadowsocks_system_sendfd(JNIEnv *env, jobject thiz, jint tun_fd, jstring path) {
int fd;
struct sockaddr_un addr;
const char *sock_str = env->GetStringUTFChars(path, 0);
if ( (fd = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
LOGE("socket() failed: %s (socket fd = %d)\n", strerror(errno), fd);
return (jint)-1;
}
memset(&addr, 0, sizeof(addr));
addr.sun_family = AF_UNIX;
strncpy(addr.sun_path, sock_str, sizeof(addr.sun_path)-1);
if (connect(fd, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
LOGE("connect() failed: %s (fd = %d)\n", strerror(errno), fd);
close(fd);
return (jint)-1;
}
if (ancil_send_fd(fd, tun_fd)) {
LOGE("ancil_send_fd: %s", strerror(errno));
close(fd);
return (jint)-1;
}
close(fd);
env->ReleaseStringUTFChars(path, sock_str);
return 0;
}
static const char *classPathName = "com/github/shadowsocks/System";
static JNINativeMethod method_table[] = {
{ "jniclose", "(I)V",
(void*) Java_com_github_shadowsocks_system_jniclose },
{ "sendfd", "(ILjava/lang/String;)I",
(void*) Java_com_github_shadowsocks_system_sendfd },
{ "exec", "(Ljava/lang/String;)I",
(void*) Java_com_github_shadowsocks_system_exec }
};
/*
* Register several native methods for one class.
*/
static int registerNativeMethods(JNIEnv* env, const char* className,
JNINativeMethod* gMethods, int numMethods)
{
jclass clazz;
clazz = env->FindClass(className);
if (clazz == NULL) {
LOGE("Native registration unable to find class '%s'", className);
return JNI_FALSE;
}
if (env->RegisterNatives(clazz, gMethods, numMethods) < 0) {
LOGE("RegisterNatives failed for '%s'", className);
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* Register native methods for all classes we know about.
*
* returns JNI_TRUE on success.
*/
static int registerNatives(JNIEnv* env)
{
if (!registerNativeMethods(env, classPathName, method_table,
sizeof(method_table) / sizeof(method_table[0]))) {
return JNI_FALSE;
}
return JNI_TRUE;
}
/*
* This is called by the VM when the shared library is first loaded.
*/
typedef union {
JNIEnv* env;
void* venv;
} UnionJNIEnvToVoid;
jint JNI_OnLoad(JavaVM* vm, void* reserved) {
UnionJNIEnvToVoid uenv;
uenv.venv = NULL;
jint result = -1;
JNIEnv* env = NULL;
LOGI("JNI_OnLoad");
if (vm->GetEnv(&uenv.venv, JNI_VERSION_1_4) != JNI_OK) {
LOGE("ERROR: GetEnv failed");
goto bail;
}
env = uenv.env;
if (registerNatives(env) != JNI_TRUE) {
LOGE("ERROR: registerNatives failed");
goto bail;
}
result = JNI_VERSION_1_4;
bail:
return result;
}
@@ -10,6 +10,5 @@
<EditText android:id="@+id/content" <EditText android:id="@+id/content"
android:layout_width="match_parent" android:layout_width="match_parent"
android:layout_height="wrap_content" android:layout_height="wrap_content"
android:inputType="textNoSuggestions" android:inputType="textNoSuggestions|textMultiLine"/>
android:lines="1"/>
</LinearLayout> </LinearLayout>
+8 -4
View File
@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?> <?xml version="1.0" encoding="utf-8"?>
<resources> <resources>
<string-array name="enc_method_entry"> <string-array name="enc_method_entry">
<item>TABLE</item>
<item>RC4</item>
<item>RC4-MD5</item> <item>RC4-MD5</item>
<item>AES-128-CFB</item> <item>AES-128-CFB</item>
<item>AES-192-CFB</item> <item>AES-192-CFB</item>
@@ -17,11 +15,13 @@
<item>SALSA20</item> <item>SALSA20</item>
<item>CHACHA20</item> <item>CHACHA20</item>
<item>CHACHA20-IETF</item> <item>CHACHA20-IETF</item>
<item>AES-128-GCM</item>
<item>AES-192-GCM</item>
<item>AES-256-GCM</item>
<item>CHACHA20-IETF-POLY1305</item>
</string-array> </string-array>
<string-array name="enc_method_value"> <string-array name="enc_method_value">
<item>table</item>
<item>rc4</item>
<item>rc4-md5</item> <item>rc4-md5</item>
<item>aes-128-cfb</item> <item>aes-128-cfb</item>
<item>aes-192-cfb</item> <item>aes-192-cfb</item>
@@ -36,6 +36,10 @@
<item>salsa20</item> <item>salsa20</item>
<item>chacha20</item> <item>chacha20</item>
<item>chacha20-ietf</item> <item>chacha20-ietf</item>
<item>aes-128-gcm</item>
<item>aes-192-gcm</item>
<item>aes-256-gcm</item>
<item>chacha20-ietf-poly1305</item>
</string-array> </string-array>
<string-array name="route_entry"> <string-array name="route_entry">
-3
View File
@@ -36,8 +36,6 @@
<!-- feature category --> <!-- feature category -->
<string name="ipv6">IPv6 Route</string> <string name="ipv6">IPv6 Route</string>
<string name="ipv6_summary">Redirect IPv6 traffic to remote</string> <string name="ipv6_summary">Redirect IPv6 traffic to remote</string>
<string name="onetime_auth">One-time Authentication</string>
<string name="onetime_auth_summary">Enable one-time authentication</string>
<string name="route_list">Route</string> <string name="route_list">Route</string>
<string name="route_entry_all">All</string> <string name="route_entry_all">All</string>
<string name="route_entry_bypass_lan">Bypass LAN</string> <string name="route_entry_bypass_lan">Bypass LAN</string>
@@ -62,7 +60,6 @@
<string name="forward_success">Shadowsocks started.</string> <string name="forward_success">Shadowsocks started.</string>
<string name="invalid_server">Invalid server name</string> <string name="invalid_server">Invalid server name</string>
<string name="service_failed">Failed to connect the remote server</string> <string name="service_failed">Failed to connect the remote server</string>
<string name="method_unsafe">WARNING: The current encryption method is unsafe</string>
<string name="nat_deprecated">WARNING: NAT mode has been deprecated since Android 5.0</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="nat_no_root">NAT mode requires ROOT permission</string>
<string name="switch_to_vpn">Switch to VPN mode</string> <string name="switch_to_vpn">Switch to VPN mode</string>
+5 -8
View File
@@ -2,13 +2,14 @@
<PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android" <PreferenceScreen xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks"> xmlns:app="http://schemas.android.com/apk/res/com.github.shadowsocks">
<be.mygod.preference.EditTextPreference
android:key="profileName"
android:summary="%s"
android:title="@string/profile_name"/>
<be.mygod.preference.PreferenceCategory <be.mygod.preference.PreferenceCategory
android:title="@string/proxy_cat"> android:title="@string/proxy_cat">
<be.mygod.preference.EditTextPreference
android:key="profileName"
android:summary="%s"
android:title="@string/profile_name"/>
<be.mygod.preference.EditTextPreference <be.mygod.preference.EditTextPreference
android:key="proxy" android:key="proxy"
android:summary="%s" android:summary="%s"
@@ -36,10 +37,6 @@
android:entryValues="@array/enc_method_value" android:entryValues="@array/enc_method_value"
android:summary="%s" android:summary="%s"
android:title="@string/enc_method"/> android:title="@string/enc_method"/>
<SwitchPreference
android:key="isAuth"
android:summary="@string/onetime_auth_summary"
android:title="@string/onetime_auth"/>
</be.mygod.preference.PreferenceCategory> </be.mygod.preference.PreferenceCategory>
@@ -39,11 +39,11 @@ import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.plugin.{PluginConfiguration, PluginManager, PluginOptions} import com.github.shadowsocks.plugin.{PluginConfiguration, PluginManager, PluginOptions}
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
import okhttp3.{Dns, FormBody, OkHttpClient, Request} 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
import scala.collection.mutable.ArrayBuffer import scala.collection.mutable.ArrayBuffer
import scala.collection.JavaConversions._
import scala.util.Random import scala.util.Random
trait BaseService extends Service { trait BaseService extends Service {
@@ -135,9 +135,9 @@ trait BaseService extends Service {
case Some(ip) => util.Arrays.asList(InetAddress.getByName(ip)) case Some(ip) => util.Arrays.asList(InetAddress.getByName(ip))
case _ => Dns.SYSTEM.lookup(hostname) case _ => Dns.SYSTEM.lookup(hostname)
}) })
.connectTimeout(10, TimeUnit.SECONDS) .connectTimeout(5, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS) .writeTimeout(5, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS) .readTimeout(5, TimeUnit.SECONDS)
.build() .build()
val requestBody = new FormBody.Builder() val requestBody = new FormBody.Builder()
.add("sig", sig) .add("sig", sig)
@@ -183,9 +183,6 @@ trait BaseService extends Service {
changeState(State.CONNECTING) changeState(State.CONNECTING)
if (profile.isMethodUnsafe)
handler.post(() => Toast.makeText(this, R.string.method_unsafe, Toast.LENGTH_LONG).show())
Utils.ThrowableFuture(try connect() catch { Utils.ThrowableFuture(try connect() catch {
case _: NameNotResolvedException => stopRunner(stopService = true, getString(R.string.invalid_server)) case _: NameNotResolvedException => stopRunner(stopService = true, getString(R.string.invalid_server))
case _: NullConnectionException => stopRunner(stopService = true, getString(R.string.reboot_required)) case _: NullConnectionException => stopRunner(stopService = true, getString(R.string.reboot_required))
@@ -325,11 +322,66 @@ trait BaseService extends Service {
.put("server_port", profile.remotePort) .put("server_port", profile.remotePort)
.put("password", profile.password) .put("password", profile.password)
.put("method", profile.method) .put("method", profile.method)
if (profile.auth) config.put("auth", true)
if (pluginPath != null) config if (pluginPath != null) config
.put("plugin", Commandline.toString(buildPluginCommandLine())) .put("plugin", Commandline.toString(buildPluginCommandLine()))
.put("plugin_opts", plugin.toString) .put("plugin_opts", plugin.toString)
IOUtils.writeString(new File(getFilesDir, file), config.toString) IOUtils.writeString(new File(getFilesDir, file), config.toString)
file file
} }
protected final def buildOvertureConfig(file: String): String = {
val url = "http://icanhazip.com"
val client = new OkHttpClient.Builder()
.connectTimeout(10, TimeUnit.SECONDS)
.writeTimeout(10, TimeUnit.SECONDS)
.readTimeout(30, TimeUnit.SECONDS)
.build()
val request = new Request.Builder()
.url(url)
.build()
val response = client.newCall(request).execute()
val externalIp = if (response.isSuccessful) response.body.string.trim else ""
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)
if (edns) dns
.put("Protocol", "tcp")
.put("Socks5Address", "127.0.0.1:" + profile.localPort)
.put("EDNSClientSubnet", new JSONObject()
.put("Policy", "auto")
.put("ExternalIP", externalIp))
else dns
.put("Protocol", "udp")
.put("EDNSClientSubnet", new JSONObject().put("Policy", "disable"))
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
}
} }
@@ -24,6 +24,7 @@ import java.io._
import java.lang.System.currentTimeMillis import java.lang.System.currentTimeMillis
import java.util.concurrent.Semaphore import java.util.concurrent.Semaphore
import android.os.Build
import android.util.Log import android.util.Log
import com.github.shadowsocks.utils.CloseUtils._ import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils.Commandline import com.github.shadowsocks.utils.Commandline
@@ -89,7 +90,7 @@ class GuardedProcess(cmd: String*) {
} catch { } catch {
case _: InterruptedException => case _: InterruptedException =>
if (BuildConfig.DEBUG) Log.d(TAG, "thread interrupt, destroy process: " + Commandline.toString(cmd)) if (BuildConfig.DEBUG) Log.d(TAG, "thread interrupt, destroy process: " + Commandline.toString(cmd))
process.destroy() destroyProcess()
case e: IOException => ioException = e case e: IOException => ioException = e
} finally semaphore.release() } finally semaphore.release()
}, "GuardThread-" + cmd.head) }, "GuardThread-" + cmd.head)
@@ -105,16 +106,24 @@ class GuardedProcess(cmd: String*) {
def destroy() { def destroy() {
isDestroyed = true isDestroyed = true
guardThread.interrupt() guardThread.interrupt()
process.destroy() destroyProcess()
try guardThread.join() catch { try guardThread.join() catch {
case _: InterruptedException => case _: InterruptedException =>
} }
} }
private def destroyProcess() {
if (Build.VERSION.SDK_INT < 24) {
JniHelper.sigtermCompat(process)
JniHelper.waitForCompat(process, 500)
}
process.destroy()
}
def restart() { def restart() {
this.synchronized { this.synchronized {
isRestart = true isRestart = true
process.destroy() destroyProcess()
} }
} }
@@ -84,11 +84,6 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
private var txRateText: TextView = _ private var txRateText: TextView = _
private var rxRateText: TextView = _ private var rxRateText: TextView = _
private var currentFragment: ToolbarFragment = _
private lazy val profilesFragment = new ProfilesFragment()
private lazy val customRulesFragment = new CustomRulesFragment()
private lazy val globalSettingsFragment = new GlobalSettingsFragment()
private lazy val aboutFragment = new AboutFragment()
private lazy val customTabsIntent = new CustomTabsIntent.Builder() private lazy val customTabsIntent = new CustomTabsIntent.Builder()
.setToolbarColor(ContextCompat.getColor(this, R.color.material_primary_500)) .setToolbarColor(ContextCompat.getColor(this, R.color.material_primary_500))
.build() .build()
@@ -172,11 +167,11 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
private def addDisableNatToSnackbar(snackbar: Snackbar) = snackbar.setAction(R.string.switch_to_vpn, (_ => private def addDisableNatToSnackbar(snackbar: Snackbar) = snackbar.setAction(R.string.switch_to_vpn, (_ =>
if (state == State.STOPPED) app.editor.putBoolean(Key.isNAT, false).apply()): View.OnClickListener) if (state == State.STOPPED) app.editor.putBoolean(Key.isNAT, false).apply()): View.OnClickListener)
override def binderDied() { override def binderDied(): Unit = handler.post(() => {
detachService() detachService()
app.crashRecovery() app.crashRecovery()
attachService(callback) attachService(callback)
} })
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = resultCode match { override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = resultCode match {
case Activity.RESULT_OK => bgService.use(app.profileId) case Activity.RESULT_OK => bgService.use(app.profileId)
@@ -255,7 +250,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
val tf = Typefaces.get(this, "fonts/Iceland.ttf") val tf = Typefaces.get(this, "fonts/Iceland.ttf")
if (tf != null) title.setTypeface(tf) if (tf != null) title.setTypeface(tf)
if (savedInstanceState == null) displayFragment(profilesFragment) if (savedInstanceState == null) displayFragment(new ProfilesFragment)
statusText = findViewById(R.id.status).asInstanceOf[TextView] statusText = findViewById(R.id.status).asInstanceOf[TextView]
txText = findViewById(R.id.tx).asInstanceOf[TextView] txText = findViewById(R.id.tx).asInstanceOf[TextView]
txRateText = findViewById(R.id.txRate).asInstanceOf[TextView] txRateText = findViewById(R.id.txRate).asInstanceOf[TextView]
@@ -364,14 +359,13 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
} }
private def displayFragment(fragment: ToolbarFragment) { private def displayFragment(fragment: ToolbarFragment) {
currentFragment = fragment
getFragmentManager.beginTransaction().replace(R.id.fragment_holder, fragment).commitAllowingStateLoss() getFragmentManager.beginTransaction().replace(R.id.fragment_holder, fragment).commitAllowingStateLoss()
drawer.closeDrawer() drawer.closeDrawer()
} }
override def onItemClick(view: View, position: Int, drawerItem: IDrawerItem[_, _ <: ViewHolder]): Boolean = { override def onItemClick(view: View, position: Int, drawerItem: IDrawerItem[_, _ <: ViewHolder]): Boolean = {
drawerItem.getIdentifier match { drawerItem.getIdentifier match {
case DRAWER_PROFILES => displayFragment(profilesFragment) case DRAWER_PROFILES => displayFragment(new ProfilesFragment)
case DRAWER_RECOVERY => case DRAWER_RECOVERY =>
app.track("GlobalConfigFragment", "reset") app.track("GlobalConfigFragment", "reset")
if (bgService != null) bgService.use(-1) if (bgService != null) bgService.use(-1)
@@ -384,12 +378,12 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
app.copyAssets() app.copyAssets()
handler.sendEmptyMessage(0) handler.sendEmptyMessage(0)
} }
case DRAWER_GLOBAL_SETTINGS => displayFragment(globalSettingsFragment) case DRAWER_GLOBAL_SETTINGS => displayFragment(new GlobalSettingsFragment)
case DRAWER_ABOUT => case DRAWER_ABOUT =>
app.track(TAG, "about") app.track(TAG, "about")
displayFragment(aboutFragment) displayFragment(new AboutFragment)
case DRAWER_FAQ => launchUrl(getString(R.string.faq_url)) case DRAWER_FAQ => launchUrl(getString(R.string.faq_url))
case DRAWER_CUSTOM_RULES => displayFragment(customRulesFragment) case DRAWER_CUSTOM_RULES => displayFragment(new CustomRulesFragment)
} }
true // unexpected cases will throw exception true // unexpected cases will throw exception
} }
@@ -408,12 +402,14 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
setListeningForBandwidth(true) setListeningForBandwidth(true)
} }
override def onBackPressed(): Unit = override def onBackPressed(): Unit = if (drawer.isDrawerOpen) drawer.closeDrawer() else {
if (drawer.isDrawerOpen) drawer.closeDrawer() else if (!currentFragment.onBackPressed()) val currentFragment = getFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
if (currentFragment != profilesFragment) { if (!currentFragment.onBackPressed())
displayFragment(profilesFragment) if (currentFragment.isInstanceOf[ProfilesFragment]) super.onBackPressed() else {
displayFragment(new ProfilesFragment)
drawer.setSelection(DRAWER_PROFILES) drawer.setSelection(DRAWER_PROFILES)
} else super.onBackPressed() }
}
override def onStop() { override def onStop() {
setListeningForBandwidth(false) setListeningForBandwidth(false)
@@ -358,7 +358,9 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
REQUEST_SCAN_QR_CODE) catch { REQUEST_SCAN_QR_CODE) catch {
case _: ActivityNotFoundException => case _: ActivityNotFoundException =>
Toast.makeText(getActivity, R.string.add_profile_scanner_not_installed, Toast.LENGTH_LONG).show() Toast.makeText(getActivity, R.string.add_profile_scanner_not_installed, Toast.LENGTH_LONG).show()
startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=tw.com.quickmark"))) try startActivity(new Intent(Intent.ACTION_VIEW, Uri.parse("market://details?id=tw.com.quickmark"))) catch {
case exc: ActivityNotFoundException => exc.printStackTrace()
}
} }
true true
case R.id.action_import => case R.id.action_import =>
@@ -197,15 +197,17 @@ class ShadowsocksApplication extends Application {
def copyAssets() { def copyAssets() {
val assetManager = getAssets val assetManager = getAssets
var files: Array[String] = null for (dir <- List("acl", "overture")) {
try files = assetManager.list("acl") catch { var files: Array[String] = null
case e: IOException => try files = assetManager.list(dir) catch {
Log.e(TAG, e.getMessage) case e: IOException =>
app.track(e) 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() editor.putInt(Key.currentVersionCode, BuildConfig.VERSION_CODE).apply()
} }
@@ -49,7 +49,7 @@ class ShadowsocksNatService extends BaseService {
var sslocalProcess: GuardedProcess = _ var sslocalProcess: GuardedProcess = _
var sstunnelProcess: GuardedProcess = _ var sstunnelProcess: GuardedProcess = _
var redsocksProcess: GuardedProcess = _ var redsocksProcess: GuardedProcess = _
var pdnsdProcess: GuardedProcess = _ var overtureProcess: GuardedProcess = _
var su: Shell.Interactive = _ var su: Shell.Interactive = _
def startShadowsocksDaemon() { def startShadowsocksDaemon() {
@@ -74,7 +74,7 @@ class ShadowsocksNatService extends BaseService {
"-t", "10", "-t", "10",
"-b", "127.0.0.1", "-b", "127.0.0.1",
"-l", (profile.localPort + 63).toString, "-l", (profile.localPort + 63).toString,
"-L", if (profile.remoteDns == null) "8.8.8.8:53" else profile.remoteDns + ":53", "-L", profile.remoteDns.trim + ":53",
"-c", buildShadowsocksConfig("ss-tunnel-nat.conf")) "-c", buildShadowsocksConfig("ss-tunnel-nat.conf"))
if (profile.udpdns) cmd.append("-u") if (profile.udpdns) cmd.append("-u")
@@ -83,21 +83,8 @@ class ShadowsocksNatService extends BaseService {
} }
def startDnsDaemon() { def startDnsDaemon() {
val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0" overtureProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/liboverture.so",
IOUtils.writeString(new File(getFilesDir, "pdnsd-nat.conf"), profile.route match { "-c", buildOvertureConfig("overture-nat.conf"))
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")
.start() .start()
} }
@@ -111,11 +98,13 @@ class ShadowsocksNatService extends BaseService {
/** Called when the activity is first created. */ /** Called when the activity is first created. */
def handleConnection() { def handleConnection() {
startDNSTunnel()
startRedsocksDaemon() startRedsocksDaemon()
startShadowsocksDaemon() startShadowsocksDaemon()
if (!profile.udpdns) startDnsDaemon() if (!profile.udpdns)
startDnsDaemon()
else
startDNSTunnel()
setupIptables() setupIptables()
@@ -143,9 +132,9 @@ class ShadowsocksNatService extends BaseService {
redsocksProcess.destroy() redsocksProcess.destroy()
redsocksProcess = null redsocksProcess = null
} }
if (pdnsdProcess != null) { if (overtureProcess != null) {
pdnsdProcess.destroy() overtureProcess.destroy()
pdnsdProcess = null overtureProcess = null
} }
su.addCommand("iptables -t nat -F OUTPUT") su.addCommand("iptables -t nat -F OUTPUT")
@@ -23,14 +23,13 @@ package com.github.shadowsocks
import java.io.File import java.io.File
import java.util.Locale import java.util.Locale
import android.annotation.SuppressLint
import android.content._ import android.content._
import android.content.pm.PackageManager.NameNotFoundException import android.content.pm.PackageManager.NameNotFoundException
import android.net.VpnService import android.net.VpnService
import android.os._ import android.os._
import android.util.Log import android.util.Log
import com.github.shadowsocks.ShadowsocksApplication.app import com.github.shadowsocks.ShadowsocksApplication.app
import com.github.shadowsocks.acl.{Acl, AclSyncJob} import com.github.shadowsocks.acl.{Acl, AclSyncJob, Subnet}
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.utils._ import com.github.shadowsocks.utils._
@@ -46,8 +45,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
private var notification: ShadowsocksNotification = _ private var notification: ShadowsocksNotification = _
var sslocalProcess: GuardedProcess = _ var sslocalProcess: GuardedProcess = _
var sstunnelProcess: GuardedProcess = _ var overtureProcess: GuardedProcess = _
var pdnsdProcess: GuardedProcess = _
var tun2socksProcess: GuardedProcess = _ var tun2socksProcess: GuardedProcess = _
override def onBind(intent: Intent): IBinder = { override def onBind(intent: Intent): IBinder = {
@@ -95,17 +93,13 @@ class ShadowsocksVpnService extends VpnService with BaseService {
sslocalProcess.destroy() sslocalProcess.destroy()
sslocalProcess = null sslocalProcess = null
} }
if (sstunnelProcess != null) {
sstunnelProcess.destroy()
sstunnelProcess = null
}
if (tun2socksProcess != null) { if (tun2socksProcess != null) {
tun2socksProcess.destroy() tun2socksProcess.destroy()
tun2socksProcess = null tun2socksProcess = null
} }
if (pdnsdProcess != null) { if (overtureProcess != null) {
pdnsdProcess.destroy() overtureProcess.destroy()
pdnsdProcess = null overtureProcess = null
} }
} }
@@ -150,15 +144,14 @@ class ShadowsocksVpnService extends VpnService with BaseService {
/** Called when the activity is first created. */ /** Called when the activity is first created. */
def handleConnection() { def handleConnection() {
val fd = startVpn()
if (!sendFd(fd)) throw new Exception("sendFd failed")
startShadowsocksDaemon() startShadowsocksDaemon()
if (!profile.udpdns) { if (!profile.udpdns) {
startDnsDaemon() startDnsDaemon()
startDnsTunnel()
} }
val fd = startVpn()
if (!sendFd(fd)) throw new Exception("sendFd failed")
} }
override protected def buildPluginCommandLine(): ArrayBuffer[String] = super.buildPluginCommandLine() += "-V" override protected def buildPluginCommandLine(): ArrayBuffer[String] = super.buildPluginCommandLine() += "-V"
@@ -183,36 +176,12 @@ class ShadowsocksVpnService extends VpnService with BaseService {
sslocalProcess = new GuardedProcess(cmd: _*).start() 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" , if (profile.remoteDns == null) "8.8.8.8:53" else profile.remoteDns + ":53",
"-c", buildShadowsocksConfig("ss-tunnel-vpn.conf")).start()
def startDnsDaemon() { def startDnsDaemon() {
val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0" overtureProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/liboverture.so",
IOUtils.writeString(new File(getFilesDir, "pdnsd-vpn.conf"), profile.route match { "-c", buildOvertureConfig("overture-vpn.conf"), "-V")
case Acl.BYPASS_CHN | Acl.BYPASS_LAN_CHN | Acl.GFWLIST | Acl.CUSTOM_RULES => .start()
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()
} }
@SuppressLint(Array("NewApi"))
def startVpn(): Int = { def startVpn(): Int = {
val builder = new Builder() val builder = new Builder()
@@ -221,7 +190,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
.setMtu(VPN_MTU) .setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24) .addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
builder.addDnsServer("8.8.8.8") builder.addDnsServer(profile.remoteDns.trim)
if (profile.ipv6) { if (profile.ipv6) {
builder.addAddress(PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "1"), 126) builder.addAddress(PRIVATE_VLAN6.formatLocal(Locale.ENGLISH, "1"), 126)
@@ -249,15 +218,13 @@ class ShadowsocksVpnService extends VpnService with BaseService {
if (profile.route == Acl.ALL || profile.route == Acl.BYPASS_CHN) { if (profile.route == Acl.ALL || profile.route == Acl.BYPASS_CHN) {
builder.addRoute("0.0.0.0", 0) builder.addRoute("0.0.0.0", 0)
} else { } else {
val privateList = getResources.getStringArray(R.array.bypass_private_route) getResources.getStringArray(R.array.bypass_private_route).foreach(cidr => {
privateList.foreach(cidr => { val subnet = Subnet.fromString(cidr)
val addr = cidr.split('/') builder.addRoute(subnet.address.getHostAddress, subnet.prefixSize)
builder.addRoute(addr(0), addr(1).toInt)
}) })
builder.addRoute(profile.remoteDns.trim, 32)
} }
builder.addRoute("8.8.0.0", 16)
conn = builder.establish() conn = builder.establish()
if (conn == null) throw new NullConnectionException if (conn == null) throw new NullConnectionException
@@ -291,7 +258,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
var tries = 1 var tries = 1
while (tries < 5) { while (tries < 5) {
Thread.sleep(1000 * tries) Thread.sleep(1000 * tries)
if (System.sendfd(fd, new File(getFilesDir, "sock_path").getAbsolutePath) != -1) { if (JniHelper.sendFd(fd, new File(getFilesDir, "sock_path").getAbsolutePath) != -1) {
return true return true
} }
tries += 1 tries += 1
@@ -92,7 +92,7 @@ class ShadowsocksVpnThread(service: ShadowsocksVpnService) extends Thread {
val ret = service.protect(fd) val ret = service.protect(fd)
// Trick to close file decriptor // Trick to close file decriptor
System.jniclose(fd) JniHelper.close(fd)
if (ret) { if (ret) {
output.write(0) output.write(0)
@@ -36,7 +36,7 @@ object AclSyncJob {
final val TAG = "AclSyncJob" final val TAG = "AclSyncJob"
def schedule(route: String): Int = new JobRequest.Builder(AclSyncJob.TAG + ':' + route) def schedule(route: String): Int = new JobRequest.Builder(AclSyncJob.TAG + ':' + route)
.setExecutionWindow(1, TimeUnit.DAYS.toMillis(28)) .setExecutionWindow(TimeUnit.SECONDS.toMillis(10), TimeUnit.DAYS.toMillis(28))
.setRequirementsEnforced(true) .setRequirementsEnforced(true)
.setRequiredNetworkType(JobRequest.NetworkType.UNMETERED) .setRequiredNetworkType(JobRequest.NetworkType.UNMETERED)
.setRequiresCharging(true) .setRequiresCharging(true)
@@ -20,6 +20,7 @@ import com.github.shadowsocks.widget.UndoSnackbarManager
import com.github.shadowsocks.{MainActivity, R, ToolbarFragment} import com.github.shadowsocks.{MainActivity, R, ToolbarFragment}
import scala.collection.mutable import scala.collection.mutable
import scala.io.Source
/** /**
* @author Mygod * @author Mygod
@@ -144,8 +145,12 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
apply() apply()
index index
} else -1 } else -1
def addToProxy(input: String): Int = try addSubnet(Subnet.fromString(input)) catch { def addToProxy(input: String): Int = {
case _: IllegalArgumentException => addHostname(input) val acl = new Acl().fromSource(Source.fromString(input), defaultBypass = true)
var result = -1
for (hostname <- acl.proxyHostnames) result = addHostname(hostname)
if (acl.bypass) for (subnet <- acl.subnets) result = addSubnet(subnet)
result
} }
def addFromTemplate(template: Int, text: CharSequence): Int = template match { def addFromTemplate(template: Int, text: CharSequence): Int = template match {
case GENERIC => addToProxy(text.toString) case GENERIC => addToProxy(text.toString)
@@ -298,13 +303,7 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
.create().show() .create().show()
true true
case R.id.action_import => case R.id.action_import =>
try { try if (adapter.addToProxy(clipboard.getPrimaryClip.getItemAt(0).getText.toString) != -1) return true catch {
val items = clipboard.getPrimaryClip.getItemAt(0).getText.toString.split("\n")
if (items.nonEmpty) {
items.foreach(adapter.addToProxy)
return true
}
} catch {
case _: Exception => case _: Exception =>
} }
Snackbar.make(getActivity.findViewById(R.id.snackbar), R.string.action_import_err, Snackbar.LENGTH_LONG).show() Snackbar.make(getActivity.findViewById(R.id.snackbar), R.string.action_import_err, Snackbar.LENGTH_LONG).show()
@@ -1,6 +1,7 @@
package com.github.shadowsocks.acl package com.github.shadowsocks.acl
import java.net.{Inet4Address, Inet6Address, InetAddress} import java.net.InetAddress
import java.util.Objects
import com.github.shadowsocks.utils.Utils import com.github.shadowsocks.utils.Utils
@@ -9,16 +10,11 @@ import com.github.shadowsocks.utils.Utils
*/ */
@throws[IllegalArgumentException] @throws[IllegalArgumentException]
class Subnet(val address: InetAddress, val prefixSize: Int) extends Comparable[Subnet] { class Subnet(val address: InetAddress, val prefixSize: Int) extends Comparable[Subnet] {
if (prefixSize < 0) throw new IllegalArgumentException private def addressLength = address.getAddress.length << 3
address match { if (prefixSize < 0 || prefixSize > addressLength) throw new IllegalArgumentException
case _: Inet4Address => if (prefixSize > 32) throw new IllegalArgumentException
case _: Inet6Address => if (prefixSize > 128) throw new IllegalArgumentException
}
override def toString: String = if (address match { override def toString: String =
case _: Inet4Address => prefixSize == 32 if (prefixSize == addressLength) address.getHostAddress else address.getHostAddress + '/' + prefixSize
case _: Inet6Address => prefixSize == 128
}) address.getHostAddress else address.getHostAddress + '/' + prefixSize
override def compareTo(that: Subnet): Int = { override def compareTo(that: Subnet): Int = {
val addrThis = address.getAddress val addrThis = address.getAddress
@@ -31,19 +27,21 @@ class Subnet(val address: InetAddress, val prefixSize: Int) extends Comparable[S
} }
prefixSize compare that.prefixSize prefixSize compare that.prefixSize
} }
override def equals(other: Any): Boolean = other match {
case that: Subnet => address == that.address && prefixSize == that.prefixSize
case _ => false
}
override def hashCode: Int = Objects.hash(address, prefixSize: Integer)
} }
object Subnet { object Subnet {
@throws[IllegalArgumentException] @throws[IllegalArgumentException]
def fromString(value: String): Subnet = { def fromString(value: String): Subnet = {
val parts = value.split("/") val parts = value.split("/")
if (!Utils.isNumeric(parts(0))) throw new IllegalArgumentException() val addr = Utils.parseNumericAddress(parts(0))
val addr = InetAddress.getByName(parts(0))
parts.length match { parts.length match {
case 1 => new Subnet(addr, addr match { case 1 => new Subnet(addr, addr.getAddress.length << 3)
case _: Inet4Address => 32
case _: Inet6Address => 128
})
case 2 => new Subnet(addr, parts(1).toInt) case 2 => new Subnet(addr, parts(1).toInt)
case _ => throw new IllegalArgumentException() case _ => throw new IllegalArgumentException()
} }
@@ -77,9 +77,6 @@ class DBHelper(val context: Context)
} else if (oldVersion < 19) { } else if (oldVersion < 19) {
profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'all' WHERE route IS NULL;") profileDao.executeRawNoArgs("UPDATE `profile` SET route = 'all' WHERE route IS NULL;")
} }
if (oldVersion < 10) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN auth SMALLINT;")
}
if (oldVersion < 11) { if (oldVersion < 11) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;") profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
} }
@@ -89,9 +86,9 @@ class DBHelper(val context: Context)
TableUtils.createTable(connectionSource, classOf[Profile]) TableUtils.createTable(connectionSource, classOf[Profile])
profileDao.executeRawNoArgs( profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," + "INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
" udpdns, auth, ipv6, individual) " + " udpdns, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, auth," + "SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, ipv6," +
" ipv6, individual FROM `tmp`;") " individual FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;") profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
profileDao.executeRawNoArgs("COMMIT;") profileDao.executeRawNoArgs("COMMIT;")
} else if (oldVersion < 13) { } else if (oldVersion < 13) {
@@ -127,10 +124,10 @@ class DBHelper(val context: Context)
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;") profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
TableUtils.createTable(connectionSource, classOf[Profile]) TableUtils.createTable(connectionSource, classOf[Profile])
profileDao.executeRawNoArgs( profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, remoteDns, proxyApps," + "INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, remoteDns, " +
" bypass, udpdns, auth, ipv6, individual, tx, rx, date, userOrder, plugin) " + "proxyApps, bypass, udpdns, ipv6, individual, tx, rx, date, userOrder, plugin) " +
"SELECT id, name, host, localPort, CASE WHEN kcp = 1 THEN kcpPort ELSE remotePort END, password, method, " + "SELECT id, name, host, localPort, CASE WHEN kcp = 1 THEN kcpPort ELSE remotePort END, password, method, " +
"route, remoteDns, proxyApps, bypass, udpdns, auth, ipv6, individual, tx, rx, date, userOrder, " + "route, remoteDns, proxyApps, bypass, udpdns, ipv6, individual, tx, rx, date, userOrder, " +
"CASE WHEN kcp = 1 THEN 'kcptun ' || kcpcli ELSE NULL END FROM `tmp`;") "CASE WHEN kcp = 1 THEN 'kcptun ' || kcpcli ELSE NULL END FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;") profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
profileDao.executeRawNoArgs("COMMIT;") profileDao.executeRawNoArgs("COMMIT;")
@@ -20,13 +20,13 @@
package com.github.shadowsocks.database package com.github.shadowsocks.database
import java.net.URLEncoder
import java.util.Locale import java.util.Locale
import android.content.SharedPreferences import android.content.SharedPreferences
import android.net.Uri
import android.os.Binder import android.os.Binder
import android.text.TextUtils
import android.util.Base64 import android.util.Base64
import com.github.shadowsocks.plugin.PluginConfiguration
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.j256.ormlite.field.{DataType, DatabaseField} import com.j256.ormlite.field.{DataType, DatabaseField}
@@ -68,9 +68,6 @@ class Profile {
@DatabaseField @DatabaseField
var udpdns: Boolean = _ var udpdns: Boolean = _
@DatabaseField
var auth: Boolean = _
@DatabaseField @DatabaseField
var ipv6: Boolean = _ var ipv6: Boolean = _
@@ -96,11 +93,19 @@ class Profile {
def nameIsEmpty: Boolean = name == null || name.isEmpty def nameIsEmpty: Boolean = name == null || name.isEmpty
def getName: String = if (nameIsEmpty) formattedAddress else name def getName: String = if (nameIsEmpty) formattedAddress else name
override def toString: String = "ss://" + Base64.encodeToString("%s%s:%s@%s:%d".formatLocal(Locale.ENGLISH, def toUri: Uri = {
method, if (auth) "-auth" else "", password, host, remotePort).getBytes, Base64.NO_PADDING | Base64.NO_WRAP) + val builder = new Uri.Builder()
(if (TextUtils.isEmpty(name)) "" else '#' + URLEncoder.encode(name, "utf-8")) .scheme("ss")
.encodedAuthority("%s@%s:%d".formatLocal(Locale.ENGLISH,
def isMethodUnsafe: Boolean = "table".equalsIgnoreCase(method) || "rc4".equalsIgnoreCase(method) Base64.encodeToString("%s:%s".formatLocal(Locale.ENGLISH, method, password).getBytes,
Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE), host, remotePort))
val configuration = new PluginConfiguration(plugin)
if (configuration.selected.nonEmpty)
builder.appendQueryParameter(Key.plugin, configuration.selectedOptions.toString(false))
if (!nameIsEmpty) builder.fragment(name)
builder.build()
}
override def toString: String = toUri.toString
def serialize(editor: SharedPreferences.Editor): SharedPreferences.Editor = editor def serialize(editor: SharedPreferences.Editor): SharedPreferences.Editor = editor
.putString(Key.name, name) .putString(Key.name, name)
@@ -114,7 +119,6 @@ class Profile {
.putBoolean(Key.proxyApps, proxyApps) .putBoolean(Key.proxyApps, proxyApps)
.putBoolean(Key.bypass, bypass) .putBoolean(Key.bypass, bypass)
.putBoolean(Key.udpdns, udpdns) .putBoolean(Key.udpdns, udpdns)
.putBoolean(Key.auth, auth)
.putBoolean(Key.ipv6, ipv6) .putBoolean(Key.ipv6, ipv6)
.putString(Key.individual, individual) .putString(Key.individual, individual)
.putString(Key.plugin, plugin) .putString(Key.plugin, plugin)
@@ -132,7 +136,6 @@ class Profile {
proxyApps = pref.getBoolean(Key.proxyApps, false) proxyApps = pref.getBoolean(Key.proxyApps, false)
bypass = pref.getBoolean(Key.bypass, false) bypass = pref.getBoolean(Key.bypass, false)
udpdns = pref.getBoolean(Key.udpdns, false) udpdns = pref.getBoolean(Key.udpdns, false)
auth = pref.getBoolean(Key.auth, false)
ipv6 = pref.getBoolean(Key.ipv6, false) ipv6 = pref.getBoolean(Key.ipv6, false)
individual = pref.getString(Key.individual, null) individual = pref.getString(Key.individual, null)
plugin = pref.getString(Key.plugin, null) plugin = pref.getString(Key.plugin, null)
@@ -50,6 +50,20 @@ object PluginManager {
|RSo6EooQ7+NBejOXysqIF1q0BJs8Y5s/CaTOmgbL7uPCkzArB6SS/hzXgDk5gw6v |RSo6EooQ7+NBejOXysqIF1q0BJs8Y5s/CaTOmgbL7uPCkzArB6SS/hzXgDk5gw6v
|wkGeOtzcj1DlbUTvt1s5GlnwBTGUmkbLx+YUje+n+IBgMbohLUDYBtUHylRVgMsc |wkGeOtzcj1DlbUTvt1s5GlnwBTGUmkbLx+YUje+n+IBgMbohLUDYBtUHylRVgMsc
|1WS67kDqeJiiQZvrxvyW6CZZ/MIGI+uAkkj3DqJpaZirkwPgvpcOIrjZy0uFvQM= |1WS67kDqeJiiQZvrxvyW6CZZ/MIGI+uAkkj3DqJpaZirkwPgvpcOIrjZy0uFvQM=
""", Base64.DEFAULT)) +
new Signature(Base64.decode( // @madeye
"""
|MIICQzCCAaygAwIBAgIETV9OhjANBgkqhkiG9w0BAQUFADBmMQswCQYDVQQGEwJjbjERMA8GA1UE
|CBMIU2hhbmdoYWkxDzANBgNVBAcTBlB1ZG9uZzEUMBIGA1UEChMLRnVkYW4gVW5pdi4xDDAKBgNV
|BAsTA1BQSTEPMA0GA1UEAxMGTWF4IEx2MB4XDTExMDIxOTA1MDA1NFoXDTM2MDIxMzA1MDA1NFow
|ZjELMAkGA1UEBhMCY24xETAPBgNVBAgTCFNoYW5naGFpMQ8wDQYDVQQHEwZQdWRvbmcxFDASBgNV
|BAoTC0Z1ZGFuIFVuaXYuMQwwCgYDVQQLEwNQUEkxDzANBgNVBAMTBk1heCBMdjCBnzANBgkqhkiG
|9w0BAQEFAAOBjQAwgYkCgYEAq6lA8LqdeEI+es9SDX85aIcx8LoL3cc//iRRi+2mFIWvzvZ+bLKr
|4Wd0rhu/iU7OeMm2GvySFyw/GdMh1bqh5nNPLiRxAlZxpaZxLOdRcxuvh5Nc5yzjM+QBv8ECmuvu
|AOvvT3UDmA0AMQjZqSCmxWIxc/cClZ/0DubreBo2st0CAwEAATANBgkqhkiG9w0BAQUFAAOBgQAQ
|Iqonxpwk2ay+Dm5RhFfZyG9SatM/JNFx2OdErU16WzuK1ItotXGVJaxCZv3u/tTwM5aaMACGED5n
|AvHaDGCWynY74oDAopM4liF/yLe1wmZDu6Zo/7fXrH+T03LBgj2fcIkUfN1AA4dvnBo8XWAm9VrI
|1iNuLIssdhDz3IL9Yg==
""", Base64.DEFAULT)) """, Base64.DEFAULT))
private var receiver: BroadcastReceiver = _ private var receiver: BroadcastReceiver = _
@@ -45,86 +45,6 @@ object ConfigUtils {
" port = %d;\n" + " port = %d;\n" +
" type = socks5;\n" + " type = socks5;\n" +
"}\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 { object Key {
@@ -141,7 +61,6 @@ object Key {
val proxyApps = "isProxyApps" val proxyApps = "isProxyApps"
val bypass = "isBypassApps" val bypass = "isBypassApps"
val udpdns = "isUdpDns" val udpdns = "isUdpDns"
val auth = "isAuth"
val ipv6 = "isIpv6" val ipv6 = "isIpv6"
val host = "proxy" val host = "proxy"
@@ -20,32 +20,49 @@
package com.github.shadowsocks.utils package com.github.shadowsocks.utils
import java.net.URLDecoder import android.net.Uri
import android.util.{Base64, Log} import android.util.{Base64, Log}
import com.github.shadowsocks.database.Profile import com.github.shadowsocks.database.Profile
object Parser { object Parser {
val TAG = "ShadowParser" val TAG = "ShadowParser"
private val pattern = "(?i)ss://([A-Za-z0-9+-/=_]+)(#(.+))?".r private val pattern = "(?i)ss://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]".r
private val decodedPattern = "(?i)^((.+?)(-auth)??:(.*)@(.+?):(\\d+?))$".r private val userInfoPattern = "^(.+?):(.*)$".r
private val legacyPattern = "^((.+?):(.*)@(.+?):(\\d+?))$".r
def findAll(data: CharSequence): Iterator[Profile] = def findAll(data: CharSequence): Iterator[Profile] =
pattern.findAllMatchIn(if (data == null) "" else data).map(m => try pattern.findAllMatchIn(if (data == null) "" else data).map(m => {
decodedPattern.findFirstMatchIn(new String(Base64.decode(m.group(1), Base64.NO_PADDING), "UTF-8")) match { val uri = Uri.parse(m.matched)
case Some(ss) => uri.getUserInfo match {
val profile = new Profile case null => new String(Base64.decode(uri.getHost, Base64.NO_PADDING), "UTF-8") match {
profile.method = ss.group(2).toLowerCase case legacyPattern(_, method, password, host, port) => // legacy uri
if (ss.group(3) != null) profile.auth = true val profile = new Profile
profile.password = ss.group(4) profile.method = method.toLowerCase
profile.host = ss.group(5) profile.password = password
profile.remotePort = ss.group(6).toInt profile.host = host
if (m.group(2) != null) profile.name = URLDecoder.decode(m.group(3), "utf-8") profile.remotePort = port.toInt
profile profile.plugin = uri.getQueryParameter(Key.plugin)
case _ => null profile.name = uri.getFragment
} catch { profile
case ex: Exception => case _ =>
Log.e(TAG, "parser error: " + m.source, ex)// Ignore Log.e(TAG, "Unrecognized URI: " + m.matched)
null null
}).filter(_ != null) }
case userInfo =>
new String(Base64.decode(userInfo, Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE), "UTF-8") match {
case userInfoPattern(method, password) =>
val profile = new Profile
profile.method = method
profile.password = password
profile.host = uri.getHost
profile.remotePort = uri.getPort
profile.plugin = uri.getQueryParameter(Key.plugin)
profile.name = uri.getFragment
profile
case _ =>
Log.e(TAG, "Unknown user info: " + m.matched)
null
}
}
}).filter(_ != null)
} }
@@ -45,7 +45,7 @@ object TrafficMonitor {
def formatTraffic(size: Long): String = { def formatTraffic(size: Long): String = {
var n: Double = size var n: Double = size
var i = -1 var i = -1
while (n >= 1000) { while (n >= 999.5) {
n /= 1024 n /= 1024
i = i + 1 i = i + 1
} }
@@ -20,6 +20,7 @@
package com.github.shadowsocks.utils package com.github.shadowsocks.utils
import java.lang.reflect.InvocationTargetException
import java.net._ import java.net._
import java.security.MessageDigest import java.security.MessageDigest
@@ -108,7 +109,15 @@ object Utils {
.orElse(resolve(host)) .orElse(resolve(host))
private lazy val isNumericMethod = classOf[InetAddress].getMethod("isNumeric", classOf[String]) private lazy val isNumericMethod = classOf[InetAddress].getMethod("isNumeric", classOf[String])
private lazy val parseNumericAddressMethod = classOf[InetAddress].getMethod("parseNumericAddress", classOf[String])
def isNumeric(address: String): Boolean = isNumericMethod.invoke(null, address).asInstanceOf[Boolean] def isNumeric(address: String): Boolean = isNumericMethod.invoke(null, address).asInstanceOf[Boolean]
def parseNumericAddress(address: String): InetAddress =
try parseNumericAddressMethod.invoke(null, address).asInstanceOf[InetAddress] catch {
case exc: InvocationTargetException => throw exc.getCause match {
case null => exc
case cause => cause
}
}
/** /**
* If there exists a valid IPv6 interface * If there exists a valid IPv6 interface
Submodule mobile/src/overture added at 5cb3f8c9e1
+6
View File
@@ -0,0 +1,6 @@
* 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.
* 0.0.1: Initial release.
+4 -5
View File
@@ -1,5 +1,7 @@
# shadowsocks-android plugin framework # shadowsocks-android plugin framework
[Documentation](doc.md) | [Change log](CHANGES.md)
Support library for easier development on [shadowsocks Support library for easier development on [shadowsocks
plugin](https://github.com/shadowsocks/shadowsocks-org/issues/28) for Android. Also includes some plugin](https://github.com/shadowsocks/shadowsocks-org/issues/28) for Android. Also includes some
useful resources to easily get consistent styling with the main app. useful resources to easily get consistent styling with the main app.
@@ -19,9 +21,6 @@ This library is designed with Java interoperability in mind so theoretically you
library with other languages and/or build tools but there isn't documentation for that yet. This library with other languages and/or build tools but there isn't documentation for that yet. This
guide is written for Scala + SBT. Contributions are welcome. guide is written for Scala + SBT. Contributions are welcome.
The following is just a simple guide to get you started, for more technical details, see
[doc.md](https://github.com/shadowsocks/shadowsocks-android/blob/develop/plugin/doc.md).
### Package name ### Package name
There are no arbitrary restrictions/requirements on package name, component name and content There are no arbitrary restrictions/requirements on package name, component name and content
@@ -37,7 +36,7 @@ First you need to add this library to your dependencies. This library is written
and it's most convenient to use it with SBT: and it's most convenient to use it with SBT:
```scala ```scala
libraryDependencies += "com.github.shadowsocks" %% "plugin" % "0.0.1-SNAPSHOT" libraryDependencies += "com.github.shadowsocks" %% "plugin" % "0.0.2"
``` ```
### Native binary configuration ### Native binary configuration
@@ -88,7 +87,7 @@ Then add it to your manifest:
... ...
<provider android:name=".BinaryProvider" <provider android:name=".BinaryProvider"
android:exported="true" android:exported="true"
android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID"> android:authorities="$FULLY_QUALIFIED_NAME_OF_YOUR_CONTENTPROVIDER">
<intent-filter> <intent-filter>
<action android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"/> <action android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"/>
</intent-filter> </intent-filter>
+28 -1
View File
@@ -2,4 +2,31 @@ enablePlugins(AndroidLib)
android.useSupportVectors android.useSupportVectors
name := "plugin" name := "plugin"
version := "0.0.1" version := "0.0.3"
pomExtra in Global := {
<url>https://github.com/shadowsocks/shadowsocks-android</url>
<licenses>
<license>
<name>GPLv3</name>
<url>https://www.gnu.org/licenses/gpl-3.0.html</url>
</license>
</licenses>
<scm>
<connection>scm:git:git://github.com/shadowsocks/shadowsocks-android.git</connection>
<developerConnection>scm:git:git@github.com:shadowsocks/shadowsocks-android.git</developerConnection>
<url>github.com/shadowsocks/shadowsocks-android</url>
</scm>
<developers>
<developer>
<id>madeye</id>
<name>Max Lv</name>
<url>https://github.com/madeye</url>
</developer>
<developer>
<id>Mygod</id>
<name>Mygod Studio</name>
<url>https://github.com/Mygod</url>
</developer>
</developers>
}
+3 -3
View File
@@ -153,7 +153,7 @@ This corresponds to `com.github.shadowsocks.plugin.NativePluginProvider` in the
... ...
<provider android:name=".BinaryProvider" <provider android:name=".BinaryProvider"
android:exported="true" android:exported="true"
android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID"> android:authorities="$FULLY_QUALIFIED_NAME_OF_YOUR_CONTENTPROVIDER">
<intent-filter> <intent-filter>
<action android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"/> <action android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"/>
</intent-filter> </intent-filter>
@@ -196,7 +196,7 @@ This feature hasn't been implemented yet.
Plugins are certified using package signatures and shadowsocks-android will consider these Plugins are certified using package signatures and shadowsocks-android will consider these
signatures as trusted: signatures as trusted:
* Signatures by [trusted sources](https://github.com/shadowsocks/shadowsocks-android/blob/develop/mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala#L22) * Signatures by [trusted sources](/mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala#L22)
which includes: which includes:
- @madeye, i.e. the signer of the main repo; - @madeye, i.e. the signer of the main repo;
- The main repo doesn't contain any other trusted signatures. Third-party forks should add their - The main repo doesn't contain any other trusted signatures. Third-party forks should add their
@@ -226,5 +226,5 @@ Plugin app must include this in their application tag: (which should be automati
``` ```
<meta-data android:name="com.github.shadowsocks.plugin.version" <meta-data android:name="com.github.shadowsocks.plugin.version"
android:value="0.0.1"/> android:value="0.0.2"/>
``` ```
+1 -1
View File
@@ -6,6 +6,6 @@
<application <application
android:theme="@style/Theme.Shadowsocks"> android:theme="@style/Theme.Shadowsocks">
<meta-data android:name="com.github.shadowsocks.plugin.version" <meta-data android:name="com.github.shadowsocks.plugin.version"
android:value="0.0.1"/> android:value="0.0.2"/>
</application> </application>
</manifest> </manifest>
@@ -24,6 +24,14 @@ public final class PluginOptions extends HashMap<String, String> {
super(initialCapacity, loadFactor); super(initialCapacity, loadFactor);
} }
// TODO: this method is not needed since API 24
public String getOrDefault(Object key, String defaultValue) {
String v;
return (((v = get(key)) != null) || containsKey(key))
? v
: defaultValue;
}
private PluginOptions(String options, boolean parseId) { private PluginOptions(String options, boolean parseId) {
this(); this();
if (TextUtils.isEmpty(options)) return; if (TextUtils.isEmpty(options)) return;
@@ -15,7 +15,7 @@ import android.os.{Bundle, ParcelFileDescriptor}
* &lt;application&gt; * &lt;application&gt;
* ... * ...
* &lt;provider android:name="com.github.shadowsocks.$PLUGIN_ID.BinaryProvider" * &lt;provider android:name="com.github.shadowsocks.$PLUGIN_ID.BinaryProvider"
* android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID"&gt; * android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID.BinaryProvider"&gt;
* &lt;intent-filter&gt; * &lt;intent-filter&gt;
* &lt;category android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" /&gt; * &lt;category android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" /&gt;
* &lt;/intent-filter&gt; * &lt;/intent-filter&gt;
+1 -1
View File
@@ -1,4 +1,4 @@
addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.3") addSbtPlugin("org.scala-android" % "sbt-android" % "1.7.6")
addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10") addSbtPlugin("com.timushev.sbt" % "sbt-updates" % "0.1.10")
-23
View File
@@ -1,23 +0,0 @@
sonatypeProfileName := "com.github.shadowsocks"
pomExtra in Global := {
<url>https://github.com/shadowsocks/shadowsocks-android</url>
<licenses>
<license>
<name>GPLv3</name>
<url>https://www.gnu.org/licenses/gpl-3.0.html</url>
</license>
</licenses>
<scm>
<connection>scm:git:git://github.com/shadowsocks/shadowsocks-android.git</connection>
<developerConnection>scm:git:git@github.com:shadowsocks/shadowsocks-android.git</developerConnection>
<url>github.com/shadowsocks/shadowsocks-android</url>
</scm>
<developers>
<developer>
<id>madeye</id>
<name>Max Lv</name>
<url>https://github.com/madeye/</url>
</developer>
</developers>
}