Compare commits

...
82 Commits
Author SHA1 Message Date
Max Lv 60d7d90565 Fix a DNS issue with GFWList mode 2017-03-05 16:11:24 +08:00
Max Lv d2ba9493c0 Bump version 2017-03-04 10:26:43 +08:00
Max Lv 0eaece7af9 Remove external IP #1148 2017-03-04 09:56:52 +08:00
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 8017 additions and 621 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;
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?
+1
View File
@@ -9,3 +9,4 @@ local.sbt
mobile/src/main/libs
mobile/src/main/obj
mobile/src/main/jni/overture
+5 -5
View File
@@ -17,10 +17,6 @@
path = mobile/src/main/jni/redsocks
url = https://github.com/shadowsocks/redsocks.git
branch = shadowsocks-android
[submodule "mobile/src/main/jni/pdnsd"]
path = mobile/src/main/jni/pdnsd
url = https://github.com/shadowsocks/pdnsd.git
branch = shadowsocks-android
[submodule "mobile/src/main/jni/mbedtls"]
path = mobile/src/main/jni/mbedtls
url = https://github.com/ARMmbed/mbedtls
@@ -29,10 +25,14 @@
url = https://android.googlesource.com/platform/external/pcre
[submodule "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"]
path = mobile/src/main/jni/libev
url = https://github.com/shadowsocks/libev.git
[submodule "mobile/src/main/jni/libudns"]
path = mobile/src/main/jni/libudns
url = https://github.com/shadowsocks/libudns.git
[submodule "mobile/src/overture"]
path = mobile/src/overture
url = https://github.com/shadowsocks/overture-android.git
+2 -1
View File
@@ -7,6 +7,7 @@ env:
global:
- NDK_VERSION=r12b
- NDK_CCACHE=ccache
- GOROOT_BOOTSTRAP=$GOROOT
- ANDROID_NDK_HOME=$HOME/.android/android-ndk-${NDK_VERSION}
- SBTPATH=$HOME/.sbt
- PATH=${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools:${PATH}
@@ -49,4 +50,4 @@ install:
fi
script:
- $SBTPATH/sbt android:package
- $SBTPATH/sbt go-build android:package
+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>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>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>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>
+1 -1
View File
@@ -18,7 +18,7 @@ lazy val commonSettings = Seq(
typedResources := false
)
val supportLibsVersion = "25.1.0"
val supportLibsVersion = "25.2.0"
lazy val root = Project(id = "shadowsocks-android", base = file("."))
.settings(commonSettings)
.aggregate(plugin, mobile)
+18 -10
View File
@@ -2,11 +2,11 @@ enablePlugins(AndroidApp)
android.useSupportVectors
name := "shadowsocks"
version := "4.0.0"
versionCode := Some(175)
version := "4.1.1"
versionCode := Some(181)
proguardOptions ++=
"-keep class com.github.shadowsocks.System { *; }" ::
"-keep class com.github.shadowsocks.JniHelper { *; }" ::
"-dontwarn com.google.android.gms.internal.**" ::
"-dontwarn com.j256.ormlite.**" ::
"-dontwarn okio.**" ::
@@ -15,24 +15,32 @@ proguardOptions ++=
resConfigs := Seq("ja", "ru", "zh-rCN", "zh-rTW")
val playServicesVersion = "10.0.1"
val playServicesVersion = "10.2.0"
resolvers += Resolver.jcenterRepo
libraryDependencies ++=
"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.google.android.gms" % "play-services-ads" % playServicesVersion ::
"com.google.android.gms" % "play-services-analytics" % playServicesVersion ::
"com.google.android.gms" % "play-services-gcm" % playServicesVersion ::
"com.j256.ormlite" % "ormlite-android" % "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" % "materialdrawer" % "5.8.1" ::
"com.mikepenz" % "materialize" % "1.0.0" ::
"com.squareup.okhttp3" % "okhttp" % "3.5.0" ::
"com.mikepenz" % "materialdrawer" % "5.8.2" ::
"com.mikepenz" % "materialize" % "1.0.1" ::
"com.squareup.okhttp3" % "okhttp" % "3.6.0" ::
"com.twofortyfouram" % "android-plugin-api-for-locale" % "1.0.2" ::
"dnsjava" % "dnsjava" % "2.1.7" ::
"dnsjava" % "dnsjava" % "2.1.8" ::
"eu.chainfire" % "libsuperuser" % "1.0.0.201608240809" ::
"net.glxn.qrgen" % "android" % "2.0" ::
Nil
lazy val goBuild = TaskKey[Unit]("go-build", "Build go and kcptun")
goBuild := {
Process(Seq("mobile/src/overture/make.bash", minSdkVersion.value)) ! streams.value.log match {
case 0 => // Success!
case n => sys.error(s"Native build script exit code: $n")
}
}
+2
View File
@@ -144,6 +144,8 @@
android:process=":bg"/>
<service android:name="com.evernote.android.job.gcm.PlatformGcmService"
android:process=":bg"/>
<service android:name="com.evernote.android.job.JobRescheduleService"
android:process=":bg"/>
<receiver android:name="com.evernote.android.job.v14.PlatformAlarmReceiver"
android:process=":bg"/>
<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>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>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>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>
@@ -38,12 +38,35 @@
package com.github.shadowsocks;
public class System {
static {
java.lang.System.loadLibrary("system");
}
import android.os.Build;
import android.system.ErrnoException;
public static native int exec(String cmd);
public static native int sendfd(int fd, String path);
public static native void jniclose(int fd);
public class JniHelper {
static {
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 := \
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/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-load-sse2.h \
crypto_generichash/blake2/ref/blake2b-load-sse41.h \
crypto_generichash/blake2/ref/blake2b-ref.c \
crypto_generichash/blake2/ref/blake2b-round.h \
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.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_pwhash/scryptsalsa208sha256/crypto_scrypt-common.c \
crypto_pwhash/scryptsalsa208sha256/crypto_scrypt.h \
crypto_pwhash/scryptsalsa208sha256/scrypt_platform.c \
crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.c \
crypto_pwhash/scryptsalsa208sha256/pbkdf2-sha256.h \
crypto_pwhash/scryptsalsa208sha256/pwhash_scryptsalsa208sha256.c \
crypto_pwhash/scryptsalsa208sha256/sysendian.h \
crypto_pwhash/scryptsalsa208sha256/nosse/pwhash_scryptsalsa208sha256_nosse.c \
crypto_scalarmult/crypto_scalarmult.c \
crypto_pwhash/crypto_pwhash.c \
crypto_pwhash/argon2/argon2-core.c \
crypto_pwhash/argon2/argon2.c \
crypto_pwhash/argon2/argon2-encoding.c \
crypto_pwhash/argon2/argon2-fill-block-ref.c \
crypto_pwhash/argon2/blake2b-long.c \
crypto_pwhash/argon2/pwhash_argon2i.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.h \
crypto_stream/chacha20/ref/stream_chacha20_ref.h \
crypto_stream/chacha20/ref/stream_chacha20_ref.c \
crypto_stream/salsa20/stream_salsa20_api.c \
crypto_stream/xsalsa20/stream_xsalsa20_api.c \
crypto_stream/xsalsa20/ref/stream_xsalsa20.c \
crypto_stream/xsalsa20/ref/xor_xsalsa20.c \
crypto_verify/16/verify_16_api.c \
crypto_stream/salsa20/ref/stream_salsa20_ref.c \
crypto_stream/salsa20/ref/xor_salsa20_ref.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/sysrandom/randombytes_sysrandom.c \
sodium/core.c \
sodium/runtime.c \
sodium/utils.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_CFLAGS += -O2 -I$(LOCAL_PATH)/libsodium/src/libsodium/include \
-I$(LOCAL_PATH)/include \
@@ -200,6 +117,22 @@ LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE))
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
########################################################
@@ -306,32 +239,17 @@ LOCAL_CFLAGS := -O2 -std=gnu99 -DUSE_IPTABLES \
include $(BUILD_SHARED_EXECUTABLE)
########################################################
## pdnsd
########################################################
include $(CLEAR_VARS)
PDNSD_SOURCES := $(wildcard $(LOCAL_PATH)/pdnsd/src/*.c)
LOCAL_MODULE := pdnsd
LOCAL_SRC_FILES := $(PDNSD_SOURCES:$(LOCAL_PATH)/%=%)
LOCAL_CFLAGS := -DANDROID -Wall -O2 -I$(LOCAL_PATH)/pdnsd \
-I$(LOCAL_PATH)/include/pdnsd -I$(LOCAL_PATH)/libancillary
LOCAL_STATIC_LIBRARIES := libancillary
LOCAL_LDLIBS := -llog
include $(BUILD_SHARED_EXECUTABLE)
########################################################
## shadowsocks-libev local
########################################################
include $(CLEAR_VARS)
SHADOWSOCKS_SOURCES := local.c cache.c udprelay.c encrypt.c \
utils.c netutils.c json.c jconf.c acl.c http.c tls.c rule.c \
plugin.c \
SHADOWSOCKS_SOURCES := local.c \
cache.c udprelay.c utils.c netutils.c json.c jconf.c \
acl.c http.c tls.c rule.c \
crypto.c aead.c stream.c base64.c \
plugin.c ppbloom.c \
android.c
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)/pcre \
-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/sodium \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libipset/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libbloom \
-I$(LOCAL_PATH)/libev
LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libudns \
LOCAL_STATIC_LIBRARIES := libev libmbedtls libipset libcork libbloom libudns \
libsodium libancillary libpcre
LOCAL_LDLIBS := -llog
@@ -364,7 +283,11 @@ include $(BUILD_SHARED_EXECUTABLE)
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_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)/include \
-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/sodium \
-I$(LOCAL_PATH)/mbedtls/include \
-I$(LOCAL_PATH)/libev \
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
-I$(LOCAL_PATH)/shadowsocks-libev/libbloom \
-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
include $(BUILD_SHARED_EXECUTABLE)
########################################################
## system
## jni-helper
########################################################
include $(CLEAR_VARS)
LOCAL_MODULE:= system
LOCAL_MODULE:= jni-helper
LOCAL_C_INCLUDES:= $(LOCAL_PATH)/libancillary
LOCAL_SRC_FILES:= system.cpp
LOCAL_SRC_FILES:= jni-helper.cpp
LOCAL_LDLIBS := -ldl -llog
@@ -553,6 +477,17 @@ LOCAL_SRC_FILES := $(addprefix pcre/, $(libpcre_src_files))
include $(BUILD_STATIC_LIBRARY)
########################################################
## overture
########################################################
include $(CLEAR_VARS)
LOCAL_MODULE := overture
LOCAL_SRC_FILES := overture/$(TARGET_ARCH_ABI)/liboverture.so
include $(PREBUILT_SHARED_LIBRARY)
# Import cpufeatures
$(call import-module,android/cpufeatures)
+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"
android:layout_width="match_parent"
android:layout_height="wrap_content"
android:inputType="textNoSuggestions"
android:lines="1"/>
android:inputType="textNoSuggestions|textMultiLine"/>
</LinearLayout>
+8 -4
View File
@@ -1,8 +1,6 @@
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="enc_method_entry">
<item>TABLE</item>
<item>RC4</item>
<item>RC4-MD5</item>
<item>AES-128-CFB</item>
<item>AES-192-CFB</item>
@@ -17,11 +15,13 @@
<item>SALSA20</item>
<item>CHACHA20</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 name="enc_method_value">
<item>table</item>
<item>rc4</item>
<item>rc4-md5</item>
<item>aes-128-cfb</item>
<item>aes-192-cfb</item>
@@ -36,6 +36,10 @@
<item>salsa20</item>
<item>chacha20</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 name="route_entry">
-3
View File
@@ -36,8 +36,6 @@
<!-- feature category -->
<string name="ipv6">IPv6 Route</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_entry_all">All</string>
<string name="route_entry_bypass_lan">Bypass LAN</string>
@@ -62,7 +60,6 @@
<string name="forward_success">Shadowsocks started.</string>
<string name="invalid_server">Invalid server name</string>
<string name="service_failed">Failed to connect the remote server</string>
<string name="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_no_root">NAT mode requires ROOT permission</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"
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
android:title="@string/proxy_cat">
<be.mygod.preference.EditTextPreference
android:key="profileName"
android:summary="%s"
android:title="@string/profile_name"/>
<be.mygod.preference.EditTextPreference
android:key="proxy"
android:summary="%s"
@@ -36,10 +37,6 @@
android:entryValues="@array/enc_method_value"
android:summary="%s"
android:title="@string/enc_method"/>
<SwitchPreference
android:key="isAuth"
android:summary="@string/onetime_auth_summary"
android:title="@string/onetime_auth"/>
</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.utils._
import okhttp3.{Dns, FormBody, OkHttpClient, Request}
import org.json.JSONObject
import org.json.{JSONArray, JSONObject}
import scala.collection.JavaConversions._
import scala.collection.mutable
import scala.collection.mutable.ArrayBuffer
import scala.collection.JavaConversions._
import scala.util.Random
trait BaseService extends Service {
@@ -183,9 +183,6 @@ trait BaseService extends Service {
changeState(State.CONNECTING)
if (profile.isMethodUnsafe)
handler.post(() => Toast.makeText(this, R.string.method_unsafe, Toast.LENGTH_LONG).show())
Utils.ThrowableFuture(try connect() catch {
case _: NameNotResolvedException => stopRunner(stopService = true, getString(R.string.invalid_server))
case _: NullConnectionException => stopRunner(stopService = true, getString(R.string.reboot_required))
@@ -325,11 +322,52 @@ trait BaseService extends Service {
.put("server_port", profile.remotePort)
.put("password", profile.password)
.put("method", profile.method)
if (profile.auth) config.put("auth", true)
if (pluginPath != null) config
.put("plugin", Commandline.toString(buildPluginCommandLine()))
.put("plugin_opts", plugin.toString)
IOUtils.writeString(new File(getFilesDir, file), config.toString)
file
}
protected final def buildOvertureConfig(file: String): String = {
val config = new JSONObject()
.put("BindAddress", ":" + (profile.localPort + 53))
.put("RedirectIPv6Record", true)
.put("DomainBase64Decode", true)
.put("HostsFile", "hosts")
.put("MinimumTTL", 3600)
.put("CacheSize", 4096)
def makeDns(name: String, address: String, edns: Boolean = true) = {
val dns = new JSONObject()
.put("Name", name)
.put("Address", address + ":53")
.put("Timeout", 6)
if (edns) dns
.put("Protocol", "tcp")
.put("Socks5Address", "127.0.0.1:" + profile.localPort)
.put("EDNSClientSubnet", new JSONObject().put("Policy", "disable"))
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.util.concurrent.Semaphore
import android.os.Build
import android.util.Log
import com.github.shadowsocks.utils.CloseUtils._
import com.github.shadowsocks.utils.Commandline
@@ -89,7 +90,7 @@ class GuardedProcess(cmd: String*) {
} catch {
case _: InterruptedException =>
if (BuildConfig.DEBUG) Log.d(TAG, "thread interrupt, destroy process: " + Commandline.toString(cmd))
process.destroy()
destroyProcess()
case e: IOException => ioException = e
} finally semaphore.release()
}, "GuardThread-" + cmd.head)
@@ -105,16 +106,24 @@ class GuardedProcess(cmd: String*) {
def destroy() {
isDestroyed = true
guardThread.interrupt()
process.destroy()
destroyProcess()
try guardThread.join() catch {
case _: InterruptedException =>
}
}
private def destroyProcess() {
if (Build.VERSION.SDK_INT < 24) {
JniHelper.sigtermCompat(process)
JniHelper.waitForCompat(process, 500)
}
process.destroy()
}
def restart() {
this.synchronized {
isRestart = true
process.destroy()
destroyProcess()
}
}
@@ -84,11 +84,6 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
private var txRateText: 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()
.setToolbarColor(ContextCompat.getColor(this, R.color.material_primary_500))
.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, (_ =>
if (state == State.STOPPED) app.editor.putBoolean(Key.isNAT, false).apply()): View.OnClickListener)
override def binderDied() {
override def binderDied(): Unit = handler.post(() => {
detachService()
app.crashRecovery()
attachService(callback)
}
})
override def onActivityResult(requestCode: Int, resultCode: Int, data: Intent): Unit = resultCode match {
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")
if (tf != null) title.setTypeface(tf)
if (savedInstanceState == null) displayFragment(profilesFragment)
if (savedInstanceState == null) displayFragment(new ProfilesFragment)
statusText = findViewById(R.id.status).asInstanceOf[TextView]
txText = findViewById(R.id.tx).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) {
currentFragment = fragment
getFragmentManager.beginTransaction().replace(R.id.fragment_holder, fragment).commitAllowingStateLoss()
drawer.closeDrawer()
}
override def onItemClick(view: View, position: Int, drawerItem: IDrawerItem[_, _ <: ViewHolder]): Boolean = {
drawerItem.getIdentifier match {
case DRAWER_PROFILES => displayFragment(profilesFragment)
case DRAWER_PROFILES => displayFragment(new ProfilesFragment)
case DRAWER_RECOVERY =>
app.track("GlobalConfigFragment", "reset")
if (bgService != null) bgService.use(-1)
@@ -384,12 +378,12 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
app.copyAssets()
handler.sendEmptyMessage(0)
}
case DRAWER_GLOBAL_SETTINGS => displayFragment(globalSettingsFragment)
case DRAWER_GLOBAL_SETTINGS => displayFragment(new GlobalSettingsFragment)
case DRAWER_ABOUT =>
app.track(TAG, "about")
displayFragment(aboutFragment)
displayFragment(new AboutFragment)
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
}
@@ -408,12 +402,14 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
setListeningForBandwidth(true)
}
override def onBackPressed(): Unit =
if (drawer.isDrawerOpen) drawer.closeDrawer() else if (!currentFragment.onBackPressed())
if (currentFragment != profilesFragment) {
displayFragment(profilesFragment)
override def onBackPressed(): Unit = if (drawer.isDrawerOpen) drawer.closeDrawer() else {
val currentFragment = getFragmentManager.findFragmentById(R.id.fragment_holder).asInstanceOf[ToolbarFragment]
if (!currentFragment.onBackPressed())
if (currentFragment.isInstanceOf[ProfilesFragment]) super.onBackPressed() else {
displayFragment(new ProfilesFragment)
drawer.setSelection(DRAWER_PROFILES)
} else super.onBackPressed()
}
}
override def onStop() {
setListeningForBandwidth(false)
@@ -358,7 +358,9 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
REQUEST_SCAN_QR_CODE) catch {
case _: ActivityNotFoundException =>
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
case R.id.action_import =>
@@ -197,15 +197,17 @@ class ShadowsocksApplication extends Application {
def copyAssets() {
val assetManager = getAssets
var files: Array[String] = null
try files = assetManager.list("acl") catch {
case e: IOException =>
Log.e(TAG, e.getMessage)
app.track(e)
for (dir <- List("acl", "overture")) {
var files: Array[String] = null
try files = assetManager.list(dir) catch {
case e: IOException =>
Log.e(TAG, e.getMessage)
app.track(e)
}
if (files != null) for (file <- files) autoClose(assetManager.open(dir + "/" + file))(in =>
autoClose(new FileOutputStream(new File(getFilesDir, file)))(out =>
IOUtils.copy(in, out)))
}
if (files != null) for (file <- files) autoClose(assetManager.open("acl/" + file))(in =>
autoClose(new FileOutputStream(new File(getFilesDir, file)))(out =>
IOUtils.copy(in, out)))
editor.putInt(Key.currentVersionCode, BuildConfig.VERSION_CODE).apply()
}
@@ -49,7 +49,7 @@ class ShadowsocksNatService extends BaseService {
var sslocalProcess: GuardedProcess = _
var sstunnelProcess: GuardedProcess = _
var redsocksProcess: GuardedProcess = _
var pdnsdProcess: GuardedProcess = _
var overtureProcess: GuardedProcess = _
var su: Shell.Interactive = _
def startShadowsocksDaemon() {
@@ -74,7 +74,7 @@ class ShadowsocksNatService extends BaseService {
"-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",
"-L", profile.remoteDns.trim + ":53",
"-c", buildShadowsocksConfig("ss-tunnel-nat.conf"))
if (profile.udpdns) cmd.append("-u")
@@ -83,21 +83,8 @@ class ShadowsocksNatService extends BaseService {
}
def startDnsDaemon() {
val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0"
IOUtils.writeString(new File(getFilesDir, "pdnsd-nat.conf"), profile.route match {
case Acl.BYPASS_CHN | Acl.BYPASS_LAN_CHN | Acl.GFWLIST | Acl.CUSTOM_RULES =>
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getCacheDir.getAbsolutePath,
"127.0.0.1", profile.localPort + 53, "114.114.114.114, 223.5.5.5, 1.2.4.8",
getBlackList, reject, profile.localPort + 63, reject)
case Acl.CHINALIST =>
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "", getCacheDir.getAbsolutePath,
"127.0.0.1", profile.localPort + 53, "8.8.8.8, 8.8.4.4, 208.67.222.222",
"", reject, profile.localPort + 63, reject)
case _ =>
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "", getCacheDir.getAbsolutePath,
"127.0.0.1", profile.localPort + 53, profile.localPort + 63, reject)
})
pdnsdProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/libpdnsd.so", "-c", "pdnsd-nat.conf")
overtureProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/liboverture.so",
"-c", buildOvertureConfig("overture-nat.conf"))
.start()
}
@@ -111,11 +98,13 @@ class ShadowsocksNatService extends BaseService {
/** Called when the activity is first created. */
def handleConnection() {
startDNSTunnel()
startRedsocksDaemon()
startShadowsocksDaemon()
if (!profile.udpdns) startDnsDaemon()
if (!profile.udpdns)
startDnsDaemon()
else
startDNSTunnel()
setupIptables()
@@ -143,9 +132,9 @@ class ShadowsocksNatService extends BaseService {
redsocksProcess.destroy()
redsocksProcess = null
}
if (pdnsdProcess != null) {
pdnsdProcess.destroy()
pdnsdProcess = null
if (overtureProcess != null) {
overtureProcess.destroy()
overtureProcess = null
}
su.addCommand("iptables -t nat -F OUTPUT")
@@ -23,14 +23,13 @@ package com.github.shadowsocks
import java.io.File
import java.util.Locale
import android.annotation.SuppressLint
import android.content._
import android.content.pm.PackageManager.NameNotFoundException
import android.net.VpnService
import android.os._
import android.util.Log
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.utils._
@@ -46,8 +45,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
private var notification: ShadowsocksNotification = _
var sslocalProcess: GuardedProcess = _
var sstunnelProcess: GuardedProcess = _
var pdnsdProcess: GuardedProcess = _
var overtureProcess: GuardedProcess = _
var tun2socksProcess: GuardedProcess = _
override def onBind(intent: Intent): IBinder = {
@@ -95,17 +93,13 @@ class ShadowsocksVpnService extends VpnService with BaseService {
sslocalProcess.destroy()
sslocalProcess = null
}
if (sstunnelProcess != null) {
sstunnelProcess.destroy()
sstunnelProcess = null
}
if (tun2socksProcess != null) {
tun2socksProcess.destroy()
tun2socksProcess = null
}
if (pdnsdProcess != null) {
pdnsdProcess.destroy()
pdnsdProcess = null
if (overtureProcess != null) {
overtureProcess.destroy()
overtureProcess = null
}
}
@@ -150,15 +144,14 @@ class ShadowsocksVpnService extends VpnService with BaseService {
/** Called when the activity is first created. */
def handleConnection() {
val fd = startVpn()
if (!sendFd(fd)) throw new Exception("sendFd failed")
startShadowsocksDaemon()
if (!profile.udpdns) {
startDnsDaemon()
startDnsTunnel()
}
val fd = startVpn()
if (!sendFd(fd)) throw new Exception("sendFd failed")
}
override protected def buildPluginCommandLine(): ArrayBuffer[String] = super.buildPluginCommandLine() += "-V"
@@ -183,36 +176,12 @@ class ShadowsocksVpnService extends VpnService with BaseService {
sslocalProcess = new GuardedProcess(cmd: _*).start()
}
def startDnsTunnel(): Unit =
sstunnelProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/libss-tunnel.so",
"-V",
"-t", "10",
"-b", "127.0.0.1",
"-l", (profile.localPort + 63).toString,
"-L" , if (profile.remoteDns == null) "8.8.8.8:53" else profile.remoteDns + ":53",
"-c", buildShadowsocksConfig("ss-tunnel-vpn.conf")).start()
def startDnsDaemon() {
val reject = if (profile.ipv6) "224.0.0.0/3" else "224.0.0.0/3, ::/0"
IOUtils.writeString(new File(getFilesDir, "pdnsd-vpn.conf"), profile.route match {
case Acl.BYPASS_CHN | Acl.BYPASS_LAN_CHN | Acl.GFWLIST | Acl.CUSTOM_RULES =>
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "protect = \"protect_path\";", getCacheDir.getAbsolutePath,
"0.0.0.0", profile.localPort + 53, "114.114.114.114, 223.5.5.5, 1.2.4.8",
getBlackList, reject, profile.localPort + 63, reject)
case Acl.CHINALIST =>
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "protect = \"protect_path\";", getCacheDir.getAbsolutePath,
"0.0.0.0", profile.localPort + 53, "8.8.8.8, 8.8.4.4, 208.67.222.222",
"", reject, profile.localPort + 63, reject)
case _ =>
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "protect = \"protect_path\";", getCacheDir.getAbsolutePath,
"0.0.0.0", profile.localPort + 53, profile.localPort + 63, reject)
})
val cmd = Array(getApplicationInfo.nativeLibraryDir + "/libpdnsd.so", "-c", "pdnsd-vpn.conf")
pdnsdProcess = new GuardedProcess(cmd: _*).start()
overtureProcess = new GuardedProcess(getApplicationInfo.nativeLibraryDir + "/liboverture.so",
"-c", buildOvertureConfig("overture-vpn.conf"), "-V")
.start()
}
@SuppressLint(Array("NewApi"))
def startVpn(): Int = {
val builder = new Builder()
@@ -221,7 +190,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
.setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
builder.addDnsServer("8.8.8.8")
builder.addDnsServer(profile.remoteDns.trim)
if (profile.ipv6) {
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) {
builder.addRoute("0.0.0.0", 0)
} else {
val privateList = getResources.getStringArray(R.array.bypass_private_route)
privateList.foreach(cidr => {
val addr = cidr.split('/')
builder.addRoute(addr(0), addr(1).toInt)
getResources.getStringArray(R.array.bypass_private_route).foreach(cidr => {
val subnet = Subnet.fromString(cidr)
builder.addRoute(subnet.address.getHostAddress, subnet.prefixSize)
})
builder.addRoute(profile.remoteDns.trim, 32)
}
builder.addRoute("8.8.0.0", 16)
conn = builder.establish()
if (conn == null) throw new NullConnectionException
@@ -291,7 +258,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
var tries = 1
while (tries < 5) {
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
}
tries += 1
@@ -92,7 +92,7 @@ class ShadowsocksVpnThread(service: ShadowsocksVpnService) extends Thread {
val ret = service.protect(fd)
// Trick to close file decriptor
System.jniclose(fd)
JniHelper.close(fd)
if (ret) {
output.write(0)
@@ -36,7 +36,7 @@ object AclSyncJob {
final val TAG = "AclSyncJob"
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)
.setRequiredNetworkType(JobRequest.NetworkType.UNMETERED)
.setRequiresCharging(true)
@@ -20,6 +20,7 @@ import com.github.shadowsocks.widget.UndoSnackbarManager
import com.github.shadowsocks.{MainActivity, R, ToolbarFragment}
import scala.collection.mutable
import scala.io.Source
/**
* @author Mygod
@@ -144,8 +145,12 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
apply()
index
} else -1
def addToProxy(input: String): Int = try addSubnet(Subnet.fromString(input)) catch {
case _: IllegalArgumentException => addHostname(input)
def addToProxy(input: String): Int = {
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 {
case GENERIC => addToProxy(text.toString)
@@ -298,13 +303,7 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
.create().show()
true
case R.id.action_import =>
try {
val items = clipboard.getPrimaryClip.getItemAt(0).getText.toString.split("\n")
if (items.nonEmpty) {
items.foreach(adapter.addToProxy)
return true
}
} catch {
try if (adapter.addToProxy(clipboard.getPrimaryClip.getItemAt(0).getText.toString) != -1) return true catch {
case _: Exception =>
}
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
import java.net.{Inet4Address, Inet6Address, InetAddress}
import java.net.InetAddress
import java.util.Objects
import com.github.shadowsocks.utils.Utils
@@ -9,16 +10,11 @@ import com.github.shadowsocks.utils.Utils
*/
@throws[IllegalArgumentException]
class Subnet(val address: InetAddress, val prefixSize: Int) extends Comparable[Subnet] {
if (prefixSize < 0) throw new IllegalArgumentException
address match {
case _: Inet4Address => if (prefixSize > 32) throw new IllegalArgumentException
case _: Inet6Address => if (prefixSize > 128) throw new IllegalArgumentException
}
private def addressLength = address.getAddress.length << 3
if (prefixSize < 0 || prefixSize > addressLength) throw new IllegalArgumentException
override def toString: String = if (address match {
case _: Inet4Address => prefixSize == 32
case _: Inet6Address => prefixSize == 128
}) address.getHostAddress else address.getHostAddress + '/' + prefixSize
override def toString: String =
if (prefixSize == addressLength) address.getHostAddress else address.getHostAddress + '/' + prefixSize
override def compareTo(that: Subnet): Int = {
val addrThis = address.getAddress
@@ -31,19 +27,21 @@ class Subnet(val address: InetAddress, val prefixSize: Int) extends Comparable[S
}
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 {
@throws[IllegalArgumentException]
def fromString(value: String): Subnet = {
val parts = value.split("/")
if (!Utils.isNumeric(parts(0))) throw new IllegalArgumentException()
val addr = InetAddress.getByName(parts(0))
val addr = Utils.parseNumericAddress(parts(0))
parts.length match {
case 1 => new Subnet(addr, addr match {
case _: Inet4Address => 32
case _: Inet6Address => 128
})
case 1 => new Subnet(addr, addr.getAddress.length << 3)
case 2 => new Subnet(addr, parts(1).toInt)
case _ => throw new IllegalArgumentException()
}
@@ -77,9 +77,6 @@ class DBHelper(val context: Context)
} else if (oldVersion < 19) {
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) {
profileDao.executeRawNoArgs("ALTER TABLE `profile` ADD COLUMN ipv6 SMALLINT;")
}
@@ -89,9 +86,9 @@ class DBHelper(val context: Context)
TableUtils.createTable(connectionSource, classOf[Profile])
profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, proxyApps, bypass," +
" udpdns, auth, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, auth," +
" ipv6, individual FROM `tmp`;")
" udpdns, ipv6, individual) " +
"SELECT id, name, host, localPort, remotePort, password, method, route, 1 - global, bypass, udpdns, ipv6," +
" individual FROM `tmp`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
profileDao.executeRawNoArgs("COMMIT;")
} else if (oldVersion < 13) {
@@ -127,10 +124,10 @@ class DBHelper(val context: Context)
profileDao.executeRawNoArgs("ALTER TABLE `profile` RENAME TO `tmp`;")
TableUtils.createTable(connectionSource, classOf[Profile])
profileDao.executeRawNoArgs(
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, remoteDns, proxyApps," +
" bypass, udpdns, auth, ipv6, individual, tx, rx, date, userOrder, plugin) " +
"INSERT INTO `profile`(id, name, host, localPort, remotePort, password, method, route, remoteDns, " +
"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, " +
"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`;")
profileDao.executeRawNoArgs("DROP TABLE `tmp`;")
profileDao.executeRawNoArgs("COMMIT;")
@@ -20,13 +20,13 @@
package com.github.shadowsocks.database
import java.net.URLEncoder
import java.util.Locale
import android.content.SharedPreferences
import android.net.Uri
import android.os.Binder
import android.text.TextUtils
import android.util.Base64
import com.github.shadowsocks.plugin.PluginConfiguration
import com.github.shadowsocks.utils.Key
import com.j256.ormlite.field.{DataType, DatabaseField}
@@ -68,9 +68,6 @@ class Profile {
@DatabaseField
var udpdns: Boolean = _
@DatabaseField
var auth: Boolean = _
@DatabaseField
var ipv6: Boolean = _
@@ -96,11 +93,19 @@ class Profile {
def nameIsEmpty: Boolean = name == null || name.isEmpty
def getName: String = if (nameIsEmpty) formattedAddress else name
override def toString: String = "ss://" + Base64.encodeToString("%s%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
method, if (auth) "-auth" else "", password, host, remotePort).getBytes, Base64.NO_PADDING | Base64.NO_WRAP) +
(if (TextUtils.isEmpty(name)) "" else '#' + URLEncoder.encode(name, "utf-8"))
def isMethodUnsafe: Boolean = "table".equalsIgnoreCase(method) || "rc4".equalsIgnoreCase(method)
def toUri: Uri = {
val builder = new Uri.Builder()
.scheme("ss")
.encodedAuthority("%s@%s:%d".formatLocal(Locale.ENGLISH,
Base64.encodeToString("%s:%s".formatLocal(Locale.ENGLISH, method, password).getBytes,
Base64.NO_PADDING | Base64.NO_WRAP | Base64.URL_SAFE), host, remotePort))
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
.putString(Key.name, name)
@@ -114,7 +119,6 @@ class Profile {
.putBoolean(Key.proxyApps, proxyApps)
.putBoolean(Key.bypass, bypass)
.putBoolean(Key.udpdns, udpdns)
.putBoolean(Key.auth, auth)
.putBoolean(Key.ipv6, ipv6)
.putString(Key.individual, individual)
.putString(Key.plugin, plugin)
@@ -132,7 +136,6 @@ class Profile {
proxyApps = pref.getBoolean(Key.proxyApps, false)
bypass = pref.getBoolean(Key.bypass, false)
udpdns = pref.getBoolean(Key.udpdns, false)
auth = pref.getBoolean(Key.auth, false)
ipv6 = pref.getBoolean(Key.ipv6, false)
individual = pref.getString(Key.individual, null)
plugin = pref.getString(Key.plugin, null)
@@ -50,6 +50,20 @@ object PluginManager {
|RSo6EooQ7+NBejOXysqIF1q0BJs8Y5s/CaTOmgbL7uPCkzArB6SS/hzXgDk5gw6v
|wkGeOtzcj1DlbUTvt1s5GlnwBTGUmkbLx+YUje+n+IBgMbohLUDYBtUHylRVgMsc
|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))
private var receiver: BroadcastReceiver = _
@@ -45,86 +45,6 @@ object ConfigUtils {
" port = %d;\n" +
" type = socks5;\n" +
"}\n"
val PDNSD_LOCAL =
"""
|global {
| perm_cache = 2048;
| %s
| cache_dir = "%s";
| server_ip = %s;
| server_port = %d;
| query_method = tcp_only;
| min_ttl = 15m;
| max_ttl = 1w;
| timeout = 10;
| daemon = off;
|}
|
|server {
| label = "local";
| ip = 127.0.0.1;
| port = %d;
| reject = %s;
| reject_policy = negate;
| reject_recursively = on;
|}
|
|rr {
| name=localhost;
| reverse=on;
| a=127.0.0.1;
| owner=localhost;
| soa=localhost,root.localhost,42,86400,900,86400,86400;
|}
""".stripMargin
val PDNSD_DIRECT =
"""
|global {
| perm_cache = 2048;
| %s
| cache_dir = "%s";
| server_ip = %s;
| server_port = %d;
| query_method = udp_only;
| min_ttl = 15m;
| max_ttl = 1w;
| timeout = 10;
| daemon = off;
| par_queries = 4;
|}
|
|server {
| label = "remote-servers";
| ip = %s;
| timeout = 3;
| query_method = udp_only;
| %s
| policy = included;
| reject = %s;
| reject_policy = fail;
| reject_recursively = on;
|}
|
|server {
| label = "local-server";
| ip = 127.0.0.1;
| query_method = tcp_only;
| port = %d;
| reject = %s;
| reject_policy = negate;
| reject_recursively = on;
|}
|
|rr {
| name=localhost;
| reverse=on;
| a=127.0.0.1;
| owner=localhost;
| soa=localhost,root.localhost,42,86400,900,86400,86400;
|}
""".stripMargin
}
object Key {
@@ -141,7 +61,6 @@ object Key {
val proxyApps = "isProxyApps"
val bypass = "isBypassApps"
val udpdns = "isUdpDns"
val auth = "isAuth"
val ipv6 = "isIpv6"
val host = "proxy"
@@ -20,32 +20,49 @@
package com.github.shadowsocks.utils
import java.net.URLDecoder
import android.net.Uri
import android.util.{Base64, Log}
import com.github.shadowsocks.database.Profile
object Parser {
val TAG = "ShadowParser"
private val pattern = "(?i)ss://([A-Za-z0-9+-/=_]+)(#(.+))?".r
private val decodedPattern = "(?i)^((.+?)(-auth)??:(.*)@(.+?):(\\d+?))$".r
private val pattern = "(?i)ss://[-a-zA-Z0-9+&@#/%?=~_|!:,.;]*[-a-zA-Z0-9+&@#/%=~_|]".r
private val userInfoPattern = "^(.+?):(.*)$".r
private val legacyPattern = "^((.+?):(.*)@(.+?):(\\d+?))$".r
def findAll(data: CharSequence): Iterator[Profile] =
pattern.findAllMatchIn(if (data == null) "" else data).map(m => try
decodedPattern.findFirstMatchIn(new String(Base64.decode(m.group(1), Base64.NO_PADDING), "UTF-8")) match {
case Some(ss) =>
val profile = new Profile
profile.method = ss.group(2).toLowerCase
if (ss.group(3) != null) profile.auth = true
profile.password = ss.group(4)
profile.host = ss.group(5)
profile.remotePort = ss.group(6).toInt
if (m.group(2) != null) profile.name = URLDecoder.decode(m.group(3), "utf-8")
profile
case _ => null
} catch {
case ex: Exception =>
Log.e(TAG, "parser error: " + m.source, ex)// Ignore
null
}).filter(_ != null)
pattern.findAllMatchIn(if (data == null) "" else data).map(m => {
val uri = Uri.parse(m.matched)
uri.getUserInfo match {
case null => new String(Base64.decode(uri.getHost, Base64.NO_PADDING), "UTF-8") match {
case legacyPattern(_, method, password, host, port) => // legacy uri
val profile = new Profile
profile.method = method.toLowerCase
profile.password = password
profile.host = host
profile.remotePort = port.toInt
profile.plugin = uri.getQueryParameter(Key.plugin)
profile.name = uri.getFragment
profile
case _ =>
Log.e(TAG, "Unrecognized URI: " + m.matched)
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 = {
var n: Double = size
var i = -1
while (n >= 1000) {
while (n >= 999.5) {
n /= 1024
i = i + 1
}
@@ -20,6 +20,7 @@
package com.github.shadowsocks.utils
import java.lang.reflect.InvocationTargetException
import java.net._
import java.security.MessageDigest
@@ -108,7 +109,15 @@ object Utils {
.orElse(resolve(host))
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 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
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
[Documentation](doc.md) | [Change log](CHANGES.md)
Support library for easier development on [shadowsocks
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.
@@ -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
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
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:
```scala
libraryDependencies += "com.github.shadowsocks" %% "plugin" % "0.0.1-SNAPSHOT"
libraryDependencies += "com.github.shadowsocks" %% "plugin" % "0.0.2"
```
### Native binary configuration
@@ -88,7 +87,7 @@ Then add it to your manifest:
...
<provider android:name=".BinaryProvider"
android:exported="true"
android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID">
android:authorities="$FULLY_QUALIFIED_NAME_OF_YOUR_CONTENTPROVIDER">
<intent-filter>
<action android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"/>
</intent-filter>
+28 -1
View File
@@ -2,4 +2,31 @@ enablePlugins(AndroidLib)
android.useSupportVectors
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"
android:exported="true"
android:authorities="com.github.shadowsocks.plugin.$PLUGIN_ID">
android:authorities="$FULLY_QUALIFIED_NAME_OF_YOUR_CONTENTPROVIDER">
<intent-filter>
<action android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN"/>
</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
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:
- @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
@@ -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"
android:value="0.0.1"/>
android:value="0.0.2"/>
```
+1 -1
View File
@@ -6,6 +6,6 @@
<application
android:theme="@style/Theme.Shadowsocks">
<meta-data android:name="com.github.shadowsocks.plugin.version"
android:value="0.0.1"/>
android:value="0.0.2"/>
</application>
</manifest>
@@ -24,6 +24,14 @@ public final class PluginOptions extends HashMap<String, String> {
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) {
this();
if (TextUtils.isEmpty(options)) return;
@@ -15,7 +15,7 @@ import android.os.{Bundle, ParcelFileDescriptor}
* &lt;application&gt;
* ...
* &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;category android:name="com.github.shadowsocks.plugin.ACTION_NATIVE_PLUGIN" /&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")
-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>
}