Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
c31a5c1951 | ||
|
|
6c47c361f7 | ||
|
|
ddd37a3219 | ||
|
|
01125d53b3 | ||
|
|
9613d65303 | ||
|
|
a510b11187 | ||
|
|
051e600e59 | ||
|
|
fea9a6f919 | ||
|
|
c2c7cabbaf | ||
|
|
092d880c7d | ||
|
|
c208c4bf50 | ||
|
|
34af6aeef5 | ||
|
|
d9af2f93e0 | ||
|
|
aae068b7b2 | ||
|
|
7626c1117e | ||
|
|
1188b59b05 | ||
|
|
cbd46d0c95 | ||
|
|
8d02a1a6a4 | ||
|
|
9f67636464 | ||
|
|
abb97e16c4 | ||
|
|
541f8de2cf | ||
|
|
7c31f5e5b4 | ||
|
|
809f4c13e0 | ||
|
|
04725803e4 | ||
|
|
faad961804 | ||
|
|
2f7424d07b | ||
|
|
fd4015fac2 | ||
|
|
f2c39bd16e | ||
|
|
e849a9e2cb | ||
|
|
524d3b6fdc | ||
|
|
6f5db34a6f | ||
|
|
3c161be1e4 | ||
|
|
1c30fdb8f2 | ||
|
|
ce24ddb9ed | ||
|
|
17eaf25b4f | ||
|
|
f4b171a3d9 | ||
|
|
ae9622a1b3 | ||
|
|
44e1b9daa3 | ||
|
|
7a2c42626b | ||
|
|
adb979a111 | ||
|
|
1e23307f95 | ||
|
|
64e6f14da1 | ||
|
|
3dd718dc01 | ||
|
|
942d181e36 | ||
|
|
fa083a8eca | ||
|
|
ef2ff23cd0 | ||
|
|
c1e208737c | ||
|
|
75bb00593a | ||
|
|
15f25b95a0 | ||
|
|
cc9fa4c4a6 | ||
|
|
9041404f42 | ||
|
|
5aae822cdd | ||
|
|
33ed5c65c5 | ||
|
|
1424e056ac | ||
|
|
ed79e0090e | ||
|
|
304113ce97 | ||
|
|
290bf8cb31 | ||
|
|
fcb0e2ace3 | ||
|
|
f240d5d202 | ||
|
|
ea85bbd83d |
-28
@@ -1,28 +0,0 @@
|
||||
language: android
|
||||
android:
|
||||
components:
|
||||
- tools
|
||||
- platform-tools
|
||||
- build-tools-21.1.1
|
||||
- android-21
|
||||
- extra-google-m2repository
|
||||
- extra-android-m2repository
|
||||
cache:
|
||||
- apt
|
||||
before_install:
|
||||
- echo "deb https://dl.bintray.com/sbt/debian /" | sudo tee -a /etc/apt/sources.list.d/sbt.list
|
||||
- sudo apt-get update
|
||||
install:
|
||||
- sudo apt-get install -y --force-yes sbt libc6-dev-i386 lib32ncurses5-dev libx11-dev g++-multilib lib32z1-dev ccache
|
||||
- export NDK_CCACHE=ccache
|
||||
- export ARCH=`uname -m`
|
||||
- wget http://dl.google.com/android/ndk/android-ndk-r10d-linux-${ARCH}.bin
|
||||
- chmod +x android-ndk-r10d-linux-${ARCH}.bin
|
||||
- ./android-ndk-r10d-linux-${ARCH}.bin -y | grep gcc
|
||||
- export ANDROID_NDK_HOME=`pwd`/android-ndk-r10d
|
||||
- export PATH=${PATH}:${ANDROID_NDK_HOME}:${ANDROID_HOME}/tools:${ANDROID_HOME}/platform-tools
|
||||
script:
|
||||
- cp local.properties.travis local.properties
|
||||
- ./build.sh
|
||||
- sbt android:package-release
|
||||
- exit 0
|
||||
@@ -6,16 +6,16 @@ Help to translate shadowsocks: http://crowdin.net/project/shadowsocks/invite
|
||||
|
||||
[](https://play.google.com/store/apps/details?id=com.github.shadowsocks)
|
||||
|
||||
### TRAVIS CI STATUS
|
||||
### CI STATUS
|
||||
|
||||
[](http://travis-ci.org/shadowsocks/shadowsocks-android)
|
||||
[](https://drone.io/github.com/shadowsocks/shadowsocks-android/latest)
|
||||
|
||||
### PREREQUISITES
|
||||
|
||||
* JDK 1.6+
|
||||
* SBT 0.12.3
|
||||
* Android SDK r21+
|
||||
* Android NDK r9+
|
||||
* JDK 1.7+
|
||||
* SBT 0.13.0+
|
||||
* Android SDK r24+
|
||||
* Android NDK r10d+
|
||||
|
||||
### BUILD
|
||||
|
||||
@@ -58,6 +58,16 @@ Help to translate shadowsocks: http://crowdin.net/project/shadowsocks/invite
|
||||
sbt clean android:package-release
|
||||
```
|
||||
|
||||
## OPEN SOURCE LICENSES
|
||||
|
||||
* shadowsocks-libev: [GPLv3](https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE)
|
||||
* tun2socks: [BSD](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/badvpn/COPYING)
|
||||
* redsocks: [APL 2.0](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/redsocks/README)
|
||||
* OpenSSL: [OpenSSL](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/openssl/NOTICE)
|
||||
* pdnsd: [GPLv3](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING)
|
||||
* libev: [GPLv2](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libev/LICENSE)
|
||||
* libevent: [BSD](https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libevent/LICENSE)
|
||||
|
||||
### LICENSE
|
||||
|
||||
Copyright (C) 2014 Max Lv <max.c.lv@gmail.com>
|
||||
|
||||
@@ -16,18 +16,20 @@ ndkBuild in Android := List()
|
||||
|
||||
typedResources in Android := false
|
||||
|
||||
resolvers += Resolver.jcenterRepo
|
||||
|
||||
resolvers += "JRAF" at "http://JRAF.org/static/maven/2"
|
||||
|
||||
resolvers += "madeye private releases" at "http://madeye-maven-repository.googlecode.com/git/"
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"dnsjava" % "dnsjava" % "2.1.5",
|
||||
"dnsjava" % "dnsjava" % "2.1.7",
|
||||
"com.github.kevinsawicki" % "http-request" % "5.6",
|
||||
"commons-net" % "commons-net" % "3.3",
|
||||
"com.google.zxing" % "android-integration" % "3.1.0"
|
||||
)
|
||||
|
||||
libraryDependencies ++= Seq(
|
||||
"com.joanzapata.android" % "android-iconify" % "1.0.9",
|
||||
"net.glxn.qrgen" % "android" % "2.0",
|
||||
"net.simonvt.menudrawer" % "menudrawer" % "3.0.6",
|
||||
"com.google.android.gms" % "play-services-base" % "6.5.87",
|
||||
"com.google.android.gms" % "play-services-ads" % "6.5.87",
|
||||
@@ -48,6 +50,7 @@ proguardOptions in Android ++= Seq("-keep class android.support.v4.app.** { *; }
|
||||
"-keep class org.jraf.android.backport.** { *; }",
|
||||
"-keep class com.github.shadowsocks.** { *; }",
|
||||
"-keep class * extends com.j256.ormlite.** { *; }",
|
||||
"-keep class com.joanzapata.** { *; }",
|
||||
"-keepattributes *Annotation*",
|
||||
"-dontwarn org.xbill.**",
|
||||
"-dontwarn com.actionbarsherlock.**")
|
||||
|
||||
@@ -7,8 +7,8 @@ function try () {
|
||||
try pushd src/main
|
||||
|
||||
# Build
|
||||
try $ANDROID_NDK_HOME/ndk-build clean
|
||||
try $ANDROID_NDK_HOME/ndk-build
|
||||
#try $ANDROID_NDK_HOME/ndk-build clean
|
||||
try $ANDROID_NDK_HOME/ndk-build -j8
|
||||
|
||||
# copy executables
|
||||
rm -rf assets/armeabi-v7a
|
||||
|
||||
+14
-12
@@ -10,20 +10,22 @@ import IPy
|
||||
|
||||
def main():
|
||||
china_list_set = IPy.IPSet()
|
||||
for line in sys.stdin:
|
||||
line_params = line.split("|")
|
||||
if len(line_params) < 5 or line_params[2] != "ipv4" or line_params[1] != "CN":
|
||||
continue
|
||||
ip_addr = line_params[3]
|
||||
ip_length = float(line_params[4])
|
||||
ip_mask = 32 - int(math.ceil(math.log(ip_length, 2)))
|
||||
china_list_set.add(IPy.IP("%s/%d" % (ip_addr, ip_mask)))
|
||||
# for line in sys.stdin:
|
||||
# line_params = line.split("|")
|
||||
# if len(line_params) < 5 or line_params[2] != "ipv4" or line_params[1] != "CN":
|
||||
# continue
|
||||
# ip_addr = line_params[3]
|
||||
# ip_length = float(line_params[4])
|
||||
# ip_mask = 32 - int(math.ceil(math.log(ip_length, 2)))
|
||||
# china_list_set.add(IPy.IP("%s/%d" % (ip_addr, ip_mask)))
|
||||
|
||||
# 添加内网地址
|
||||
internal_list = IPy.IPSet(map(IPy.IP, [
|
||||
"0.0.0.0/8",
|
||||
"10.0.0.0/8",
|
||||
"100.64.0.0/10",
|
||||
"112.124.47.0/24",
|
||||
"114.114.114.0/24",
|
||||
"127.0.0.0/8",
|
||||
"169.254.0.0/16",
|
||||
"172.16.0.0/12",
|
||||
@@ -45,10 +47,10 @@ def main():
|
||||
for ip in china_list_set:
|
||||
all.discard(ip)
|
||||
|
||||
filter = itertools.ifilter(lambda x: len(x) <= 65536, all)
|
||||
for ip in filter:
|
||||
all.discard(ip)
|
||||
all.add(IPy.IP(ip.strNormal(0)).make_net('255.255.0.0'))
|
||||
# filter = itertools.ifilter(lambda x: len(x) <= 65536, all)
|
||||
# for ip in filter:
|
||||
# all.discard(ip)
|
||||
# all.add(IPy.IP(ip.strNormal(0)).make_net('255.255.0.0'))
|
||||
|
||||
# 输出结果
|
||||
for ip in all:
|
||||
|
||||
@@ -1 +1 @@
|
||||
sbt.version=0.13.5
|
||||
sbt.version=0.13.8
|
||||
|
||||
+3
-3
@@ -1,7 +1,7 @@
|
||||
resolvers += Resolver.url("scalasbt releases", new URL("http://scalasbt.artifactoryonline.com/scalasbt/sbt-plugin-snapshots"))(Resolver.ivyStylePatterns)
|
||||
|
||||
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.3.10")
|
||||
addSbtPlugin("com.hanhuy.sbt" % "android-sdk-plugin" % "1.4.8")
|
||||
|
||||
resolvers += Resolver.sbtPluginRepo("snapshots")
|
||||
resolvers += "Sonatype snapshots" at "https://oss.sonatype.org/content/repositories/snapshots/"
|
||||
|
||||
addSbtPlugin("com.hanhuy.sbt" % "sbt-idea" % "1.7.0-SNAPSHOT")
|
||||
addSbtPlugin("com.github.mpeltonen" % "sbt-idea" % "1.7.0-SNAPSHOT")
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
|
||||
package="com.github.shadowsocks"
|
||||
android:versionCode="103"
|
||||
android:versionName="2.6.5">
|
||||
android:versionCode="113"
|
||||
android:versionName="2.7.5">
|
||||
|
||||
<uses-permission android:name="android.permission.INTERNET"/>
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
|
||||
|
||||
@@ -286,6 +286,17 @@ GNU General Public License for more details.</p>
|
||||
<p>You should have received a copy of the GNU General Public License
|
||||
along with this program. If not, see <a href="http://www.gnu.org/licenses/">http://www.gnu.org/licenses/</a>.</p>
|
||||
|
||||
<h3>Open Source Licenses</h3>
|
||||
<ul>
|
||||
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li>
|
||||
<li>tun2socks: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/badvpn/COPYING">BSD</a></li>
|
||||
<li>redsocks: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/redsocks/README">APL 2.0</a></li>
|
||||
<li>OpenSSL: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/openssl/NOTICE">OpenSSL</a></li>
|
||||
<li>pdnsd: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/pdnsd/COPYING">GPLv3</a></li>
|
||||
<li>libev: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libev/LICENSE">GPLv2</a></li>
|
||||
<li>libevent: <a href="https://github.com/shadowsocks/shadowsocks-android/blob/master/src/main/jni/libevent/LICENSE">BSD</a></li>
|
||||
</ul>
|
||||
|
||||
</body>
|
||||
</html>
|
||||
<!-- This document was created with MarkdownPad, the Markdown editor for Windows (http://markdownpad.com) -->
|
||||
|
||||
@@ -45,4 +45,6 @@ public class System {
|
||||
|
||||
public static native void exec(String cmd);
|
||||
public static native String getABI();
|
||||
public static native int sendfd(int fd);
|
||||
public static native void jniclose(int fd);
|
||||
}
|
||||
|
||||
+30
-9
@@ -110,6 +110,21 @@ LOCAL_CFLAGS := -O2 -I$(LOCAL_PATH)/libevent \
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
########################################################
|
||||
## libancillary
|
||||
########################################################
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
ANCILLARY_SOURCE := fd_recv.c fd_send.c
|
||||
|
||||
LOCAL_MODULE := libancillary
|
||||
LOCAL_CFLAGS += -O2 -I$(LOCAL_PATH)/libancillary
|
||||
|
||||
LOCAL_SRC_FILES := $(addprefix libancillary/, $(ANCILLARY_SOURCE))
|
||||
|
||||
include $(BUILD_STATIC_LIBRARY)
|
||||
|
||||
########################################################
|
||||
## libipset
|
||||
########################################################
|
||||
@@ -234,13 +249,14 @@ include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
SHADOWSOCKS_SOURCES := local.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c acl.c
|
||||
SHADOWSOCKS_SOURCES := local.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c acl.c android.c
|
||||
|
||||
LOCAL_MODULE := ss-local
|
||||
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
|
||||
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL \
|
||||
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H \
|
||||
-I$(LOCAL_PATH)/libev/ \
|
||||
-I$(LOCAL_PATH)/libev \
|
||||
-I$(LOCAL_PATH)/libancillary \
|
||||
-I$(LOCAL_PATH)/openssl/include \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libudns \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
|
||||
@@ -248,7 +264,7 @@ LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include/sodium \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libipset/include
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libev libcrypto libipset libcork libudns libsodium
|
||||
LOCAL_STATIC_LIBRARIES := libev libcrypto libipset libcork libudns libsodium libancillary
|
||||
|
||||
LOCAL_LDLIBS := -llog
|
||||
|
||||
@@ -260,20 +276,21 @@ include $(BUILD_EXECUTABLE)
|
||||
|
||||
include $(CLEAR_VARS)
|
||||
|
||||
SHADOWSOCKS_SOURCES := tunnel.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c
|
||||
SHADOWSOCKS_SOURCES := tunnel.c cache.c udprelay.c encrypt.c utils.c netutils.c json.c jconf.c android.c
|
||||
|
||||
LOCAL_MODULE := ss-tunnel
|
||||
LOCAL_SRC_FILES := $(addprefix shadowsocks-libev/src/, $(SHADOWSOCKS_SOURCES))
|
||||
LOCAL_CFLAGS := -Wall -O2 -fno-strict-aliasing -DUDPRELAY_LOCAL -DUDPRELAY_TUNNEL \
|
||||
-DUSE_CRYPTO_OPENSSL -DANDROID -DHAVE_CONFIG_H -DSSTUNNEL_JNI \
|
||||
-I$(LOCAL_PATH)/libev/ \
|
||||
-I$(LOCAL_PATH)/libev \
|
||||
-I$(LOCAL_PATH)/libancillary \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libudns \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libcork/include \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include \
|
||||
-I$(LOCAL_PATH)/shadowsocks-libev/libsodium/src/libsodium/include/sodium \
|
||||
-I$(LOCAL_PATH)/openssl/include
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libev libcrypto libsodium libcork libudns
|
||||
LOCAL_STATIC_LIBRARIES := libev libcrypto libsodium libcork libudns libancillary
|
||||
|
||||
LOCAL_LDLIBS := -llog
|
||||
|
||||
@@ -287,12 +304,13 @@ include $(CLEAR_VARS)
|
||||
|
||||
LOCAL_MODULE:= system
|
||||
|
||||
LOCAL_SRC_FILES:= \
|
||||
system.cpp
|
||||
LOCAL_C_INCLUDES:= $(LOCAL_PATH)/libancillary
|
||||
|
||||
LOCAL_SRC_FILES:= system.cpp
|
||||
|
||||
LOCAL_LDLIBS := -ldl -llog
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := cpufeatures
|
||||
LOCAL_STATIC_LIBRARIES := cpufeatures libancillary
|
||||
|
||||
include $(BUILD_SHARED_LIBRARY)
|
||||
|
||||
@@ -309,7 +327,10 @@ LOCAL_CFLAGS += -DBADVPN_LITTLE_ENDIAN -DBADVPN_THREAD_SAFE
|
||||
LOCAL_CFLAGS += -DNDEBUG -DANDROID
|
||||
# LOCAL_CFLAGS += -DTUN2SOCKS_JNI
|
||||
|
||||
LOCAL_STATIC_LIBRARIES := libancillary
|
||||
|
||||
LOCAL_C_INCLUDES:= \
|
||||
$(LOCAL_PATH)/libancillary \
|
||||
$(LOCAL_PATH)/badvpn/lwip/src/include/ipv4 \
|
||||
$(LOCAL_PATH)/badvpn/lwip/src/include/ipv6 \
|
||||
$(LOCAL_PATH)/badvpn/lwip/src/include \
|
||||
|
||||
@@ -71,9 +71,13 @@
|
||||
#include <generated/blog_channel_tun2socks.h>
|
||||
|
||||
#ifdef ANDROID
|
||||
|
||||
#include <ancillary.h>
|
||||
|
||||
#include <sys/prctl.h>
|
||||
#include <sys/un.h>
|
||||
#include <structure/BAVL.h>
|
||||
|
||||
BAVL connections_tree;
|
||||
typedef struct {
|
||||
BAddr local_addr;
|
||||
@@ -408,7 +412,6 @@ int main (int argc, char **argv)
|
||||
prctl(PR_SET_NAME, "com.github.shadowsocks");
|
||||
}
|
||||
|
||||
|
||||
// handle --help and --version
|
||||
if (options.help) {
|
||||
print_version();
|
||||
@@ -490,11 +493,56 @@ int main (int argc, char **argv)
|
||||
|
||||
#ifdef ANDROID
|
||||
// use supplied file descriptor
|
||||
|
||||
int sock, fd;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
if ((sock = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
||||
BLog(BLOG_ERROR, "socket() failed: %s (socket sock = %d)\n", strerror(errno), sock);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
char *path = "#shadowsocks_tunfd";
|
||||
memset(&addr, 0, sizeof(addr));
|
||||
addr.sun_family = AF_UNIX;
|
||||
strncpy(addr.sun_path, path, sizeof(addr.sun_path)-1);
|
||||
addr.sun_path[0] = 0;
|
||||
|
||||
if (bind(sock, (struct sockaddr*)&addr, sizeof(addr)) == -1) {
|
||||
BLog(BLOG_ERROR, "bind() failed: %s (sock = %d)\n", strerror(errno), sock);
|
||||
close(sock);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
if (listen(sock, 5) == -1) {
|
||||
BLog(BLOG_ERROR, "listen() failed: %s (sock = %d)\n", strerror(errno), sock);
|
||||
close(sock);
|
||||
goto fail2;
|
||||
}
|
||||
|
||||
for (;;) {
|
||||
int sock2;
|
||||
struct sockaddr_un remote;
|
||||
int t = sizeof(remote);
|
||||
if ((sock2 = accept(sock, (struct sockaddr *)&remote, &t)) == -1) {
|
||||
BLog(BLOG_ERROR, "accept() failed: %s (sock = %d)\n", strerror(errno), sock);
|
||||
continue;
|
||||
}
|
||||
if (ancil_recv_fd(sock2, &fd)) {
|
||||
BLog(BLOG_ERROR, "ancil_recv_fd: %s (sock = %d)\n", strerror(errno), sock2);
|
||||
close(sock2);
|
||||
} else {
|
||||
close(sock2);
|
||||
BLog(BLOG_INFO, "received fd = %d", fd);
|
||||
break;
|
||||
}
|
||||
}
|
||||
close(sock);
|
||||
|
||||
struct BTap_init_data init_data;
|
||||
init_data.dev_type = BTAP_DEV_TUN;
|
||||
init_data.init_type = BTAP_INIT_FD;
|
||||
init_data.init.fd.fd = options.tun_fd;
|
||||
init_data.init.fd.fd = fd;
|
||||
init_data.init.fd.mtu = options.tun_mtu;
|
||||
|
||||
if (!BTap_Init2(&device, &ss, init_data, device_error_handler, NULL)) {
|
||||
|
||||
@@ -0,0 +1,139 @@
|
||||
This library provide an easy interface to the black magic that can be done
|
||||
on Unix domain sockets, like passing file descriptors from one process to
|
||||
another.
|
||||
|
||||
Programs that uses this library should include the ancillary.h header file.
|
||||
Nothing else is required.
|
||||
|
||||
All functions of this library require the following header:
|
||||
|
||||
#include <ancillary.h>
|
||||
|
||||
At this time, the only ancillary data defined by the Single Unix
|
||||
Specification (v3) is file descriptors.
|
||||
|
||||
Passing file descriptors
|
||||
|
||||
int ancil_send_fd(socket, file_descriptor)
|
||||
int socket: the Unix socket
|
||||
int file_descriptor: the file descriptor
|
||||
Return value: 0 for success, -1 for failure.
|
||||
|
||||
Sends one file descriptor on a socket.
|
||||
In case of failure, errno is set; the possible values are the ones of the
|
||||
sendmsg(2) system call.
|
||||
|
||||
|
||||
int ancil_recv_fd(socket, file_descriptor)
|
||||
int socket: the Unix socket
|
||||
int *file_descriptor: pointer to the returned file descriptor
|
||||
Return value: 0 for success, -1 for failure
|
||||
|
||||
Receives one file descriptor from a socket.
|
||||
In case of success, the file descriptor is stored in the integer pointed
|
||||
to by file_descriptor.
|
||||
In case of failure, errno is set; the possible values are the ones of the
|
||||
recvmsg(2) system call.
|
||||
The behavior is undefined if the recv_fd does not match a send_fd* on the
|
||||
other side.
|
||||
|
||||
|
||||
int ancil_send_fds(socket, file_descriptors, num_file_descriptors)
|
||||
int socket: the Unix socket
|
||||
const int *file_descriptors: array of file descriptors
|
||||
unsigned num_file_descriptors: number of file descriptors
|
||||
Return value: 0 for success, -1 for failure
|
||||
|
||||
Sends several file descriptors on a socket.
|
||||
In case of failure, errno is set; the possible values are the ones of the
|
||||
sendmsg(2) system call.
|
||||
The maximum number of file descriptors that can be sent using this
|
||||
function is ANCIL_MAX_N_FDS; the behavior is undefined in case of
|
||||
overflow, probably a stack corruption.
|
||||
|
||||
|
||||
int ancil_recv_fds(socket, file_descriptors, num_file_descriptors)
|
||||
int socket: the Unix socket
|
||||
int *file_descriptors: return array of file descriptors
|
||||
unsigned num_file_descriptors: number of file descriptors
|
||||
Return value: number of received fd for success, -1 for failure
|
||||
|
||||
Receives several file descriptors from a socket, no more than
|
||||
num_file_descriptors.
|
||||
In case of success, the received file descriptors are stored in the array
|
||||
pointed to by file_descriptors.
|
||||
In case of failure, errno is set; the possible values are the ones of the
|
||||
recvmsg(2) system call.
|
||||
The maximum number of file descriptors that can be received using this
|
||||
function is ANCIL_MAX_N_FDS; the behavior is undefined in case of
|
||||
overflow, probably a stack corruption.
|
||||
The behavior is undefined if the recv_fds does not match a send_fd* on
|
||||
the other side, or if the number of received file descriptors is more than
|
||||
num_file_descriptors.
|
||||
|
||||
|
||||
int ancil_send_fds_with_buffer(socket, fds, num, buffer)
|
||||
int socket: the Unix socket
|
||||
const int *fds: array of file descriptors
|
||||
unsigned num: number of file descriptors
|
||||
void *buffer: buffer to hold the system data structures
|
||||
Return value: 0 for success, -1 for failure
|
||||
|
||||
Sends several file descriptors on a socket.
|
||||
In case of failure, errno is set; the possible values are the ones of the
|
||||
sendmsg(2) system call.
|
||||
The buffer argument must point to a memory area large enough to hold the
|
||||
system data structures, see ANCIL_FD_BUFFER.
|
||||
|
||||
|
||||
int ancil_send_fds_with_buffer(socket, fds, num, buffer)
|
||||
int socket: the Unix socket
|
||||
int *fds: return array of file descriptors
|
||||
unsigned num: number of file descriptors
|
||||
void *buffer: buffer to hold the system data structures
|
||||
Return value: number of received fd for success, -1 for failure
|
||||
|
||||
Receives several file descriptors from a socket, no more than
|
||||
num_file_descriptors.
|
||||
In case of success, the received file descriptors are stored in the array
|
||||
pointed to by file_descriptors.
|
||||
In case of failure, errno is set; the possible values are the ones of the
|
||||
recvmsg(2) system call.
|
||||
The behavior is undefined if the recv_fds does not match a send_fd* on
|
||||
the other side, or if the number of received file descriptors is more than
|
||||
num_file_descriptors.
|
||||
The buffer argument must point to a memory area large enough to hold the
|
||||
system data structures, see ANCIL_FD_BUFFER.
|
||||
|
||||
|
||||
ANCIL_MAX_N_FDS
|
||||
|
||||
Maximum number of file descriptors that can be sent with the sent_fds and
|
||||
recv_fds functions. If you have to send more at once, use the
|
||||
*_with_buffer versions. The value is enough to send "quite a few" file
|
||||
descriptors.
|
||||
|
||||
|
||||
ANCIL_FD_BUFFER(n)
|
||||
int n: number of file descriptors
|
||||
|
||||
Expands to a structure data type large enough to hold the system data
|
||||
structures for n file descriptors. So the address of a variable declared
|
||||
of type ANCIL_FD_BUFFER(n) is suitable as the buffer argument for
|
||||
*_with_buffer on n file descriptors.
|
||||
To use this macro, you need <sys/types.h> and <sys/socket.h>. Bevare: with
|
||||
Solaris, the _XPG4_2 macro must be defined before sys/socket is included.
|
||||
|
||||
|
||||
Tuning the compilation
|
||||
|
||||
This library is designed to be included in projects, not installed in
|
||||
/usr/lib. If your project does not use some of the functions, the
|
||||
TUNE_OPTS variable in the Makefile allows not to build them. It is a list
|
||||
of proprocessor options:
|
||||
|
||||
-DNDEBUG: turn assertions off (see assert(3))
|
||||
-DSPARE_SEND_FDS: do not build ancil_send_fds
|
||||
-DSPARE_SEND_FD: do not build ancil_send_fd
|
||||
-DSPARE_RECV_FDS: do not build ancil_recv_fds
|
||||
-DSPARE_RECV_FD: do not build ancil_recv_fd
|
||||
@@ -0,0 +1,21 @@
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are met:
|
||||
|
||||
1. Redistributions of source code must retain the above copyright notice,
|
||||
this list of conditions and the following disclaimer.
|
||||
2. Redistributions in binary form must reproduce the above copyright
|
||||
notice, this list of conditions and the following disclaimer in the
|
||||
documentation and/or other materials provided with the distribution.
|
||||
3. The name of the author may not be used to endorse or promote products
|
||||
derived from this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
@@ -0,0 +1,73 @@
|
||||
###########################################################################
|
||||
# libancillary - black magic on Unix domain sockets
|
||||
# (C) Nicolas George
|
||||
# Makefile - guess what
|
||||
###########################################################################
|
||||
|
||||
# Redistribution and use in source and binary forms, with or without
|
||||
# modification, are permitted provided that the following conditions are met:
|
||||
#
|
||||
# 1. Redistributions of source code must retain the above copyright notice,
|
||||
# this list of conditions and the following disclaimer.
|
||||
# 2. Redistributions in binary form must reproduce the above copyright
|
||||
# notice, this list of conditions and the following disclaimer in the
|
||||
# documentation and/or other materials provided with the distribution.
|
||||
# 3. The name of the author may not be used to endorse or promote products
|
||||
# derived from this software without specific prior written permission.
|
||||
#
|
||||
# THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
# WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
# MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
# EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
# SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
# PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
# OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
# WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
# OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
# ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
|
||||
CC=gcc
|
||||
CFLAGS=-Wall -g -O2
|
||||
LDFLAGS=
|
||||
LIBS=
|
||||
AR=ar
|
||||
RANLIB=ranlib
|
||||
RM=rm
|
||||
CP=cp
|
||||
MKDIR=mkdir
|
||||
TAR=tar
|
||||
GZIP=gzip -9
|
||||
|
||||
NAME=libancillary
|
||||
DISTRIBUTION=API COPYING Makefile ancillary.h fd_send.c fd_recv.c test.c
|
||||
VERSION=0.9.1
|
||||
|
||||
OBJECTS=fd_send.o fd_recv.o
|
||||
|
||||
TUNE_OPTS=-DNDEBUG
|
||||
#TUNE_OPTS=-DNDEBUG \
|
||||
-DSPARE_SEND_FDS -DSPARE_SEND_FD -DSPARE_RECV_FDS -DSPARE_RECV_FD
|
||||
|
||||
.c.o:
|
||||
$(CC) -c $(CFLAGS) $(TUNE_OPTS) $<
|
||||
|
||||
all: libancillary.a
|
||||
|
||||
libancillary.a: $(OBJECTS)
|
||||
$(AR) cr $@ $(OBJECTS)
|
||||
$(RANLIB) $@
|
||||
|
||||
fd_send.o: ancillary.h
|
||||
fd_recv.o: ancillary.h
|
||||
|
||||
test: test.c libancillary.a
|
||||
$(CC) -o $@ $(CFLAGS) $(LDFLAGS) -L. test.c -lancillary $(LIBS)
|
||||
|
||||
clean:
|
||||
-$(RM) -f *.o *.a test
|
||||
|
||||
dist:
|
||||
$(MKDIR) $(NAME)-$(VERSION)
|
||||
$(CP) $(DISTRIBUTION) $(NAME)-$(VERSION)
|
||||
$(TAR) -cf - $(NAME)-$(VERSION) | $(GZIP) > $(NAME)-$(VERSION).tar.gz
|
||||
$(RM) -rf $(NAME)-$(VERSION)
|
||||
@@ -0,0 +1,131 @@
|
||||
/***************************************************************************
|
||||
* libancillary - black magic on Unix domain sockets
|
||||
* (C) Nicolas George
|
||||
* ancillary.c - public header
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef ANCILLARY_H__
|
||||
#define ANCILLARY_H__
|
||||
|
||||
#ifdef __cplusplus
|
||||
extern "C" {
|
||||
#endif
|
||||
|
||||
/***************************************************************************
|
||||
* Start of the readable part.
|
||||
***************************************************************************/
|
||||
|
||||
#define ANCIL_MAX_N_FDS 960
|
||||
/*
|
||||
* Maximum number of fds that can be sent or received using the "esay"
|
||||
* functions; this is so that all can fit in one page.
|
||||
*/
|
||||
|
||||
extern int
|
||||
ancil_send_fds_with_buffer(int, const int *, unsigned, void *);
|
||||
/*
|
||||
* ancil_send_fds_with_buffer(sock, n_fds, fds, buffer)
|
||||
*
|
||||
* Sends the file descriptors in the array pointed by fds, of length n_fds
|
||||
* on the socket sock.
|
||||
* buffer is a writeable memory area large enough to hold the required data
|
||||
* structures.
|
||||
* Returns: -1 and errno in case of error, 0 in case of success.
|
||||
*/
|
||||
|
||||
extern int
|
||||
ancil_recv_fds_with_buffer(int, int *, unsigned, void *);
|
||||
/*
|
||||
* ancil_recv_fds_with_buffer(sock, n_fds, fds, buffer)
|
||||
*
|
||||
* Receives *n_fds file descriptors into the array pointed by fds
|
||||
* from the socket sock.
|
||||
* buffer is a writeable memory area large enough to hold the required data
|
||||
* structures.
|
||||
* Returns: -1 and errno in case of error, the actual number of received fd
|
||||
* in case of success
|
||||
*/
|
||||
|
||||
#define ANCIL_FD_BUFFER(n) \
|
||||
struct { \
|
||||
struct cmsghdr h; \
|
||||
int fd[n]; \
|
||||
}
|
||||
/* ANCIL_FD_BUFFER(n)
|
||||
*
|
||||
* A structure type suitable to be used as buffer for n file descriptors.
|
||||
* Requires <sys/socket.h>.
|
||||
* Example:
|
||||
* ANCIL_FD_BUFFER(42) buffer;
|
||||
* ancil_recv_fds_with_buffer(sock, 42, my_fds, &buffer);
|
||||
*/
|
||||
|
||||
extern int
|
||||
ancil_send_fds(int, const int *, unsigned);
|
||||
/*
|
||||
* ancil_send_fds(sock, n_fds, fds)
|
||||
*
|
||||
* Sends the file descriptors in the array pointed by fds, of length n_fds
|
||||
* on the socket sock.
|
||||
* n_fds must not be greater than ANCIL_MAX_N_FDS.
|
||||
* Returns: -1 and errno in case of error, 0 in case of success.
|
||||
*/
|
||||
|
||||
extern int
|
||||
ancil_recv_fds(int, int *, unsigned);
|
||||
/*
|
||||
* ancil_recv_fds(sock, n_fds, fds)
|
||||
*
|
||||
* Receives *n_fds file descriptors into the array pointed by fds
|
||||
* from the socket sock.
|
||||
* *n_fds must not be greater than ANCIL_MAX_N_FDS.
|
||||
* Returns: -1 and errno in case of error, the actual number of received fd
|
||||
* in case of success.
|
||||
*/
|
||||
|
||||
|
||||
extern int
|
||||
ancil_send_fd(int, int);
|
||||
/* ancil_recv_fd(sock, fd);
|
||||
*
|
||||
* Sends the file descriptor fd on the socket sock.
|
||||
* Returns : -1 and errno in case of error, 0 in case of success.
|
||||
*/
|
||||
|
||||
extern int
|
||||
ancil_recv_fd(int, int *);
|
||||
/* ancil_send_fd(sock, &fd);
|
||||
*
|
||||
* Receives the file descriptor fd from the socket sock.
|
||||
* Returns : -1 and errno in case of error, 0 in case of success.
|
||||
*/
|
||||
|
||||
#ifdef __cplusplus
|
||||
}
|
||||
#endif
|
||||
|
||||
#endif /* ANCILLARY_H__ */
|
||||
@@ -0,0 +1,98 @@
|
||||
/***************************************************************************
|
||||
* libancillary - black magic on Unix domain sockets
|
||||
* (C) Nicolas George
|
||||
* fd_send.c - receiving file descriptors
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _XPG4_2 /* Solaris sucks */
|
||||
# define _XPG4_2
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <assert.h>
|
||||
#if defined(__FreeBSD__)
|
||||
# include <sys/param.h> /* FreeBSD sucks */
|
||||
#endif
|
||||
|
||||
#include "ancillary.h"
|
||||
|
||||
int
|
||||
ancil_recv_fds_with_buffer(int sock, int *fds, unsigned n_fds, void *buffer)
|
||||
{
|
||||
struct msghdr msghdr;
|
||||
char nothing;
|
||||
struct iovec nothing_ptr;
|
||||
struct cmsghdr *cmsg;
|
||||
int i;
|
||||
|
||||
nothing_ptr.iov_base = ¬hing;
|
||||
nothing_ptr.iov_len = 1;
|
||||
msghdr.msg_name = NULL;
|
||||
msghdr.msg_namelen = 0;
|
||||
msghdr.msg_iov = ¬hing_ptr;
|
||||
msghdr.msg_iovlen = 1;
|
||||
msghdr.msg_flags = 0;
|
||||
msghdr.msg_control = buffer;
|
||||
msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
|
||||
cmsg = CMSG_FIRSTHDR(&msghdr);
|
||||
cmsg->cmsg_len = msghdr.msg_controllen;
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
for(i = 0; i < n_fds; i++)
|
||||
((int *)CMSG_DATA(cmsg))[i] = -1;
|
||||
|
||||
if(recvmsg(sock, &msghdr, 0) < 0)
|
||||
return(-1);
|
||||
for(i = 0; i < n_fds; i++)
|
||||
fds[i] = ((int *)CMSG_DATA(cmsg))[i];
|
||||
n_fds = (msghdr.msg_controllen - sizeof(struct cmsghdr)) / sizeof(int);
|
||||
return(n_fds);
|
||||
}
|
||||
|
||||
#ifndef SPARE_RECV_FDS
|
||||
int
|
||||
ancil_recv_fds(int sock, int *fd, unsigned n_fds)
|
||||
{
|
||||
ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer;
|
||||
|
||||
assert(n_fds <= ANCIL_MAX_N_FDS);
|
||||
return(ancil_recv_fds_with_buffer(sock, fd, n_fds, &buffer));
|
||||
}
|
||||
#endif /* SPARE_RECV_FDS */
|
||||
|
||||
#ifndef SPARE_RECV_FD
|
||||
int
|
||||
ancil_recv_fd(int sock, int *fd)
|
||||
{
|
||||
ANCIL_FD_BUFFER(1) buffer;
|
||||
|
||||
return(ancil_recv_fds_with_buffer(sock, fd, 1, &buffer) == 1 ? 0 : -1);
|
||||
}
|
||||
#endif /* SPARE_RECV_FD */
|
||||
@@ -0,0 +1,92 @@
|
||||
/***************************************************************************
|
||||
* libancillary - black magic on Unix domain sockets
|
||||
* (C) Nicolas George
|
||||
* fd_send.c - sending file descriptors
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#ifndef _XPG4_2 /* Solaris sucks */
|
||||
# define _XPG4_2
|
||||
#endif
|
||||
|
||||
#include <stdlib.h>
|
||||
#include <sys/types.h>
|
||||
#include <sys/socket.h>
|
||||
#include <sys/uio.h>
|
||||
#include <assert.h>
|
||||
#if defined(__FreeBSD__)
|
||||
# include <sys/param.h> /* FreeBSD sucks */
|
||||
#endif
|
||||
|
||||
#include "ancillary.h"
|
||||
|
||||
int
|
||||
ancil_send_fds_with_buffer(int sock, const int *fds, unsigned n_fds, void *buffer)
|
||||
{
|
||||
struct msghdr msghdr;
|
||||
char nothing = '!';
|
||||
struct iovec nothing_ptr;
|
||||
struct cmsghdr *cmsg;
|
||||
int i;
|
||||
|
||||
nothing_ptr.iov_base = ¬hing;
|
||||
nothing_ptr.iov_len = 1;
|
||||
msghdr.msg_name = NULL;
|
||||
msghdr.msg_namelen = 0;
|
||||
msghdr.msg_iov = ¬hing_ptr;
|
||||
msghdr.msg_iovlen = 1;
|
||||
msghdr.msg_flags = 0;
|
||||
msghdr.msg_control = buffer;
|
||||
msghdr.msg_controllen = sizeof(struct cmsghdr) + sizeof(int) * n_fds;
|
||||
cmsg = CMSG_FIRSTHDR(&msghdr);
|
||||
cmsg->cmsg_len = msghdr.msg_controllen;
|
||||
cmsg->cmsg_level = SOL_SOCKET;
|
||||
cmsg->cmsg_type = SCM_RIGHTS;
|
||||
for(i = 0; i < n_fds; i++)
|
||||
((int *)CMSG_DATA(cmsg))[i] = fds[i];
|
||||
return(sendmsg(sock, &msghdr, 0) >= 0 ? 0 : -1);
|
||||
}
|
||||
|
||||
#ifndef SPARE_SEND_FDS
|
||||
int
|
||||
ancil_send_fds(int sock, const int *fds, unsigned n_fds)
|
||||
{
|
||||
ANCIL_FD_BUFFER(ANCIL_MAX_N_FDS) buffer;
|
||||
|
||||
assert(n_fds <= ANCIL_MAX_N_FDS);
|
||||
return(ancil_send_fds_with_buffer(sock, fds, n_fds, &buffer));
|
||||
}
|
||||
#endif /* SPARE_SEND_FDS */
|
||||
|
||||
#ifndef SPARE_SEND_FD
|
||||
int
|
||||
ancil_send_fd(int sock, int fd)
|
||||
{
|
||||
ANCIL_FD_BUFFER(1) buffer;
|
||||
|
||||
return(ancil_send_fds_with_buffer(sock, &fd, 1, &buffer));
|
||||
}
|
||||
#endif /* SPARE_SEND_FD */
|
||||
@@ -0,0 +1,112 @@
|
||||
/***************************************************************************
|
||||
* libancillary - black magic on Unix domain sockets
|
||||
* (C) Nicolas George
|
||||
* test.c - testing and example program
|
||||
***************************************************************************/
|
||||
|
||||
/*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are met:
|
||||
*
|
||||
* 1. Redistributions of source code must retain the above copyright notice,
|
||||
* this list of conditions and the following disclaimer.
|
||||
* 2. Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in the
|
||||
* documentation and/or other materials provided with the distribution.
|
||||
* 3. The name of the author may not be used to endorse or promote products
|
||||
* derived from this software without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
|
||||
* WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
|
||||
* MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO
|
||||
* EVENT SHALL THE AUTHOR BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO,
|
||||
* PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS;
|
||||
* OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
|
||||
* WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR
|
||||
* OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF
|
||||
* ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <sys/stat.h>
|
||||
#include <sys/types.h>
|
||||
#include <unistd.h>
|
||||
#include <sys/wait.h>
|
||||
#include <sys/socket.h>
|
||||
#include "ancillary.h"
|
||||
|
||||
void child_process(int sock)
|
||||
{
|
||||
int fd;
|
||||
int fds[3], nfds;
|
||||
char b[] = "This is on the received fd!\n";
|
||||
|
||||
if(ancil_recv_fd(sock, &fd)) {
|
||||
perror("ancil_recv_fd");
|
||||
exit(1);
|
||||
} else {
|
||||
printf("Received fd: %d\n", fd);
|
||||
}
|
||||
write(fd, b, sizeof(b));
|
||||
close(fd);
|
||||
sleep(2);
|
||||
|
||||
nfds = ancil_recv_fds(sock, fds, 3);
|
||||
if(nfds < 0) {
|
||||
perror("ancil_recv_fds");
|
||||
exit(1);
|
||||
} else {
|
||||
printf("Received %d/3 fds : %d %d %d.\n", nfds,
|
||||
fds[0], fds[1], fds[2]);
|
||||
}
|
||||
}
|
||||
|
||||
void parent_process(int sock)
|
||||
{
|
||||
int fds[2] = { 1, 2 };
|
||||
|
||||
if(ancil_send_fd(sock, 1)) {
|
||||
perror("ancil_send_fd");
|
||||
exit(1);
|
||||
} else {
|
||||
printf("Sent fd.\n");
|
||||
}
|
||||
sleep(1);
|
||||
|
||||
if(ancil_send_fds(sock, fds, 2)) {
|
||||
perror("ancil_send_fds");
|
||||
exit(1);
|
||||
} else {
|
||||
printf("Sent two fds.\n");
|
||||
}
|
||||
}
|
||||
|
||||
int main(void)
|
||||
{
|
||||
int sock[2];
|
||||
|
||||
if(socketpair(PF_UNIX, SOCK_STREAM, 0, sock)) {
|
||||
perror("socketpair");
|
||||
exit(1);
|
||||
} else {
|
||||
printf("Established socket pair: (%d, %d)\n", sock[0], sock[1]);
|
||||
}
|
||||
|
||||
switch(fork()) {
|
||||
case 0:
|
||||
close(sock[0]);
|
||||
child_process(sock[1]);
|
||||
break;
|
||||
case -1:
|
||||
perror("fork");
|
||||
exit(1);
|
||||
default:
|
||||
close(sock[1]);
|
||||
parent_process(sock[0]);
|
||||
wait(NULL);
|
||||
break;
|
||||
}
|
||||
return(0);
|
||||
}
|
||||
Submodule src/main/jni/shadowsocks-libev updated: 02050e49e7...5d4db4f093
@@ -5,8 +5,15 @@
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <unistd.h>
|
||||
#include <errno.h>
|
||||
#include <cpu-features.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)
|
||||
@@ -36,9 +43,47 @@ void Java_com_github_shadowsocks_system_exec(JNIEnv *env, jobject thiz, jstring
|
||||
env->ReleaseStringUTFChars(cmd, str);
|
||||
}
|
||||
|
||||
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) {
|
||||
int fd;
|
||||
struct sockaddr_un addr;
|
||||
|
||||
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, "#shadowsocks_tunfd", sizeof(addr.sun_path)-1);
|
||||
addr.sun_path[0] = 0;
|
||||
|
||||
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);
|
||||
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", "(I)I",
|
||||
(void*) Java_com_github_shadowsocks_system_sendfd },
|
||||
{ "exec", "(Ljava/lang/String;)V",
|
||||
(void*) Java_com_github_shadowsocks_system_exec },
|
||||
{ "getABI", "()Ljava/lang/String;",
|
||||
|
||||
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
Executable
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.8 KiB |
BIN
Binary file not shown.
|
After Width: | Height: | Size: 2.9 KiB |
@@ -9,7 +9,7 @@
|
||||
android:layout_width="wrap_content"
|
||||
android:layout_height="wrap_content"
|
||||
android:gravity="center_vertical"
|
||||
android:textSize="24sp"
|
||||
android:textSize="26sp"
|
||||
android:textColor="@color/title"
|
||||
android:text="@string/screen_name"
|
||||
android:layout_alignParentLeft="true"
|
||||
|
||||
@@ -0,0 +1,96 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<!-- File created by the Android Action Bar Style Generator
|
||||
|
||||
Copyright (C) 2011 The Android Open Source Project
|
||||
Copyright (C) 2012 readyState Software Ltd
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
||||
-->
|
||||
|
||||
<resources>
|
||||
|
||||
<style name="Theme.Shadow" parent="@android:style/Theme.Material.Light.DarkActionBar">
|
||||
<item name="android:actionBarItemBackground">@drawable/selectable_background_shadow</item>
|
||||
<item name="android:popupMenuStyle">@style/PopupMenu.Shadow</item>
|
||||
<item name="android:dropDownListViewStyle">@style/DropDownListView.Shadow</item>
|
||||
<item name="android:actionBarTabStyle">@style/ActionBarTabStyle.Shadow</item>
|
||||
<item name="android:actionDropDownStyle">@style/DropDownNav.Shadow</item>
|
||||
<item name="android:actionBarStyle">@style/ActionBar.Solid.Shadow</item>
|
||||
<item name="android:actionModeBackground">@drawable/cab_background_top_shadow</item>
|
||||
<item name="android:actionModeSplitBackground">@drawable/cab_background_bottom_shadow</item>
|
||||
<!-- Light.DarkActionBar specific -->
|
||||
<item name="android:actionBarWidgetTheme">@style/Theme.Shadow.Widget</item>
|
||||
<item name="android:textAppearanceLargePopupMenu">@style/m_textAppearanceLargePopupMenu</item>
|
||||
<item name="android:textAppearanceSmallPopupMenu" >@style/m_textAppearanceSmallPopupMenu</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBar.Solid.Shadow" parent="@android:style/Widget.Material.ActionBar">
|
||||
<item name="android:background">@drawable/ab_background_textured_shadow</item>
|
||||
<item name="android:backgroundStacked">@drawable/ab_stacked_solid_shadow</item>
|
||||
<item name="android:backgroundSplit">@drawable/ab_background_textured_shadow</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBar.Transparent.Shadow" parent="@android:style/Widget.Material.ActionBar">
|
||||
<item name="android:background">@drawable/ab_transparent_shadow</item>
|
||||
</style>
|
||||
|
||||
<style name="PopupMenu.Shadow" parent="@android:style/Widget.Material.ListPopupWindow">
|
||||
<item name="android:popupBackground">@drawable/menu_dropdown_panel_shadow</item>
|
||||
</style>
|
||||
|
||||
<style name="m_textAppearanceSmallPopupMenu" parent="@android:style/TextAppearance.Material.Widget.PopupMenu.Small">
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="m_textAppearanceLargePopupMenu" parent="@android:style/TextAppearance.Material.Widget.PopupMenu.Large">
|
||||
<item name="android:textColor">@android:color/black</item>
|
||||
</style>
|
||||
|
||||
<style name="DropDownListView.Shadow" parent="@android:style/Widget.Material.ListView.DropDown">
|
||||
<item name="android:listSelector">@drawable/selectable_background_shadow</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBarTabStyle.Shadow" parent="@android:style/Widget.Material.ActionBar.TabView">
|
||||
<item name="android:background">@drawable/tab_indicator_ab_shadow</item>
|
||||
</style>
|
||||
|
||||
<style name="DropDownNav.Shadow" parent="@android:style/Widget.Material.Spinner">
|
||||
<item name="android:background">@drawable/spinner_background_ab_shadow</item>
|
||||
<item name="android:popupBackground">@drawable/menu_dropdown_panel_shadow</item>
|
||||
<item name="android:dropDownSelector">@drawable/selectable_background_shadow</item>
|
||||
</style>
|
||||
|
||||
<!-- this style is only referenced in a Light.DarkActionBar based theme -->
|
||||
<style name="Theme.Shadow.Widget" parent="@android:style/Theme.Material">
|
||||
<item name="android:popupMenuStyle">@style/PopupMenu.Shadow</item>
|
||||
<item name="android:dropDownListViewStyle">@style/DropDownListView.Shadow</item>
|
||||
<item name="android:textColor">@android:color/white</item>
|
||||
<item name="android:textAppearanceLargePopupMenu">@style/m_textAppearanceLargePopupMenu</item>
|
||||
<item name="android:textAppearanceSmallPopupMenu" >@style/m_textAppearanceSmallPopupMenu</item>
|
||||
</style>
|
||||
|
||||
<style name="PopupTheme" parent="@android:style/Theme.Material.Light">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<item name="android:windowIsFloating">false</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
<item name="android:windowAnimationStyle">@android:style/Animation.Dialog</item>
|
||||
<item name="android:windowSoftInputMode">stateAlwaysHidden</item>
|
||||
<item name="android:windowActionModeOverlay">true</item>
|
||||
<item name="android:colorBackgroundCacheHint">@null</item>
|
||||
<item name="android:windowCloseOnTouchOutside">true</item>
|
||||
<item name="android:windowIsTranslucent">true</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
@@ -49,7 +49,8 @@
|
||||
<string name="stopping">正在关闭...</string>
|
||||
<string name="vpn_error">后台服务启动失败:%s</string>
|
||||
<string name="ok_iknow">确定</string>
|
||||
<string name="port_alert">端口号应大于1024</string>
|
||||
<string name="close">关闭</string>
|
||||
<string name="port_alert">本地端口号应大于1024</string>
|
||||
<string name="port_empty">端口号不能为空</string>
|
||||
<string name="proxy_empty">代理服务器地址不能为空</string>
|
||||
<string name="crash_alert">检测到意外退出,状态已被重置。</string>
|
||||
@@ -67,6 +68,7 @@
|
||||
<string name="about_title">影梭(shadowsocks)%s</string>
|
||||
<string name="flush_dnscache">刷新 DNS 缓存</string>
|
||||
<string name="flushing">刷新中...</string>
|
||||
<string name="qrcode">二维码</string>
|
||||
<string name="add_profile">添加配置文件</string>
|
||||
|
||||
<string name="enable_nat">启用 NAT 模式.</string>
|
||||
|
||||
@@ -85,7 +85,41 @@
|
||||
<item>101.0.0.0/8</item>
|
||||
<item>102.0.0.0/7</item>
|
||||
<item>104.0.0.0/5</item>
|
||||
<item>112.0.0.0/5</item>
|
||||
<item>112.0.0.0/10</item>
|
||||
<item>112.64.0.0/11</item>
|
||||
<item>112.96.0.0/12</item>
|
||||
<item>112.112.0.0/13</item>
|
||||
<item>112.120.0.0/14</item>
|
||||
<item>112.124.0.0/19</item>
|
||||
<item>112.124.32.0/21</item>
|
||||
<item>112.124.40.0/22</item>
|
||||
<item>112.124.44.0/23</item>
|
||||
<item>112.124.46.0/24</item>
|
||||
<item>112.124.48.0/20</item>
|
||||
<item>112.124.64.0/18</item>
|
||||
<item>112.124.128.0/17</item>
|
||||
<item>112.125.0.0/16</item>
|
||||
<item>112.126.0.0/15</item>
|
||||
<item>112.128.0.0/9</item>
|
||||
<item>113.0.0.0/8</item>
|
||||
<item>114.0.0.0/10</item>
|
||||
<item>114.64.0.0/11</item>
|
||||
<item>114.96.0.0/12</item>
|
||||
<item>114.112.0.0/15</item>
|
||||
<item>114.114.0.0/18</item>
|
||||
<item>114.114.64.0/19</item>
|
||||
<item>114.114.96.0/20</item>
|
||||
<item>114.114.112.0/23</item>
|
||||
<item>114.114.115.0/24</item>
|
||||
<item>114.114.116.0/22</item>
|
||||
<item>114.114.120.0/21</item>
|
||||
<item>114.114.128.0/17</item>
|
||||
<item>114.115.0.0/16</item>
|
||||
<item>114.116.0.0/14</item>
|
||||
<item>114.120.0.0/13</item>
|
||||
<item>114.128.0.0/9</item>
|
||||
<item>115.0.0.0/8</item>
|
||||
<item>116.0.0.0/6</item>
|
||||
<item>120.0.0.0/6</item>
|
||||
<item>124.0.0.0/7</item>
|
||||
<item>126.0.0.0/8</item>
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -65,7 +65,8 @@
|
||||
|
||||
<!-- alert category -->
|
||||
<string name="ok_iknow">OK</string>
|
||||
<string name="port_alert">The port number should be greater than 1024</string>
|
||||
<string name="close">Close</string>
|
||||
<string name="port_alert">The local port number should be greater than 1024</string>
|
||||
<string name="port_empty">Port should not be empty</string>
|
||||
<string name="proxy_empty">Proxy should not be empty</string>
|
||||
<string name="crash_alert">An unexpected exit detected, the context has been reset.
|
||||
@@ -84,6 +85,7 @@
|
||||
<string name="recovery">Reset</string>
|
||||
<string name="about">About</string>
|
||||
<string name="about_title">Shadowsocks %s</string>
|
||||
<string name="qrcode">QR Code</string>
|
||||
<string name="flush_dnscache">Flush DNS Cache</string>
|
||||
<string name="flushing">Flushing</string>
|
||||
<string name="add_profile">Add Profile</string>
|
||||
|
||||
@@ -19,7 +19,9 @@
|
||||
<style name="Theme.AppManager" parent="@style/Theme.Shadow">
|
||||
</style>
|
||||
|
||||
<style name="PopupTheme" parent="@android:style/Theme.Translucent.NoTitleBar">
|
||||
<style name="PopupTheme" parent="@android:style/Theme.Holo.Light">
|
||||
<item name="android:windowActionBar">false</item>
|
||||
<item name="android:windowNoTitle">true</item>
|
||||
<item name="android:windowFrame">@null</item>
|
||||
<item name="android:windowIsFloating">false</item>
|
||||
<item name="android:windowContentOverlay">@null</item>
|
||||
@@ -72,4 +74,4 @@
|
||||
<item name="mdMenuSize">250dp</item>
|
||||
</style>
|
||||
|
||||
</resources>
|
||||
</resources>
|
||||
|
||||
@@ -35,7 +35,7 @@
|
||||
<item name="android:textAppearanceSmallPopupMenu" >@style/m_textAppearanceSmallPopupMenu</item>
|
||||
</style>
|
||||
|
||||
<style name="ActionBar.Solid.Shadow" parent="@android:style/Widget.Holo.Light.ActionBar.Solid.Inverse">
|
||||
<style name="ActionBar.Solid.Shadow" parent="@android:style/Widget.Holo.ActionBar">
|
||||
<item name="android:background">@drawable/ab_background_textured_shadow</item>
|
||||
<item name="android:backgroundStacked">@drawable/ab_stacked_solid_shadow</item>
|
||||
<item name="android:backgroundSplit">@drawable/ab_background_textured_shadow</item>
|
||||
|
||||
@@ -280,6 +280,7 @@ class AppManager extends Activity with OnCheckedChangeListener with OnClickListe
|
||||
val actionBar = getActionBar()
|
||||
actionBar.setTitle(R.string.proxied_help)
|
||||
actionBar.setDisplayHomeAsUpEnabled(true)
|
||||
actionBar.setDisplayShowHomeEnabled(false)
|
||||
this.setContentView(R.layout.layout_apps)
|
||||
this.overlay = View.inflate(this, R.layout.overlay, null).asInstanceOf[TextView]
|
||||
getWindowManager.addView(overlay, new
|
||||
|
||||
@@ -41,6 +41,7 @@ package com.github.shadowsocks
|
||||
import java.util
|
||||
import java.io.{OutputStream, InputStream, ByteArrayInputStream, ByteArrayOutputStream, IOException, FileOutputStream}
|
||||
import java.util.Locale
|
||||
import java.lang.Math
|
||||
|
||||
import android.app.backup.BackupManager
|
||||
import android.app.{Activity, AlertDialog, ProgressDialog}
|
||||
@@ -66,6 +67,10 @@ import com.google.android.gms.analytics.HitBuilders
|
||||
import com.google.zxing.integration.android.IntentIntegrator
|
||||
import com.nostra13.universalimageloader.core.download.BaseImageDownloader
|
||||
import net.simonvt.menudrawer.MenuDrawer
|
||||
import com.joanzapata.android.iconify.Iconify
|
||||
import com.joanzapata.android.iconify.Iconify.IconValue
|
||||
import com.joanzapata.android.iconify.IconDrawable
|
||||
import net.glxn.qrgen.android.QRCode
|
||||
|
||||
import scala.collection.mutable.{ArrayBuffer, ListBuffer}
|
||||
import scala.concurrent.ops._
|
||||
@@ -180,6 +185,7 @@ class Shadowsocks
|
||||
// Variables
|
||||
var switchButton: Switch = null
|
||||
var progressDialog: ProgressDialog = null
|
||||
var progressTag = -1
|
||||
var state = State.INIT
|
||||
var prepared = false
|
||||
var currentProfile = new Profile
|
||||
@@ -266,9 +272,10 @@ class Shadowsocks
|
||||
switchButton.setOnCheckedChangeListener(this)
|
||||
}
|
||||
|
||||
private def showProgress(msg: String): Handler = {
|
||||
private def showProgress(msg: Int): Handler = {
|
||||
clearDialog()
|
||||
progressDialog = ProgressDialog.show(this, "", msg, true, false)
|
||||
progressDialog = ProgressDialog.show(this, "", getString(msg), true, false)
|
||||
progressTag = msg
|
||||
new Handler {
|
||||
override def handleMessage(msg: Message) {
|
||||
clearDialog()
|
||||
@@ -366,24 +373,39 @@ class Shadowsocks
|
||||
|
||||
def isTextEmpty(s: String, msg: String): Boolean = {
|
||||
if (s == null || s.length <= 0) {
|
||||
new SnackBar.Builder(this).withMessage(msg).withStyle(SnackBar.Style.ALERT).show()
|
||||
new SnackBar.Builder(this)
|
||||
.withMessage(msg)
|
||||
.withActionMessageId(R.string.error)
|
||||
.withStyle(SnackBar.Style.ALERT)
|
||||
.withDuration(SnackBar.LONG_SNACK)
|
||||
.show()
|
||||
return true
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
def cancelStart() {
|
||||
handler.postDelayed(new Runnable {
|
||||
override def run() {
|
||||
clearDialog()
|
||||
changeSwitch(checked = false)
|
||||
}
|
||||
}, 1000)
|
||||
clearDialog()
|
||||
changeSwitch(checked = false)
|
||||
}
|
||||
|
||||
def isReady(): Boolean = {
|
||||
if (!checkText(Key.proxy)) return false
|
||||
if (!checkText(Key.sitekey)) return false
|
||||
if (!checkNumber(Key.localPort, low = false)) return false
|
||||
if (!checkNumber(Key.remotePort, low = true)) return false
|
||||
if (bgService == null) return false
|
||||
true
|
||||
}
|
||||
|
||||
def prepareStartService() {
|
||||
showProgress(getString(R.string.connecting))
|
||||
showProgress(R.string.connecting)
|
||||
spawn {
|
||||
if (!status.getBoolean(getVersionName, false)) {
|
||||
status.edit.putBoolean(getVersionName, true).commit()
|
||||
install()
|
||||
}
|
||||
|
||||
if (isVpnEnabled) {
|
||||
val intent = VpnService.prepare(this)
|
||||
if (intent != null) {
|
||||
@@ -392,9 +414,7 @@ class Shadowsocks
|
||||
onActivityResult(Shadowsocks.REQUEST_CONNECT, Activity.RESULT_OK, null)
|
||||
}
|
||||
} else {
|
||||
if (!serviceStart) {
|
||||
cancelStart()
|
||||
}
|
||||
serviceStart()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -403,7 +423,10 @@ class Shadowsocks
|
||||
if (compoundButton eq switchButton) {
|
||||
checked match {
|
||||
case true =>
|
||||
prepareStartService()
|
||||
if (isReady)
|
||||
prepareStartService()
|
||||
else
|
||||
changeSwitch(checked = false)
|
||||
case false =>
|
||||
serviceStop()
|
||||
}
|
||||
@@ -458,31 +481,31 @@ class Shadowsocks
|
||||
|
||||
addPreferencesFromResource(R.xml.pref_all)
|
||||
|
||||
// Update the profile
|
||||
if (!status.getBoolean(getVersionName, false)) {
|
||||
currentProfile = profileManager.create()
|
||||
}
|
||||
|
||||
// Initialize the profile
|
||||
currentProfile = {
|
||||
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
|
||||
}
|
||||
|
||||
// Update the profile
|
||||
if (!status.getBoolean(getVersionName, false)) {
|
||||
val h = showProgress(getString(R.string.initializing))
|
||||
status.edit.putBoolean(getVersionName, true).apply()
|
||||
spawn {
|
||||
reset()
|
||||
currentProfile = profileManager.create()
|
||||
h.sendEmptyMessage(0)
|
||||
}
|
||||
}
|
||||
|
||||
// Initialize drawer
|
||||
menuAdapter.setActiveId(settings.getInt(Key.profileId, -1))
|
||||
menuAdapter.setListener(this)
|
||||
listView.setAdapter(menuAdapter)
|
||||
drawer.setMenuView(listView)
|
||||
// The drawable that replaces the up indicator in the action bar
|
||||
drawer.setSlideDrawable(R.drawable.ic_drawer)
|
||||
// Whether the previous drawable should be shown
|
||||
drawer.setDrawerIndicatorEnabled(true)
|
||||
|
||||
if (Utils.isLollipopOrAbove) {
|
||||
drawer.setDrawerIndicatorEnabled(false)
|
||||
} else {
|
||||
// The drawable that replaces the up indicator in the action bar
|
||||
drawer.setSlideDrawable(R.drawable.ic_drawer)
|
||||
// Whether the previous drawable should be shown
|
||||
drawer.setDrawerIndicatorEnabled(true)
|
||||
}
|
||||
|
||||
if (!isSinglePane) {
|
||||
drawer.openMenu(false)
|
||||
}
|
||||
@@ -498,7 +521,14 @@ class Shadowsocks
|
||||
getActionBar.setCustomView(switchLayout)
|
||||
getActionBar.setDisplayShowTitleEnabled(false)
|
||||
getActionBar.setDisplayShowCustomEnabled(true)
|
||||
getActionBar.setIcon(R.drawable.ic_stat_shadowsocks)
|
||||
if (Utils.isLollipopOrAbove) {
|
||||
getWindow.addFlags(WindowManager.LayoutParams.FLAG_DRAWS_SYSTEM_BAR_BACKGROUNDS);
|
||||
getWindow.setStatusBarColor(getResources().getColor(R.color.grey3));
|
||||
getActionBar.setDisplayHomeAsUpEnabled(true)
|
||||
getActionBar.setHomeAsUpIndicator(R.drawable.ic_drawer)
|
||||
} else {
|
||||
getActionBar.setIcon(R.drawable.ic_stat_shadowsocks)
|
||||
}
|
||||
title.setOnLongClickListener(new OnLongClickListener {
|
||||
override def onLongClick(v: View): Boolean = {
|
||||
if (Utils.isLollipopOrAbove && bgService != null
|
||||
@@ -588,7 +618,7 @@ class Shadowsocks
|
||||
which match {
|
||||
case 0 =>
|
||||
dialog.dismiss()
|
||||
val h = showProgress(getString(R.string.loading))
|
||||
val h = showProgress(R.string.loading)
|
||||
h.postDelayed(new Runnable() {
|
||||
def run() {
|
||||
val integrator = new IntentIntegrator(Shadowsocks.this)
|
||||
@@ -609,10 +639,29 @@ class Shadowsocks
|
||||
builder.create().show()
|
||||
}
|
||||
|
||||
def reloadProfile() {
|
||||
drawer.closeMenu(true)
|
||||
|
||||
val h = showProgress(R.string.loading)
|
||||
|
||||
handler.postDelayed(new Runnable {
|
||||
def run() {
|
||||
currentProfile = {
|
||||
profileManager.getProfile(settings.getInt(Key.profileId, -1)) getOrElse currentProfile
|
||||
}
|
||||
menuAdapter.updateList(getMenuList, currentProfile.id)
|
||||
|
||||
updatePreferenceScreen()
|
||||
|
||||
h.sendEmptyMessage(0)
|
||||
}
|
||||
}, 600)
|
||||
}
|
||||
|
||||
def addProfile(profile: Profile) {
|
||||
drawer.closeMenu(true)
|
||||
|
||||
val h = showProgress(getString(R.string.loading))
|
||||
val h = showProgress(R.string.loading)
|
||||
|
||||
handler.postDelayed(new Runnable {
|
||||
def run() {
|
||||
@@ -631,7 +680,7 @@ class Shadowsocks
|
||||
def addProfile(id: Int) {
|
||||
drawer.closeMenu(true)
|
||||
|
||||
val h = showProgress(getString(R.string.loading))
|
||||
val h = showProgress(R.string.loading)
|
||||
|
||||
handler.postDelayed(new Runnable {
|
||||
def run() {
|
||||
@@ -649,7 +698,7 @@ class Shadowsocks
|
||||
def updateProfile(id: Int) {
|
||||
drawer.closeMenu(true)
|
||||
|
||||
val h = showProgress(getString(R.string.loading))
|
||||
val h = showProgress(R.string.loading)
|
||||
|
||||
handler.postDelayed(new Runnable {
|
||||
def run() {
|
||||
@@ -700,7 +749,7 @@ class Shadowsocks
|
||||
|
||||
def getProfileList: List[Item] = {
|
||||
val list = profileManager.getAllProfiles getOrElse List[Profile]()
|
||||
list.map(p => new Item(p.id, p.name, -1, updateProfile, delProfile))
|
||||
list.map(p => new IconItem(p.id, p.name, -1, updateProfile, delProfile))
|
||||
}
|
||||
|
||||
def getMenuList: List[Any] = {
|
||||
@@ -712,11 +761,13 @@ class Shadowsocks
|
||||
buf ++= getProfileList
|
||||
|
||||
buf +=
|
||||
new Item(-400, getString(R.string.add_profile), android.R.drawable.ic_menu_add, newProfile)
|
||||
new DrawableItem(-400, getString(R.string.add_profile), new IconDrawable(this, IconValue.fa_plus_circle)
|
||||
.colorRes(android.R.color.darker_gray).sizeDp(26), newProfile)
|
||||
|
||||
buf += new Category(getString(R.string.settings))
|
||||
|
||||
buf += new Item(-100, getString(R.string.recovery), android.R.drawable.ic_menu_revert, _ => {
|
||||
buf += new DrawableItem(-100, getString(R.string.recovery), new IconDrawable(this, IconValue.fa_recycle)
|
||||
.colorRes(android.R.color.darker_gray).sizeDp(26), _ => {
|
||||
// send event
|
||||
application.tracker.send(new HitBuilders.EventBuilder()
|
||||
.setCategory(Shadowsocks.TAG)
|
||||
@@ -727,7 +778,8 @@ class Shadowsocks
|
||||
})
|
||||
|
||||
buf +=
|
||||
new Item(-200, getString(R.string.flush_dnscache), android.R.drawable.ic_menu_delete, _ => {
|
||||
new DrawableItem(-200, getString(R.string.flush_dnscache), new IconDrawable(this, IconValue.fa_refresh)
|
||||
.colorRes(android.R.color.darker_gray).sizeDp(26), _ => {
|
||||
// send event
|
||||
application.tracker.send(new HitBuilders.EventBuilder()
|
||||
.setCategory(Shadowsocks.TAG)
|
||||
@@ -737,7 +789,20 @@ class Shadowsocks
|
||||
flushDnsCache()
|
||||
})
|
||||
|
||||
buf += new Item(-300, getString(R.string.about), android.R.drawable.ic_menu_info_details, _ => {
|
||||
buf +=
|
||||
new DrawableItem(-300, getString(R.string.qrcode), new IconDrawable(this, IconValue.fa_qrcode)
|
||||
.colorRes(android.R.color.darker_gray).sizeDp(26), _ => {
|
||||
// send event
|
||||
application.tracker.send(new HitBuilders.EventBuilder()
|
||||
.setCategory(Shadowsocks.TAG)
|
||||
.setAction("qrcode")
|
||||
.setLabel(getVersionName)
|
||||
.build())
|
||||
showQrCode()
|
||||
})
|
||||
|
||||
buf += new DrawableItem(-400, getString(R.string.about), new IconDrawable(this, IconValue.fa_info_circle)
|
||||
.colorRes(android.R.color.darker_gray).sizeDp(26), _ => {
|
||||
// send event
|
||||
application.tracker.send(new HitBuilders.EventBuilder()
|
||||
.setCategory(Shadowsocks.TAG)
|
||||
@@ -781,6 +846,11 @@ class Shadowsocks
|
||||
switchButton.setOnCheckedChangeListener(Shadowsocks.this)
|
||||
}
|
||||
ConfigUtils.refresh(this)
|
||||
|
||||
// Check if profile list changed
|
||||
val id = settings.getInt(Key.profileId, -1)
|
||||
if (id != -1 && id != currentProfile.id)
|
||||
reloadProfile()
|
||||
}
|
||||
|
||||
private def setPreferenceEnabled(enabled: Boolean) {
|
||||
@@ -847,10 +917,7 @@ class Shadowsocks
|
||||
Console.runRootCommand(ab.toArray)
|
||||
}
|
||||
|
||||
def reset() {
|
||||
|
||||
crashRecovery()
|
||||
|
||||
def install() {
|
||||
copyAssets(System.getABI)
|
||||
|
||||
val ab = new ArrayBuffer[String]
|
||||
@@ -858,20 +925,51 @@ class Shadowsocks
|
||||
ab.append("chmod 755 " + Path.BASE + executable)
|
||||
}
|
||||
Console.runCommand(ab.toArray)
|
||||
}
|
||||
|
||||
def reset() {
|
||||
|
||||
crashRecovery()
|
||||
|
||||
install()
|
||||
}
|
||||
|
||||
private def recovery() {
|
||||
val h = showProgress(getString(R.string.recovering))
|
||||
serviceStop()
|
||||
val h = showProgress(R.string.recovering)
|
||||
spawn {
|
||||
reset()
|
||||
h.sendEmptyMessage(0)
|
||||
}
|
||||
}
|
||||
|
||||
private def dp2px(dp: Int): Int = {
|
||||
val displayMetrics = getBaseContext.getResources.getDisplayMetrics()
|
||||
Math.round(dp * (displayMetrics.xdpi / DisplayMetrics.DENSITY_DEFAULT))
|
||||
}
|
||||
|
||||
private def showQrCode() {
|
||||
val image = new ImageView(this)
|
||||
image.setPadding(0, dp2px(20), 0, dp2px(20))
|
||||
image.setLayoutParams(new LinearLayout.LayoutParams(-1, -1))
|
||||
val qrcode = QRCode.from(Parser.generate(currentProfile))
|
||||
.withSize(dp2px(250), dp2px(250)).asInstanceOf[QRCode]
|
||||
image.setImageBitmap(qrcode.bitmap())
|
||||
|
||||
new AlertDialog.Builder(this)
|
||||
.setCancelable(true)
|
||||
.setNegativeButton(getString(R.string.close), new DialogInterface.OnClickListener() {
|
||||
override def onClick(dialog: DialogInterface, id: Int) {
|
||||
dialog.cancel()
|
||||
}
|
||||
})
|
||||
.setView(image)
|
||||
.create()
|
||||
.show()
|
||||
}
|
||||
|
||||
private def flushDnsCache() {
|
||||
val h = showProgress(getString(R.string.flushing))
|
||||
val h = showProgress(R.string.flushing)
|
||||
spawn {
|
||||
Utils.toggleAirplaneMode(getBaseContext)
|
||||
h.sendEmptyMessage(0)
|
||||
@@ -889,9 +987,7 @@ class Shadowsocks
|
||||
resultCode match {
|
||||
case Activity.RESULT_OK =>
|
||||
prepared = true
|
||||
if (!serviceStart) {
|
||||
cancelStart()
|
||||
}
|
||||
serviceStart()
|
||||
case _ =>
|
||||
cancelStart()
|
||||
Log.e(Shadowsocks.TAG, "Failed to start VpnService")
|
||||
@@ -925,33 +1021,34 @@ class Shadowsocks
|
||||
try {
|
||||
val port: Int = Integer.valueOf(text)
|
||||
if (!low && port <= 1024) {
|
||||
new SnackBar.Builder(this).withMessageId(R.string.port_alert).withStyle(SnackBar.Style.ALERT).show()
|
||||
new SnackBar.Builder(this)
|
||||
.withMessageId(R.string.port_alert)
|
||||
.withActionMessageId(R.string.error)
|
||||
.withStyle(SnackBar.Style.ALERT)
|
||||
.withDuration(SnackBar.LONG_SNACK)
|
||||
.show()
|
||||
return false
|
||||
}
|
||||
} catch {
|
||||
case ex: Exception =>
|
||||
new SnackBar.Builder(this).withMessageId(R.string.port_alert).withStyle(SnackBar.Style.ALERT).show()
|
||||
new SnackBar.Builder(this)
|
||||
.withMessageId(R.string.port_alert)
|
||||
.withActionMessageId(R.string.error)
|
||||
.withStyle(SnackBar.Style.ALERT)
|
||||
.withDuration(SnackBar.LONG_SNACK)
|
||||
.show()
|
||||
return false
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
/** Called when connect button is clicked. */
|
||||
def serviceStart: Boolean = {
|
||||
|
||||
if (!checkText(Key.proxy)) return false
|
||||
if (!checkText(Key.sitekey)) return false
|
||||
if (!checkNumber(Key.localPort, low = false)) return false
|
||||
if (!checkNumber(Key.remotePort, low = true)) return false
|
||||
|
||||
if (bgService == null) return false
|
||||
|
||||
def serviceStart() {
|
||||
bgService.start(ConfigUtils.load(settings))
|
||||
|
||||
if (isVpnEnabled) {
|
||||
changeSwitch(checked = false)
|
||||
}
|
||||
true
|
||||
}
|
||||
|
||||
private def showAbout() {
|
||||
@@ -990,6 +1087,7 @@ class Shadowsocks
|
||||
if (progressDialog != null) {
|
||||
progressDialog.dismiss()
|
||||
progressDialog = null
|
||||
progressTag = -1
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1003,14 +1101,19 @@ class Shadowsocks
|
||||
if (progressDialog == null) {
|
||||
progressDialog = ProgressDialog
|
||||
.show(Shadowsocks.this, "", getString(R.string.connecting), true, true)
|
||||
progressTag = R.string.connecting
|
||||
}
|
||||
setPreferenceEnabled(enabled = false)
|
||||
case State.CONNECTED =>
|
||||
clearDialog()
|
||||
if (progressTag == R.string.connecting) {
|
||||
clearDialog()
|
||||
}
|
||||
changeSwitch(checked = true)
|
||||
setPreferenceEnabled(enabled = false)
|
||||
case State.STOPPED =>
|
||||
clearDialog()
|
||||
if (progressTag == R.string.stopping) {
|
||||
clearDialog()
|
||||
}
|
||||
changeSwitch(checked = false)
|
||||
if (m != null) {
|
||||
new SnackBar.Builder(Shadowsocks.this)
|
||||
@@ -1025,6 +1128,7 @@ class Shadowsocks
|
||||
if (progressDialog == null) {
|
||||
progressDialog = ProgressDialog
|
||||
.show(Shadowsocks.this, "", getString(R.string.stopping), true, true)
|
||||
progressTag = R.string.stopping
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -61,18 +61,16 @@ import scala.concurrent.ops._
|
||||
|
||||
class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
|
||||
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
|
||||
val TAG = "ShadowsocksVpnService"
|
||||
|
||||
val VPN_MTU = 1500
|
||||
val VPN_MTU = 1400
|
||||
val PRIVATE_VLAN = "26.26.26.%s"
|
||||
|
||||
var conn: ParcelFileDescriptor = null
|
||||
var notificationManager: NotificationManager = null
|
||||
var receiver: BroadcastReceiver = null
|
||||
var apps: Array[ProxiedApp] = null
|
||||
var config: Config = null
|
||||
|
||||
private lazy val application = getApplication.asInstanceOf[ShadowsocksApplication]
|
||||
var vpnThread: ShadowsocksVpnThread = null
|
||||
|
||||
def isByass(net: SubnetUtils): Boolean = {
|
||||
val info = net.getInfo
|
||||
@@ -95,81 +93,71 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
def startShadowsocksDaemon() {
|
||||
|
||||
if (Utils.isLollipopOrAbove && config.route != Route.ALL) {
|
||||
val acl: Array[String] = config.route match {
|
||||
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
|
||||
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route_full)
|
||||
}
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "acl.list"))(p => {
|
||||
acl.foreach(item => p.println(item))
|
||||
})
|
||||
override def onBind(intent: Intent): IBinder = {
|
||||
val action = intent.getAction
|
||||
if (VpnService.SERVICE_INTERFACE == action) {
|
||||
return super.onBind(intent)
|
||||
} else if (Action.SERVICE == action) {
|
||||
return binder
|
||||
}
|
||||
|
||||
val conf = ConfigUtils
|
||||
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
|
||||
config.sitekey, config.encMethod, 10)
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-vpn.conf"))(p => {
|
||||
p.println(conf)
|
||||
})
|
||||
|
||||
val cmd = new ArrayBuffer[String]
|
||||
cmd +=(Path.BASE + "ss-local", "-u"
|
||||
, "-b" , "127.0.0.1"
|
||||
, "-t" , "600"
|
||||
, "-c" , Path.BASE + "ss-local-vpn.conf"
|
||||
, "-f" , Path.BASE + "ss-local-vpn.pid")
|
||||
|
||||
if (Utils.isLollipopOrAbove && config.route != Route.ALL) {
|
||||
cmd += "--acl"
|
||||
cmd += (Path.BASE + "acl.list")
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||
Console.runCommand(cmd.mkString(" "))
|
||||
null
|
||||
}
|
||||
|
||||
def startDnsTunnel() = {
|
||||
val conf = ConfigUtils
|
||||
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163,
|
||||
config.sitekey, config.encMethod, 10)
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-vpn.conf"))(p => {
|
||||
p.println(conf)
|
||||
})
|
||||
val cmd = new ArrayBuffer[String]
|
||||
cmd +=(Path.BASE + "ss-tunnel"
|
||||
, "-u"
|
||||
, "-t" , "10"
|
||||
, "-b" , "127.0.0.1"
|
||||
, "-l" , "8163"
|
||||
, "-L" , "8.8.8.8:53"
|
||||
, "-c" , Path.BASE + "ss-tunnel-vpn.conf"
|
||||
, "-f" , Path.BASE + "ss-tunnel-vpn.pid")
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||
Console.runCommand(cmd.mkString(" "))
|
||||
override def onDestroy() {
|
||||
super.onDestroy()
|
||||
if (vpnThread != null) vpnThread.stopThread()
|
||||
}
|
||||
|
||||
def startDnsDaemon() {
|
||||
val conf = {
|
||||
if (Utils.isLollipopOrAbove && config.route == Route.BYPASS_CHN) {
|
||||
val reject = ConfigUtils.getRejectList(getContext, application)
|
||||
val blackList = ConfigUtils.getBlackList(getContext, application)
|
||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
|
||||
Path.BASE + "pdnsd-vpn.pid", reject, blackList, 8163)
|
||||
} else {
|
||||
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
|
||||
Path.BASE + "pdnsd-vpn.pid", 8163)
|
||||
}
|
||||
}
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-vpn.conf"))(p => {
|
||||
p.println(conf)
|
||||
})
|
||||
val cmd = Path.BASE + "pdnsd -c " + Path.BASE + "pdnsd-vpn.conf"
|
||||
override def onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
|
||||
Console.runCommand(cmd)
|
||||
ConfigUtils.refresh(this)
|
||||
|
||||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
.asInstanceOf[NotificationManager]
|
||||
|
||||
vpnThread = new ShadowsocksVpnThread(this)
|
||||
vpnThread.start()
|
||||
}
|
||||
|
||||
override def onRevoke() {
|
||||
stopRunner()
|
||||
}
|
||||
|
||||
override def stopRunner() {
|
||||
|
||||
// channge the state
|
||||
changeState(State.STOPPING)
|
||||
|
||||
// send event
|
||||
application.tracker.send(new HitBuilders.EventBuilder()
|
||||
.setCategory(TAG)
|
||||
.setAction("stop")
|
||||
.setLabel(getVersionName)
|
||||
.build())
|
||||
|
||||
// reset VPN
|
||||
killProcesses()
|
||||
|
||||
// close connections
|
||||
if (conn != null) {
|
||||
conn.close()
|
||||
conn = null
|
||||
}
|
||||
|
||||
// stop the service if no callback registered
|
||||
if (getCallbackCount == 0) {
|
||||
stopSelf()
|
||||
}
|
||||
|
||||
// clean up the context
|
||||
if (receiver != null) {
|
||||
unregisterReceiver(receiver)
|
||||
receiver = null
|
||||
}
|
||||
|
||||
// channge the state
|
||||
changeState(State.STOPPED)
|
||||
}
|
||||
|
||||
def getVersionName: String = {
|
||||
@@ -184,195 +172,6 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
version
|
||||
}
|
||||
|
||||
def startVpn() {
|
||||
|
||||
val builder = new Builder()
|
||||
builder
|
||||
.setSession(config.profileName)
|
||||
.setMtu(VPN_MTU)
|
||||
.addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
|
||||
.addDnsServer("8.8.8.8")
|
||||
|
||||
if (Utils.isLollipopOrAbove) {
|
||||
|
||||
builder.allowFamily(android.system.OsConstants.AF_INET6)
|
||||
|
||||
if (!config.isGlobalProxy) {
|
||||
val apps = AppManager.getProxiedApps(this, config.proxiedAppString)
|
||||
val pkgSet: mutable.HashSet[String] = new mutable.HashSet[String]
|
||||
for (app <- apps) {
|
||||
if (app.proxied) {
|
||||
pkgSet.add(app.packageName)
|
||||
}
|
||||
}
|
||||
for (pkg <- pkgSet) {
|
||||
if (!config.isBypassApps) {
|
||||
builder.addAllowedApplication(pkg)
|
||||
} else {
|
||||
builder.addDisallowedApplication(pkg)
|
||||
}
|
||||
}
|
||||
|
||||
if (config.isBypassApps) {
|
||||
builder.addDisallowedApplication(this.getPackageName)
|
||||
}
|
||||
} else {
|
||||
builder.addDisallowedApplication(this.getPackageName)
|
||||
}
|
||||
}
|
||||
|
||||
if (InetAddressUtils.isIPv6Address(config.proxy)) {
|
||||
builder.addRoute("0.0.0.0", 0)
|
||||
} else {
|
||||
if (!Utils.isLollipopOrAbove) {
|
||||
config.route match {
|
||||
case Route.BYPASS_LAN =>
|
||||
for (i <- 1 to 223) {
|
||||
if (i != 26 && i != 127) {
|
||||
val addr = i.toString + ".0.0.0"
|
||||
val cidr = addr + "/8"
|
||||
val net = new SubnetUtils(cidr)
|
||||
|
||||
if (!isByass(net) && !isPrivateA(i)) {
|
||||
builder.addRoute(addr, 8)
|
||||
} else {
|
||||
for (j <- 0 to 255) {
|
||||
val subAddr = i.toString + "." + j.toString + ".0.0"
|
||||
val subCidr = subAddr + "/16"
|
||||
val subNet = new SubnetUtils(subCidr)
|
||||
if (!isByass(subNet) && !isPrivateB(i, j)) {
|
||||
builder.addRoute(subAddr, 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
case Route.BYPASS_CHN =>
|
||||
val list = {
|
||||
if (Build.VERSION.SDK_INT == Build.VERSION_CODES.KITKAT) {
|
||||
getResources.getStringArray(R.array.simple_route)
|
||||
} else {
|
||||
getResources.getStringArray(R.array.gfw_route)
|
||||
}
|
||||
}
|
||||
list.foreach(cidr => {
|
||||
val net = new SubnetUtils(cidr)
|
||||
if (!isByass(net)) {
|
||||
val addr = cidr.split('/')
|
||||
builder.addRoute(addr(0), addr(1).toInt)
|
||||
}
|
||||
})
|
||||
case Route.ALL =>
|
||||
for (i <- 1 to 223) {
|
||||
if (i != 26 && i != 127) {
|
||||
val addr = i.toString + ".0.0.0"
|
||||
val cidr = addr + "/8"
|
||||
val net = new SubnetUtils(cidr)
|
||||
|
||||
if (!isByass(net)) {
|
||||
builder.addRoute(addr, 8)
|
||||
} else {
|
||||
for (j <- 0 to 255) {
|
||||
val subAddr = i.toString + "." + j.toString + ".0.0"
|
||||
val subCidr = subAddr + "/16"
|
||||
val subNet = new SubnetUtils(subCidr)
|
||||
if (!isByass(subNet)) {
|
||||
builder.addRoute(subAddr, 16)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (config.route == Route.ALL) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
builder.addRoute("8.8.0.0", 16)
|
||||
|
||||
try {
|
||||
conn = builder.establish()
|
||||
} catch {
|
||||
case ex: IllegalStateException =>
|
||||
changeState(State.STOPPED, ex.getMessage)
|
||||
conn = null
|
||||
case ex: Exception => conn = null
|
||||
}
|
||||
|
||||
if (conn == null) {
|
||||
stopRunner()
|
||||
return
|
||||
}
|
||||
|
||||
val fd = conn.getFd
|
||||
|
||||
var cmd = (Path.BASE +
|
||||
"tun2socks --netif-ipaddr %s "
|
||||
+ "--netif-netmask 255.255.255.0 "
|
||||
+ "--socks-server-addr 127.0.0.1:%d "
|
||||
+ "--tunfd %d "
|
||||
+ "--tunmtu %d "
|
||||
+ "--loglevel 3 "
|
||||
+ "--pid %stun2socks-vpn.pid")
|
||||
.formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"), config.localPort, fd, VPN_MTU, Path.BASE)
|
||||
|
||||
if (config.isUdpDns)
|
||||
cmd += " --enable-udprelay"
|
||||
else
|
||||
cmd += " --dnsgw %s:8153".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"))
|
||||
|
||||
if (Utils.isLollipopOrAbove) {
|
||||
cmd += " --fake-proc"
|
||||
}
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
|
||||
|
||||
System.exec(cmd)
|
||||
}
|
||||
|
||||
/** Called when the activity is first created. */
|
||||
def handleConnection: Boolean = {
|
||||
startShadowsocksDaemon()
|
||||
if (!config.isUdpDns) {
|
||||
startDnsDaemon()
|
||||
startDnsTunnel()
|
||||
}
|
||||
startVpn()
|
||||
true
|
||||
}
|
||||
|
||||
override def onBind(intent: Intent): IBinder = {
|
||||
val action = intent.getAction
|
||||
if (VpnService.SERVICE_INTERFACE == action) {
|
||||
return super.onBind(intent)
|
||||
} else if (Action.SERVICE == action) {
|
||||
return binder
|
||||
}
|
||||
null
|
||||
}
|
||||
|
||||
override def onCreate() {
|
||||
super.onCreate()
|
||||
|
||||
ConfigUtils.refresh(this)
|
||||
|
||||
notificationManager = getSystemService(Context.NOTIFICATION_SERVICE)
|
||||
.asInstanceOf[NotificationManager]
|
||||
}
|
||||
|
||||
override def onRevoke() {
|
||||
stopRunner()
|
||||
}
|
||||
|
||||
def killProcesses() {
|
||||
for (task <- Array("ss-local", "ss-tunnel", "pdnsd", "tun2socks")) {
|
||||
try {
|
||||
@@ -458,40 +257,188 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
}
|
||||
}
|
||||
|
||||
override def stopRunner() {
|
||||
|
||||
// channge the state
|
||||
changeState(State.STOPPING)
|
||||
|
||||
// send event
|
||||
application.tracker.send(new HitBuilders.EventBuilder()
|
||||
.setCategory(TAG)
|
||||
.setAction("stop")
|
||||
.setLabel(getVersionName)
|
||||
.build())
|
||||
|
||||
// reset VPN
|
||||
killProcesses()
|
||||
|
||||
// close connections
|
||||
if (conn != null) {
|
||||
conn.close()
|
||||
conn = null
|
||||
/** Called when the activity is first created. */
|
||||
def handleConnection: Boolean = {
|
||||
startShadowsocksDaemon()
|
||||
if (!config.isUdpDns) {
|
||||
startDnsDaemon()
|
||||
startDnsTunnel()
|
||||
}
|
||||
|
||||
// stop the service if no callback registered
|
||||
if (getCallbackCount == 0) {
|
||||
stopSelf()
|
||||
val fd = startVpn()
|
||||
|
||||
if (fd != -1) {
|
||||
var tries = 1
|
||||
while (tries < 5) {
|
||||
Thread.sleep(1000 * tries)
|
||||
if (System.sendfd(fd) != -1) {
|
||||
return true
|
||||
}
|
||||
tries += 1
|
||||
}
|
||||
}
|
||||
false
|
||||
}
|
||||
|
||||
def startShadowsocksDaemon() {
|
||||
|
||||
if (config.route != Route.ALL) {
|
||||
val acl: Array[String] = config.route match {
|
||||
case Route.BYPASS_LAN => getResources.getStringArray(R.array.private_route)
|
||||
case Route.BYPASS_CHN => getResources.getStringArray(R.array.chn_route_full)
|
||||
}
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "acl.list"))(p => {
|
||||
acl.foreach(item => p.println(item))
|
||||
})
|
||||
}
|
||||
|
||||
// clean up the context
|
||||
if (receiver != null) {
|
||||
unregisterReceiver(receiver)
|
||||
receiver = null
|
||||
val conf = ConfigUtils
|
||||
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, config.localPort,
|
||||
config.sitekey, config.encMethod, 10)
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "ss-local-vpn.conf"))(p => {
|
||||
p.println(conf)
|
||||
})
|
||||
|
||||
val cmd = new ArrayBuffer[String]
|
||||
cmd +=(Path.BASE + "ss-local", "-V", "-u"
|
||||
, "-b", "127.0.0.1"
|
||||
, "-t", "600"
|
||||
, "-c", Path.BASE + "ss-local-vpn.conf"
|
||||
, "-f", Path.BASE + "ss-local-vpn.pid")
|
||||
|
||||
if (config.route != Route.ALL) {
|
||||
cmd += "--acl"
|
||||
cmd += (Path.BASE + "acl.list")
|
||||
}
|
||||
|
||||
// channge the state
|
||||
changeState(State.STOPPED)
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||
Console.runCommand(cmd.mkString(" "))
|
||||
}
|
||||
|
||||
def startDnsTunnel() = {
|
||||
val conf = ConfigUtils
|
||||
.SHADOWSOCKS.formatLocal(Locale.ENGLISH, config.proxy, config.remotePort, 8163,
|
||||
config.sitekey, config.encMethod, 10)
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "ss-tunnel-vpn.conf"))(p => {
|
||||
p.println(conf)
|
||||
})
|
||||
val cmd = new ArrayBuffer[String]
|
||||
cmd +=(Path.BASE + "ss-tunnel"
|
||||
, "-V"
|
||||
, "-u"
|
||||
, "-t", "10"
|
||||
, "-b", "127.0.0.1"
|
||||
, "-l", "8163"
|
||||
, "-L", "8.8.8.8:53"
|
||||
, "-c", Path.BASE + "ss-tunnel-vpn.conf"
|
||||
, "-f", Path.BASE + "ss-tunnel-vpn.pid")
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd.mkString(" "))
|
||||
Console.runCommand(cmd.mkString(" "))
|
||||
}
|
||||
|
||||
def startDnsDaemon() {
|
||||
val conf = {
|
||||
if (config.route == Route.BYPASS_CHN) {
|
||||
val reject = ConfigUtils.getRejectList(getContext, application)
|
||||
val blackList = ConfigUtils.getBlackList(getContext, application)
|
||||
ConfigUtils.PDNSD_DIRECT.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
|
||||
Path.BASE + "pdnsd-vpn.pid", reject, blackList, 8163)
|
||||
} else {
|
||||
ConfigUtils.PDNSD_LOCAL.formatLocal(Locale.ENGLISH, "0.0.0.0", 8153,
|
||||
Path.BASE + "pdnsd-vpn.pid", 8163)
|
||||
}
|
||||
}
|
||||
ConfigUtils.printToFile(new File(Path.BASE + "pdnsd-vpn.conf"))(p => {
|
||||
p.println(conf)
|
||||
})
|
||||
val cmd = Path.BASE + "pdnsd -c " + Path.BASE + "pdnsd-vpn.conf"
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
|
||||
Console.runCommand(cmd)
|
||||
}
|
||||
|
||||
override def getContext = getBaseContext
|
||||
|
||||
def startVpn(): Int = {
|
||||
|
||||
val builder = new Builder()
|
||||
builder
|
||||
.setSession(config.profileName)
|
||||
.setMtu(VPN_MTU)
|
||||
.addAddress(PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"), 24)
|
||||
.addDnsServer("8.8.8.8")
|
||||
|
||||
if (Utils.isLollipopOrAbove) {
|
||||
|
||||
builder.allowFamily(android.system.OsConstants.AF_INET6)
|
||||
|
||||
if (!config.isGlobalProxy) {
|
||||
val apps = AppManager.getProxiedApps(this, config.proxiedAppString)
|
||||
val pkgSet: mutable.HashSet[String] = new mutable.HashSet[String]
|
||||
for (app <- apps) {
|
||||
if (app.proxied) {
|
||||
pkgSet.add(app.packageName)
|
||||
}
|
||||
}
|
||||
for (pkg <- pkgSet) {
|
||||
if (!config.isBypassApps) {
|
||||
builder.addAllowedApplication(pkg)
|
||||
} else {
|
||||
builder.addDisallowedApplication(pkg)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (config.route == Route.ALL) {
|
||||
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)
|
||||
})
|
||||
}
|
||||
|
||||
builder.addRoute("8.8.0.0", 16)
|
||||
|
||||
try {
|
||||
conn = builder.establish()
|
||||
} catch {
|
||||
case ex: IllegalStateException =>
|
||||
changeState(State.STOPPED, ex.getMessage)
|
||||
conn = null
|
||||
case ex: Exception => conn = null
|
||||
}
|
||||
|
||||
if (conn == null) {
|
||||
stopRunner()
|
||||
return -1
|
||||
}
|
||||
|
||||
val fd = conn.getFd
|
||||
|
||||
var cmd = (Path.BASE +
|
||||
"tun2socks --netif-ipaddr %s "
|
||||
+ "--netif-netmask 255.255.255.0 "
|
||||
+ "--socks-server-addr 127.0.0.1:%d "
|
||||
+ "--tunfd %d "
|
||||
+ "--tunmtu %d "
|
||||
+ "--loglevel 3 "
|
||||
+ "--pid %stun2socks-vpn.pid")
|
||||
.formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "2"), config.localPort, fd, VPN_MTU, Path.BASE)
|
||||
|
||||
if (config.isUdpDns)
|
||||
cmd += " --enable-udprelay"
|
||||
else
|
||||
cmd += " --dnsgw %s:8153".formatLocal(Locale.ENGLISH, PRIVATE_VLAN.formatLocal(Locale.ENGLISH, "1"))
|
||||
|
||||
if (BuildConfig.DEBUG) Log.d(TAG, cmd)
|
||||
|
||||
Console.runCommand(cmd)
|
||||
|
||||
return fd
|
||||
}
|
||||
|
||||
override def stopBackgroundService() {
|
||||
@@ -501,6 +448,4 @@ class ShadowsocksVpnService extends VpnService with BaseService {
|
||||
override def getTag = TAG
|
||||
|
||||
override def getServiceMode = Mode.VPN
|
||||
|
||||
override def getContext = getBaseContext
|
||||
}
|
||||
|
||||
@@ -0,0 +1,133 @@
|
||||
/*
|
||||
* Shadowsocks - A shadowsocks client for Android
|
||||
* Copyright (C) 2015 <max.c.lv@gmail.com>
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||||
*
|
||||
*
|
||||
* ___====-_ _-====___
|
||||
* _--^^^#####// \\#####^^^--_
|
||||
* _-^##########// ( ) \\##########^-_
|
||||
* -############// |\^^/| \\############-
|
||||
* _/############// (@::@) \\############\_
|
||||
* /#############(( \\// ))#############\
|
||||
* -###############\\ (oo) //###############-
|
||||
* -#################\\ / VV \ //#################-
|
||||
* -###################\\/ \//###################-
|
||||
* _#/|##########/\######( /\ )######/\##########|\#_
|
||||
* |/ |#/\#/\#/\/ \#/\##\ | | /##/\#/ \/\#/\#/\#| \|
|
||||
* ` |/ V V ` V \#\| | | |/#/ V ' V V \| '
|
||||
* ` ` ` ` / | | | | \ ' ' ' '
|
||||
* ( | | | | )
|
||||
* __\ | | | | /__
|
||||
* (vvv(VVV)(VVV)vvv)
|
||||
*
|
||||
* HERE BE DRAGONS
|
||||
*
|
||||
*/
|
||||
|
||||
package com.github.shadowsocks
|
||||
|
||||
import java.io.{File, FileDescriptor, IOException}
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
import android.net.{LocalServerSocket, LocalSocket, LocalSocketAddress}
|
||||
import android.util.Log
|
||||
|
||||
class ShadowsocksVpnThread(vpnService: ShadowsocksVpnService) extends Thread {
|
||||
|
||||
val TAG = "ShadowsocksVpnService"
|
||||
val PATH = "shadowsocks_protect"
|
||||
|
||||
var isRunning: Boolean = true
|
||||
var serverSocket: LocalServerSocket = null
|
||||
|
||||
def stopThread() {
|
||||
isRunning = false
|
||||
if (serverSocket != null) {
|
||||
serverSocket.close()
|
||||
serverSocket = null
|
||||
}
|
||||
}
|
||||
|
||||
override def run(): Unit = {
|
||||
|
||||
try {
|
||||
if (serverSocket != null) {
|
||||
serverSocket.close()
|
||||
serverSocket = null
|
||||
}
|
||||
serverSocket = new LocalServerSocket(PATH)
|
||||
} catch {
|
||||
case e: IOException =>
|
||||
Log.e(TAG, "unable to bind", e)
|
||||
vpnService.stopBackgroundService()
|
||||
return
|
||||
}
|
||||
|
||||
val pool = Executors.newFixedThreadPool(4)
|
||||
|
||||
while (isRunning) {
|
||||
|
||||
try {
|
||||
val socket = serverSocket.accept()
|
||||
|
||||
pool.execute(new Runnable {
|
||||
override def run() {
|
||||
try {
|
||||
val input = socket.getInputStream
|
||||
val output = socket.getOutputStream
|
||||
|
||||
input.read()
|
||||
|
||||
val fds = socket.getAncillaryFileDescriptors
|
||||
|
||||
if (fds.length > 0) {
|
||||
var ret = false
|
||||
|
||||
val getInt = classOf[FileDescriptor].getDeclaredMethod("getInt$")
|
||||
val fd = getInt.invoke(fds(0)).asInstanceOf[Int]
|
||||
ret = vpnService.protect(fd)
|
||||
|
||||
// Trick to close file decriptor
|
||||
System.jniclose(fd)
|
||||
|
||||
if (ret) {
|
||||
output.write(0)
|
||||
} else {
|
||||
output.write(1)
|
||||
}
|
||||
|
||||
input.close()
|
||||
output.close()
|
||||
}
|
||||
} catch {
|
||||
case e: Exception =>
|
||||
Log.e(TAG, "Error when protect socket", e)
|
||||
}
|
||||
|
||||
// close socket
|
||||
try {
|
||||
socket.close()
|
||||
} catch {
|
||||
case _: Exception => // ignore
|
||||
}
|
||||
|
||||
}})
|
||||
} catch {
|
||||
case e: IOException => Log.e(TAG, "Error when accept socket", e)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -45,11 +45,24 @@ import android.view.View
|
||||
import android.view.ViewGroup
|
||||
import android.widget.BaseAdapter
|
||||
import android.widget.TextView
|
||||
import android.graphics.drawable.Drawable
|
||||
import com.github.shadowsocks.{R}
|
||||
import android.view.View.{OnLongClickListener, OnClickListener}
|
||||
|
||||
case class Item(id: Int, title: String, iconRes: Int,
|
||||
abstract class Item {
|
||||
def id: Int
|
||||
def title: String
|
||||
def click: Int => Unit
|
||||
def longClick: Int => Boolean
|
||||
}
|
||||
|
||||
case class IconItem(id: Int, title: String, iconRes: Int,
|
||||
click: Int => Unit, longClick: Int => Boolean = _ => false)
|
||||
extends Item
|
||||
|
||||
case class DrawableItem(id: Int, title: String, iconRes: Drawable,
|
||||
click: Int => Unit, longClick: Int => Boolean = _ => false)
|
||||
extends Item
|
||||
|
||||
case class Category(title: String)
|
||||
|
||||
@@ -89,7 +102,8 @@ class MenuAdapter(context: Context, var items: List[Any]) extends BaseAdapter {
|
||||
@Override override def getItemViewType(position: Int): Int = {
|
||||
getItem(position) match {
|
||||
case Category => MenuAdapter.CATEGORY
|
||||
case Item => MenuAdapter.ITEM
|
||||
case IconItem => MenuAdapter.ITEM
|
||||
case DrawableItem => MenuAdapter.ITEM
|
||||
case _ => MenuAdapter.UNDEFINED
|
||||
}
|
||||
}
|
||||
@@ -121,9 +135,18 @@ class MenuAdapter(context: Context, var items: List[Any]) extends BaseAdapter {
|
||||
}
|
||||
val tv: TextView = v.asInstanceOf[TextView]
|
||||
tv.setText(value.title)
|
||||
if (value.iconRes != -1) {
|
||||
tv.setCompoundDrawablesWithIntrinsicBounds(value.iconRes, 0, 0, 0)
|
||||
|
||||
value match {
|
||||
case iconItem: IconItem =>
|
||||
if (iconItem.iconRes != -1) {
|
||||
tv.setCompoundDrawablesWithIntrinsicBounds(iconItem.iconRes, 0, 0, 0)
|
||||
}
|
||||
case drawableItem: DrawableItem =>
|
||||
if (drawableItem.iconRes != null) {
|
||||
tv.setCompoundDrawablesWithIntrinsicBounds(drawableItem.iconRes, null, null, null)
|
||||
}
|
||||
}
|
||||
|
||||
tv.setOnClickListener(new OnClickListener {
|
||||
def onClick(view: View) {
|
||||
val item = view.getTag(R.id.mdItem).asInstanceOf[Item]
|
||||
|
||||
@@ -82,6 +82,9 @@ object ConfigUtils {
|
||||
| label = "local";
|
||||
| ip = 127.0.0.1;
|
||||
| port = %d;
|
||||
| reject = ::/0;
|
||||
| reject_policy = negate;
|
||||
| reject_recursively = on;
|
||||
| timeout = 5;
|
||||
|}
|
||||
|
|
||||
@@ -156,8 +159,8 @@ object ConfigUtils {
|
||||
|
|
||||
|server {
|
||||
| label = "china-servers";
|
||||
| ip = 223.5.5.5, 114.114.114.114;
|
||||
| timeout = 2;
|
||||
| ip = 114.114.114.114, 112.124.47.27;
|
||||
| timeout = 4;
|
||||
| reject = %s;
|
||||
| reject_policy = fail;
|
||||
| reject_recursively = on;
|
||||
|
||||
@@ -40,11 +40,19 @@
|
||||
package com.github.shadowsocks.utils
|
||||
|
||||
import com.github.shadowsocks.database.Profile
|
||||
import java.util.Locale
|
||||
import android.net.Uri
|
||||
import android.util.{Log, Base64}
|
||||
|
||||
object Parser {
|
||||
val TAG = "ShadowParser"
|
||||
|
||||
def generate (profile: Profile): String = {
|
||||
val path = "%s:%s@%s:%d".formatLocal(Locale.ENGLISH,
|
||||
profile.method, profile.password, profile.host, profile.remotePort)
|
||||
return "ss://" + Base64.encodeToString(path.getBytes, Base64.NO_PADDING)
|
||||
}
|
||||
|
||||
def parse (data: String): Option[Profile] = {
|
||||
try {
|
||||
Log.d(TAG, data)
|
||||
|
||||
Reference in New Issue
Block a user