Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
C
com.ccwangluo.accelerator
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
sheteng
com.ccwangluo.accelerator
Commits
265ecc04
Unverified
Commit
265ecc04
authored
Nov 13, 2017
by
Max Lv
Committed by
GitHub
Nov 13, 2017
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1446 from shadowsocks/transproxy
Implement Proxy-Only Mode and Transproxy Mode
parents
967f1981
fb2fdd5d
Changes
40
Hide whitespace changes
Inline
Side-by-side
Showing
40 changed files
with
719 additions
and
465 deletions
+719
-465
.github/faq.md
.github/faq.md
+33
-0
.github/issue_template.md
.github/issue_template.md
+2
-4
.gitmodules
.gitmodules
+8
-0
mobile/src/main/AndroidManifest.xml
mobile/src/main/AndroidManifest.xml
+14
-8
mobile/src/main/jni/Android.mk
mobile/src/main/jni/Android.mk
+48
-0
mobile/src/main/jni/libevent
mobile/src/main/jni/libevent
+1
-0
mobile/src/main/jni/redsocks
mobile/src/main/jni/redsocks
+1
-0
mobile/src/main/res/drawable/ic_navigation_refresh.xml
mobile/src/main/res/drawable/ic_navigation_refresh.xml
+0
-9
mobile/src/main/res/values/arrays.xml
mobile/src/main/res/values/arrays.xml
+11
-0
mobile/src/main/res/values/strings.xml
mobile/src/main/res/values/strings.xml
+14
-10
mobile/src/main/res/xml/pref_global.xml
mobile/src/main/res/xml/pref_global.xml
+30
-4
mobile/src/main/res/xml/pref_profile.xml
mobile/src/main/res/xml/pref_profile.xml
+0
-6
mobile/src/main/scala/com/github/shadowsocks/GlobalConfigFragment.scala
...n/scala/com/github/shadowsocks/GlobalConfigFragment.scala
+32
-10
mobile/src/main/scala/com/github/shadowsocks/MainActivity.scala
.../src/main/scala/com/github/shadowsocks/MainActivity.scala
+39
-54
mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala
.../scala/com/github/shadowsocks/ProfileConfigFragment.scala
+1
-1
mobile/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
.../main/scala/com/github/shadowsocks/ProfilesFragment.scala
+5
-4
mobile/src/main/scala/com/github/shadowsocks/QuickToggleShortcut.scala
...in/scala/com/github/shadowsocks/QuickToggleShortcut.scala
+4
-3
mobile/src/main/scala/com/github/shadowsocks/ServiceBoundContext.scala
...in/scala/com/github/shadowsocks/ServiceBoundContext.scala
+4
-5
mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala
...scala/com/github/shadowsocks/ShadowsocksApplication.scala
+29
-23
mobile/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala
...la/com/github/shadowsocks/ShadowsocksRunnerActivity.scala
+4
-8
mobile/src/main/scala/com/github/shadowsocks/acl/CustomRulesFragment.scala
...cala/com/github/shadowsocks/acl/CustomRulesFragment.scala
+3
-3
mobile/src/main/scala/com/github/shadowsocks/bg/BaseService.scala
...rc/main/scala/com/github/shadowsocks/bg/BaseService.scala
+114
-117
mobile/src/main/scala/com/github/shadowsocks/bg/Executable.scala
...src/main/scala/com/github/shadowsocks/bg/Executable.scala
+27
-0
mobile/src/main/scala/com/github/shadowsocks/bg/LocalDnsService.scala
...ain/scala/com/github/shadowsocks/bg/LocalDnsService.scala
+93
-0
mobile/src/main/scala/com/github/shadowsocks/bg/ProxyService.scala
...c/main/scala/com/github/shadowsocks/bg/ProxyService.scala
+12
-0
mobile/src/main/scala/com/github/shadowsocks/bg/ServiceNotification.scala
...scala/com/github/shadowsocks/bg/ServiceNotification.scala
+6
-6
mobile/src/main/scala/com/github/shadowsocks/bg/ServiceState.scala
...c/main/scala/com/github/shadowsocks/bg/ServiceState.scala
+16
-0
mobile/src/main/scala/com/github/shadowsocks/bg/ServiceTileService.scala
.../scala/com/github/shadowsocks/bg/ServiceTileService.scala
+8
-8
mobile/src/main/scala/com/github/shadowsocks/bg/TrafficMonitor.scala
...main/scala/com/github/shadowsocks/bg/TrafficMonitor.scala
+1
-1
mobile/src/main/scala/com/github/shadowsocks/bg/TrafficMonitorThread.scala
...cala/com/github/shadowsocks/bg/TrafficMonitorThread.scala
+1
-1
mobile/src/main/scala/com/github/shadowsocks/bg/TransproxyService.scala
...n/scala/com/github/shadowsocks/bg/TransproxyService.scala
+97
-0
mobile/src/main/scala/com/github/shadowsocks/bg/VpnService.scala
...src/main/scala/com/github/shadowsocks/bg/VpnService.scala
+33
-107
mobile/src/main/scala/com/github/shadowsocks/bg/VpnThread.scala
.../src/main/scala/com/github/shadowsocks/bg/VpnThread.scala
+5
-4
mobile/src/main/scala/com/github/shadowsocks/database/DBHelper.scala
...main/scala/com/github/shadowsocks/database/DBHelper.scala
+3
-6
mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala
.../main/scala/com/github/shadowsocks/database/Profile.scala
+0
-7
mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala
...n/scala/com/github/shadowsocks/plugin/PluginManager.scala
+1
-2
mobile/src/main/scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala
.../scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala
+1
-2
mobile/src/main/scala/com/github/shadowsocks/preference/OrmLitePreferenceDataStore.scala
...b/shadowsocks/preference/OrmLitePreferenceDataStore.scala
+4
-2
mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala
...c/main/scala/com/github/shadowsocks/utils/Constants.scala
+9
-42
mobile/src/main/scala/com/github/shadowsocks/utils/Utils.scala
...e/src/main/scala/com/github/shadowsocks/utils/Utils.scala
+5
-8
No files found.
.github/faq.md
View file @
265ecc04
...
...
@@ -65,3 +65,36 @@ To scan the QR code through the integrated QR scanner.
By the way, upgrade your Android system already.
### How to use Transproxy mode?
1.
Install
[
AFWall+
](
https://github.com/ukanth/afwall
)
;
2.
Set custom script:
```
sh
IP6TABLES
=
/system/bin/ip6tables
IPTABLES
=
/system/bin/iptables
ULIMIT
=
/system/bin/ulimit
SHADOWSOCKS_UID
=
`
dumpsys package com.github.shadowsocks |
grep
userId |
cut
-d
=
-f2
- |
cut
-d
' '
-f1
-
`
PORT_DNS
=
5450
PORT_TRANSPROXY
=
8200
$ULIMIT
-n
4096
$IP6TABLES
-F
$IP6TABLES
-A
INPUT
-j
DROP
$IP6TABLES
-A
OUTPUT
-j
DROP
$IPTABLES
-t
nat
-F
OUTPUT
$IPTABLES
-t
nat
-A
OUTPUT
-o
lo
-j
RETURN
$IPTABLES
-t
nat
-A
OUTPUT
-d
127.0.0.1
-j
RETURN
$IPTABLES
-t
nat
-A
OUTPUT
-m
owner
--uid-owner
$SHADOWSOCKS_UID
-j
RETURN
$IPTABLES
-t
nat
-A
OUTPUT
-p
tcp
--dport
53
-j
DNAT
--to-destination
127.0.0.1:
$PORT_DNS
$IPTABLES
-t
nat
-A
OUTPUT
-p
udp
--dport
53
-j
DNAT
--to-destination
127.0.0.1:
$PORT_DNS
$IPTABLES
-t
nat
-A
OUTPUT
-p
tcp
-j
DNAT
--to-destination
127.0.0.1:
$PORT_TRANSPROXY
$IPTABLES
-t
nat
-A
OUTPUT
-p
udp
-j
DNAT
--to-destination
127.0.0.1:
$PORT_TRANSPROXY
```
3.
Set custom shutdown script:
```
sh
IP6TABLES
=
/system/bin/ip6tables
IPTABLES
=
/system/bin/iptables
$IPTABLES
-t
nat
-F
OUTPUT
$IP6TABLES
-F
```
4.
Make sure to allow traffic for Shadowsocks;
5.
Start Shadowsocks transproxy service and enable firewall.
.github/issue_template.md
View file @
265ecc04
...
...
@@ -15,9 +15,7 @@ _Put an `x` inside the [ ] that applies._
*
[ ] IPv6 server address
*
[ ] Client IPv4 availability
*
[ ] Client IPv6 availability
*
Local port: 1080
*
Encrypt method:
*
[ ] One-time authentication
*
Route
*
[ ] All
*
[ ] Bypass LAN
...
...
@@ -27,14 +25,14 @@ _Put an `x` inside the [ ] that applies._
*
[ ] China List
*
[ ] Custom rules
*
[ ] IPv6 route
*
[ ]
Per-App Proxy
*
[ ]
Apps VPN mode
*
[ ] Bypass mode
*
Remote DNS: 8.8.8.8
*
[ ] DNS Forwarding
*
Plugin configuration (if applicable):
*
[ ] Auto Connect
*
[ ] TCP Fast Open
*
[ ] NAT mode
*
If you're not using VPN mode, please supply more details here:
### What did you do?
...
...
.gitmodules
View file @
265ecc04
...
...
@@ -9,6 +9,14 @@
path = mobile/src/main/jni/libancillary
url = https://github.com/shadowsocks/libancillary.git
branch = shadowsocks-android
[submodule "mobile/src/main/jni/libevent"]
path = mobile/src/main/jni/libevent
url = https://github.com/shadowsocks/libevent.git
branch = shadowsocks-android
[submodule "mobile/src/main/jni/redsocks"]
path = mobile/src/main/jni/redsocks
url = https://github.com/shadowsocks/redsocks.git
branch = shadowsocks-android
[submodule "mobile/src/main/jni/mbedtls"]
path = mobile/src/main/jni/mbedtls
url = https://github.com/ARMmbed/mbedtls
...
...
mobile/src/main/AndroidManifest.xml
View file @
265ecc04
...
...
@@ -109,13 +109,7 @@
</activity>
<service
android:name=
".ShadowsocksLocalService"
android:process=
":bg"
android:exported=
"false"
>
</service>
<service
android:name=
".ShadowsocksVpnService"
android:name=
".bg.VpnService"
android:process=
":bg"
android:label=
"@string/app_name"
android:permission=
"android.permission.BIND_VPN_SERVICE"
...
...
@@ -125,7 +119,19 @@
</intent-filter>
</service>
<service
android:name=
".ShadowsocksTileService"
android:label=
"@string/quick_toggle"
<service
android:name=
".bg.TransproxyService"
android:process=
":bg"
android:exported=
"false"
>
</service>
<service
android:name=
".bg.ProxyService"
android:process=
":bg"
android:exported=
"false"
>
</service>
<service
android:name=
".bg.ServiceTileService"
android:label=
"@string/quick_toggle"
android:process=
":bg"
android:icon=
"@drawable/ic_start_connected"
android:permission=
"android.permission.BIND_QUICK_SETTINGS_TILE"
>
...
...
mobile/src/main/jni/Android.mk
View file @
265ecc04
...
...
@@ -78,6 +78,32 @@ LOCAL_SRC_FILES := $(addprefix libsodium/src/libsodium/,$(SODIUM_SOURCE))
include
$(BUILD_STATIC_LIBRARY)
########################################################
## libevent
########################################################
include
$(CLEAR_VARS)
LIBEVENT_SOURCES
:=
\
buffer.c
\
bufferevent.c bufferevent_filter.c
\
bufferevent_pair.c bufferevent_ratelim.c
\
bufferevent_sock.c epoll.c
\
epoll_sub.c evdns.c event.c
\
event_tagging.c evmap.c
\
evrpc.c evthread.c
\
evthread_pthread.c evutil.c
\
evutil_rand.c http.c
\
listener.c log.c poll.c
\
select
.c signal.c strlcpy.c
LOCAL_MODULE
:=
event
LOCAL_SRC_FILES
:=
$(
addprefix
libevent/,
$(LIBEVENT_SOURCES)
)
LOCAL_CFLAGS
:=
-O2
-D_EVENT_HAVE_ARC4RANDOM
-I
$(LOCAL_PATH)
/libevent
\
-I
$(LOCAL_PATH)
/libevent/include
\
include
$(BUILD_STATIC_LIBRARY)
########################################################
## libancillary
########################################################
...
...
@@ -174,6 +200,28 @@ LOCAL_SRC_FILES := \
include
$(BUILD_STATIC_LIBRARY)
########################################################
## redsocks
########################################################
include
$(CLEAR_VARS)
REDSOCKS_SOURCES
:=
base.c http-connect.c
\
log.c md5.c socks5.c
\
base64.c http-auth.c http-relay.c main.c
\
parser.c redsocks.c socks4.c utils.c
LOCAL_STATIC_LIBRARIES
:=
libevent
LOCAL_MODULE
:=
redsocks
LOCAL_SRC_FILES
:=
$(
addprefix
redsocks/,
$(REDSOCKS_SOURCES)
)
LOCAL_CFLAGS
:=
-O2
-std
=
gnu99
-DUSE_IPTABLES
\
-I
$(LOCAL_PATH)
/redsocks
\
-I
$(LOCAL_PATH)
/libevent/include
\
-I
$(LOCAL_PATH)
/libevent
include
$(BUILD_SHARED_EXECUTABLE)
########################################################
## shadowsocks-libev local
########################################################
...
...
libevent
@
359ca847
Subproject commit 359ca847a649b9c318f9217c0755484d98ecb779
redsocks
@
274334f1
Subproject commit 274334f14839431ae003774d99c3d1de337afff4
mobile/src/main/res/drawable/ic_navigation_refresh.xml
deleted
100644 → 0
View file @
967f1981
<vector
xmlns:android=
"http://schemas.android.com/apk/res/android"
android:width=
"24dp"
android:height=
"24dp"
android:viewportWidth=
"24.0"
android:viewportHeight=
"24.0"
>
<path
android:fillColor=
"#FF000000"
android:pathData=
"M17.65,6.35C16.2,4.9 14.21,4 12,4c-4.42,0 -7.99,3.58 -7.99,8s3.57,8 7.99,8c3.73,0 6.84,-2.55 7.73,-6h-2.08c-0.82,2.33 -3.04,4 -5.65,4 -3.31,0 -6,-2.69 -6,-6s2.69,-6 6,-6c1.66,0 3.14,0.69 4.22,1.78L13,11h7V4l-2.35,2.35z"
/>
</vector>
mobile/src/main/res/values/arrays.xml
View file @
265ecc04
...
...
@@ -226,4 +226,15 @@
<item>
@string/acl_rule_templates_domain
</item>
<item>
URL
</item>
</string-array>
<string-array
name=
"service_modes"
>
<item>
@string/service_mode_proxy
</item>
<item>
@string/service_mode_vpn
</item>
<item>
@string/service_mode_transproxy
</item>
</string-array>
<string-array
name=
"service_mode_values"
>
<item>
proxy
</item>
<item>
vpn
</item>
<item>
transproxy
</item>
</string-array>
</resources>
mobile/src/main/res/values/strings.xml
View file @
265ecc04
...
...
@@ -7,8 +7,15 @@
<!-- misc -->
<string
name=
"profile"
>
Profile
</string>
<string
name=
"profile_summary"
>
Switch to another profile or add new profiles
</string>
<string
name=
"local"
>
SOCKS5 mode
</string>
<string
name=
"local_summary"
>
Enable SOCKS5 proxy mode instead of VPN mode to work with AFWall+ or Orbot
</string>
<string
name=
"advanced"
>
Advanced
</string>
<string
name=
"service_mode"
>
Service mode
</string>
<string
name=
"service_mode_proxy"
>
Proxy only
</string>
<string
name=
"service_mode_vpn"
>
VPN
</string>
<string
name=
"service_mode_transproxy"
>
Transproxy
</string>
<string
name=
"port_proxy"
>
SOCKS5 proxy port
</string>
<string
name=
"port_local_dns"
>
Local DNS port
</string>
<string
name=
"port_transproxy"
>
Transproxy port
</string>
<string
name=
"remote_dns"
>
Remote DNS
</string>
<string
name=
"stat_summary"
>
Sent: \t\t\t\t\t%3$s\t↑\t%1$s/s\nReceived: \t%4$s\t↓\t%2$s/s
</string>
<string
name=
"stat_profiles"
>
%1$s↑\t%2$s↓
</string>
...
...
@@ -42,9 +49,9 @@
<string
name=
"route_entry_bypass_lan_chn"
>
Bypass LAN
&
mainland China
</string>
<string
name=
"route_entry_gfwlist"
>
GFW List
</string>
<string
name=
"route_entry_chinalist"
>
China List
</string>
<string
name=
"proxied_apps"
>
Per-App Proxy
</string>
<string
name=
"proxied_apps_summary"
>
Set proxy for selected apps
</string>
<string
name=
"proxied_apps_summary_v21"
>
Set proxy for selected apps
</string>
<string
name=
"proxied_apps"
>
Apps VPN mode
</string>
<string
name=
"proxied_apps_summary"
>
Allow selected apps to bypass VPN, not available on Android 4.x
</string>
<string
name=
"proxied_apps_summary_v21"
>
Allow selected apps to bypass VPN
</string>
<string
name=
"on"
>
On
</string>
<string
name=
"bypass_apps"
>
Bypass Mode
</string>
<string
name=
"bypass_apps_summary"
>
Enable this option to bypass selected apps
</string>
...
...
@@ -57,11 +64,11 @@
<!-- notification category -->
<string
name=
"service_vpn"
>
VPN Service
</string>
<string
name=
"service_local"
>
SOCKS5 Service
</string>
<string
name=
"service_proxy"
>
Proxy Service
</string>
<string
name=
"service_transproxy"
>
Transproxy Service
</string>
<string
name=
"forward_success"
>
Shadowsocks started.
</string>
<string
name=
"invalid_server"
>
Invalid server name
</string>
<string
name=
"service_failed"
>
Failed to connect the remote server
</string>
<string
name=
"switch_to_vpn"
>
Switch to VPN mode
</string>
<string
name=
"stop"
>
Stop
</string>
<string
name=
"stopping"
>
Shutting down…
</string>
<string
name=
"vpn_error"
>
%s
</string>
...
...
@@ -73,7 +80,6 @@
<string
name=
"profile_empty"
>
Please select a profile
</string>
<string
name=
"proxy_empty"
>
Proxy/Password should not be empty
</string>
<string
name=
"connect"
>
Connect
</string>
<string
name=
"recovering"
>
Resetting…
</string>
<string
name=
"remove_profile"
>
Remove this profile "%s"?
</string>
<!-- menu category -->
...
...
@@ -81,7 +87,6 @@
<string
name=
"settings"
>
Settings
</string>
<string
name=
"faq"
>
FAQ
</string>
<string
name=
"faq_url"
>
https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md
</string>
<string
name=
"recovery"
>
Reset
</string>
<string
name=
"about"
>
About
</string>
<string
name=
"about_title"
>
Shadowsocks %s
</string>
<string
name=
"edit"
>
Edit
</string>
...
...
@@ -122,7 +127,6 @@
<string
name=
"received"
>
Received:
</string>
<string
name=
"connecting"
>
Connecting…
</string>
<string
name=
"vpn_connected"
>
Connected, tap to check connection
</string>
<string
name=
"local_connected"
>
Connected
</string>
<string
name=
"not_connected"
>
Not connected
</string>
<!-- acl -->
...
...
mobile/src/main/res/xml/pref_global.xml
View file @
265ecc04
<?xml version="1.0" encoding="utf-8"?>
<PreferenceScreen
xmlns:android=
"http://schemas.android.com/apk/res/android"
>
<PreferenceScreen
xmlns:android=
"http://schemas.android.com/apk/res/android"
xmlns:app=
"http://schemas.android.com/apk/res/com.github.shadowsocks"
>
<SwitchPreference
android:key=
"isAutoConnect"
android:persistent=
"false"
android:summary=
"@string/auto_connect_summary"
...
...
@@ -7,7 +8,32 @@
<SwitchPreference
android:key=
"tcp_fastopen"
android:summary=
"@string/tcp_fastopen_summary"
android:title=
"TCP Fast Open"
/>
<SwitchPreference
android:key=
"isNAT"
android:title=
"@string/local"
android:summary=
"@string/local_summary"
/>
<be.mygod.preference.PreferenceCategory
android:title=
"@string/advanced"
>
<DropDownPreference
android:key=
"serviceMode"
android:entries=
"@array/service_modes"
android:entryValues=
"@array/service_mode_values"
android:defaultValue=
"vpn"
android:summary=
"%s"
android:title=
"@string/service_mode"
/>
<be.mygod.preference.NumberPickerPreference
app:min=
"1025"
app:max=
"65535"
android:key=
"portProxy"
android:summary=
"%d"
android:title=
"@string/port_proxy"
/>
<be.mygod.preference.NumberPickerPreference
app:min=
"1025"
app:max=
"65535"
android:key=
"portLocalDns"
android:summary=
"%d"
android:title=
"@string/port_local_dns"
/>
<be.mygod.preference.NumberPickerPreference
app:min=
"1025"
app:max=
"65535"
android:key=
"portTransproxy"
android:summary=
"%d"
android:title=
"@string/port_transproxy"
/>
</be.mygod.preference.PreferenceCategory>
</PreferenceScreen>
mobile/src/main/res/xml/pref_profile.xml
View file @
265ecc04
...
...
@@ -20,12 +20,6 @@
android:key=
"remotePortNum"
android:summary=
"%d"
android:title=
"@string/remote_port"
/>
<be.mygod.preference.NumberPickerPreference
app:min=
"1025"
app:max=
"65535"
android:key=
"localPortNum"
android:summary=
"%d"
android:title=
"@string/port"
/>
<be.mygod.preference.EditTextPreference
android:inputType=
"textPassword"
android:key=
"sitekey"
...
...
mobile/src/main/scala/com/github/shadowsocks/GlobalConfigFragment.scala
View file @
265ecc04
...
...
@@ -23,13 +23,13 @@ package com.github.shadowsocks
import
android.os.Bundle
import
android.support.design.widget.Snackbar
import
android.support.v14.preference.SwitchPreference
import
android.support.v7.preference.Preference
DataStore
import
android.support.v7.preference.Preference
import
be.mygod.preference.PreferenceFragment
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.
preference.OnPreferenceDataStoreChangeListener
import
com.github.shadowsocks.
bg.ServiceState
import
com.github.shadowsocks.utils.
{
Key
,
TcpFastOpen
}
class
GlobalConfigFragment
extends
PreferenceFragment
with
OnPreferenceDataStoreChangeListener
{
class
GlobalConfigFragment
extends
PreferenceFragment
{
override
def
onCreatePreferences
(
bundle
:
Bundle
,
key
:
String
)
{
getPreferenceManager
.
setPreferenceDataStore
(
app
.
dataStore
)
addPreferencesFromResource
(
R
.
xml
.
pref_global
)
...
...
@@ -53,16 +53,38 @@ class GlobalConfigFragment extends PreferenceFragment with OnPreferenceDataStore
tfo
.
setEnabled
(
false
)
tfo
.
setSummary
(
getString
(
R
.
string
.
tcp_fastopen_summary_unsupported
,
java
.
lang
.
System
.
getProperty
(
"os.version"
)))
}
app
.
dataStore
.
registerChangeListener
(
this
)
val
serviceMode
=
findPreference
(
Key
.
serviceMode
)
val
portProxy
=
findPreference
(
Key
.
portProxy
)
val
portLocalDns
=
findPreference
(
Key
.
portLocalDns
)
val
portTransproxy
=
findPreference
(
Key
.
portTransproxy
)
def
onServiceModeChange
(
p
:
Preference
,
v
:
Any
)
=
{
val
(
enabledLocalDns
,
enabledTransproxy
)
=
v
match
{
case
Key
.
modeProxy
=>
(
false
,
false
)
case
Key
.
modeVpn
=>
(
true
,
false
)
case
Key
.
modeTransproxy
=>
(
true
,
true
)
}
portLocalDns
.
setEnabled
(
enabledLocalDns
)
portTransproxy
.
setEnabled
(
enabledTransproxy
)
true
}
MainActivity
.
stateListener
=
{
case
ServiceState
.
IDLE
|
ServiceState
.
STOPPED
=>
serviceMode
.
setEnabled
(
true
)
portProxy
.
setEnabled
(
true
)
onServiceModeChange
(
null
,
app
.
dataStore
.
serviceMode
)
case
_
=>
serviceMode
.
setEnabled
(
false
)
portProxy
.
setEnabled
(
false
)
portLocalDns
.
setEnabled
(
false
)
portTransproxy
.
setEnabled
(
false
)
}
MainActivity
.
stateListener
(
getActivity
.
asInstanceOf
[
MainActivity
].
state
)
serviceMode
.
setOnPreferenceChangeListener
(
onServiceModeChange
)
}
override
def
onDestroy
()
{
app
.
dataStore
.
unregisterChangeListener
(
this
)
MainActivity
.
stateListener
=
null
super
.
onDestroy
()
}
def
onPreferenceDataStoreChanged
(
store
:
PreferenceDataStore
,
key
:
String
)
:
Unit
=
key
match
{
case
Key
.
isNAT
=>
findPreference
(
key
).
asInstanceOf
[
SwitchPreference
].
setChecked
(
store
.
getBoolean
(
key
,
false
))
case
_
=>
}
}
mobile/src/main/scala/com/github/shadowsocks/MainActivity.scala
View file @
265ecc04
...
...
@@ -21,15 +21,15 @@
package
com.github.shadowsocks
import
java.lang.System.currentTimeMillis
import
java.net.
{
HttpURLConnection
,
URL
}
import
java.net.
{
HttpURLConnection
,
InetSocketAddress
,
URL
,
Proxy
=>
JavaProxy
}
import
java.util.Locale
import
android.app.Activity
import
android.app.backup.BackupManager
import
android.app.
{
Activity
,
ProgressDialog
}
import
android.content._
import
android.net.
{
Uri
,
VpnService
}
import
android.nfc.
{
NdefMessage
,
NfcAdapter
}
import
android.os.
{
Bu
ild
,
Bundle
,
Handler
,
Message
}
import
android.os.
{
Bu
ndle
,
Handler
}
import
android.support.customtabs.CustomTabsIntent
import
android.support.design.widget.
{
FloatingActionButton
,
Snackbar
}
import
android.support.v4.content.ContextCompat
...
...
@@ -45,6 +45,7 @@ import com.github.jorgecastilloprz.FABProgressCircle
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.acl.
{
Acl
,
CustomRulesFragment
}
import
com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import
com.github.shadowsocks.bg.
{
Executable
,
ServiceState
,
TrafficMonitor
}
import
com.github.shadowsocks.preference.OnPreferenceDataStoreChangeListener
import
com.github.shadowsocks.utils.CloseUtils.autoDisconnect
import
com.github.shadowsocks.utils._
...
...
@@ -61,10 +62,11 @@ object MainActivity {
private
final
val
DRAWER_PROFILES
=
0L
private
final
val
DRAWER_GLOBAL_SETTINGS
=
1L
private
final
val
DRAWER_RECOVERY
=
2L
private
final
val
DRAWER_ABOUT
=
3L
private
final
val
DRAWER_FAQ
=
4L
private
final
val
DRAWER_CUSTOM_RULES
=
5L
var
stateListener
:
Int
=>
Unit
=
_
}
class
MainActivity
extends
Activity
with
ServiceBoundContext
with
Drawer
.
OnDrawerItemClickListener
...
...
@@ -109,41 +111,41 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
}
private
def
changeState
(
s
:
Int
,
profileName
:
String
=
null
,
m
:
String
=
null
)
{
s
match
{
case
State
.
CONNECTING
=>
case
S
erviceS
tate
.
CONNECTING
=>
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
fabProgressCircle
.
show
()
statusText
.
setText
(
R
.
string
.
connecting
)
case
State
.
CONNECTED
=>
if
(
state
==
State
.
CONNECTING
)
fabProgressCircle
.
beginFinalAnimation
()
case
S
erviceS
tate
.
CONNECTED
=>
if
(
state
==
S
erviceS
tate
.
CONNECTING
)
fabProgressCircle
.
beginFinalAnimation
()
else
fabProgressCircle
.
postDelayed
(
hideCircle
,
1000
)
fab
.
setImageResource
(
R
.
drawable
.
ic_start_connected
)
statusText
.
setText
(
if
(
app
.
isLocalEnabled
)
R
.
string
.
local_connected
else
R
.
string
.
vpn_connected
)
case
State
.
STOPPING
=>
statusText
.
setText
(
R
.
string
.
vpn_connected
)
case
S
erviceS
tate
.
STOPPING
=>
fab
.
setImageResource
(
R
.
drawable
.
ic_start_busy
)
if
(
state
==
State
.
CONNECTED
)
fabProgressCircle
.
show
()
// ignore for stopped
if
(
state
==
S
erviceS
tate
.
CONNECTED
)
fabProgressCircle
.
show
()
// ignore for stopped
statusText
.
setText
(
R
.
string
.
stopping
)
case
_
=>
fab
.
setImageResource
(
R
.
drawable
.
ic_start_idle
)
fabProgressCircle
.
postDelayed
(
hideCircle
,
1000
)
if
(
m
!=
null
)
{
val
snackbar
=
Snackbar
.
make
(
findViewById
(
R
.
id
.
snackbar
),
getString
(
R
.
string
.
vpn_error
).
formatLocal
(
Locale
.
ENGLISH
,
m
),
Snackbar
.
LENGTH_LONG
)
snackbar
.
show
()
Snackbar
.
make
(
findViewById
(
R
.
id
.
snackbar
),
getString
(
R
.
string
.
vpn_error
).
formatLocal
(
Locale
.
ENGLISH
,
m
),
Snackbar
.
LENGTH_LONG
).
show
()
Log
.
e
(
TAG
,
"Error to start VPN service: "
+
m
)
}
statusText
.
setText
(
R
.
string
.
not_connected
)
}
state
=
s
if
(
state
==
State
.
CONNECTED
)
fab
.
setBackgroundTintList
(
greenTint
)
else
{
if
(
state
==
S
erviceS
tate
.
CONNECTED
)
fab
.
setBackgroundTintList
(
greenTint
)
else
{
fab
.
setBackgroundTintList
(
greyTint
)
updateTraffic
(-
1
,
0
,
0
,
0
,
0
)
testCount
+=
1
// suppress previous test messages
}
if
(
ProfilesFragment
.
instance
!=
null
)
ProfilesFragment
.
instance
.
profilesAdapter
.
notifyDataSetChanged
()
// refresh button enabled state
if
(
stateListener
!=
null
)
stateListener
(
s
)
fab
.
setEnabled
(
false
)
if
(
state
==
S
tate
.
CONNECTED
||
state
==
State
.
STOPPED
)
handler
.
postDelayed
(()
=>
fab
.
setEnabled
(
state
==
S
tate
.
CONNECTED
||
state
==
State
.
STOPPED
),
1000
)
if
(
state
==
S
erviceState
.
CONNECTED
||
state
==
Service
State
.
STOPPED
)
handler
.
postDelayed
(()
=>
fab
.
setEnabled
(
state
==
S
erviceState
.
CONNECTED
||
state
==
Service
State
.
STOPPED
),
1000
)
}
def
updateTraffic
(
profileId
:
Int
,
txRate
:
Long
,
rxRate
:
Long
,
txTotal
:
Long
,
rxTotal
:
Long
)
{
txText
.
setText
(
TrafficMonitor
.
formatTraffic
(
txTotal
))
...
...
@@ -151,18 +153,15 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
txRateText
.
setText
(
TrafficMonitor
.
formatTraffic
(
txRate
)
+
"/s"
)
rxRateText
.
setText
(
TrafficMonitor
.
formatTraffic
(
rxRate
)
+
"/s"
)
val
child
=
getFragmentManager
.
findFragmentById
(
R
.
id
.
fragment_holder
).
asInstanceOf
[
ToolbarFragment
]
if
(
state
!=
State
.
STOPPING
&&
child
!=
null
)
child
.
onTrafficUpdated
(
profileId
,
txRate
,
rxRate
,
txTotal
,
rxTotal
)
}
override
def
onServiceConnected
()
{
changeState
(
bgService
.
getState
)
if
(
state
!=
ServiceState
.
STOPPING
&&
child
!=
null
)
child
.
onTrafficUpdated
(
profileId
,
txRate
,
rxRate
,
txTotal
,
rxTotal
)
}
override
def
onServiceDisconnected
()
:
Unit
=
changeState
(
State
.
IDLE
)
override
def
onServiceConnected
()
:
Unit
=
changeState
(
bgService
.
getState
)
override
def
onServiceDisconnected
()
:
Unit
=
changeState
(
ServiceState
.
IDLE
)
override
def
binderDied
()
:
Unit
=
handler
.
post
(()
=>
{
detachService
()
app
.
crashRecovery
()
Executable
.
killAll
()
attachService
(
callback
)
})
...
...
@@ -202,12 +201,6 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
.
withIcon
(
AppCompatResources
.
getDrawable
(
this
,
R
.
drawable
.
ic_action_help_outline
))
.
withIconTintingEnabled
(
true
)
.
withSelectable
(
false
),
new
PrimaryDrawerItem
()
.
withIdentifier
(
DRAWER_RECOVERY
)
.
withName
(
R
.
string
.
recovery
)
.
withIcon
(
AppCompatResources
.
getDrawable
(
this
,
R
.
drawable
.
ic_navigation_refresh
))
.
withIconTintingEnabled
(
true
)
.
withSelectable
(
false
),
new
PrimaryDrawerItem
()
.
withIdentifier
(
DRAWER_ABOUT
)
.
withName
(
R
.
string
.
about
)
...
...
@@ -250,17 +243,21 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
txRateText
=
findViewById
(
R
.
id
.
txRate
).
asInstanceOf
[
TextView
]
rxText
=
findViewById
(
R
.
id
.
rx
).
asInstanceOf
[
TextView
]
rxRateText
=
findViewById
(
R
.
id
.
rxRate
).
asInstanceOf
[
TextView
]
findViewById
[
View
](
R
.
id
.
stat
).
setOnClickListener
(
_
=>
if
(
state
==
S
tate
.
CONNECTED
&&
app
.
isVpnEnabled
)
{
findViewById
[
View
](
R
.
id
.
stat
).
setOnClickListener
(
_
=>
if
(
state
==
S
erviceState
.
CONNECTED
)
{
testCount
+=
1
statusText
.
setText
(
R
.
string
.
connection_test_testing
)
val
id
=
testCount
// it would change by other code
Utils
.
ThrowableFuture
{
// Based on: https://android.googlesource.com/platform/frameworks/base/+/master/services/core/java/com/android/server/connectivity/NetworkMonitor.java#640
autoDisconnect
(
new
URL
(
"https"
,
app
.
currentProfile
.
get
.
route
match
{
case
Acl
.
CHINALIST
=>
"www.qualcomm.cn"
case
_
=>
"www.google.com"
},
"/generate_204"
).
openConnection
()
.
asInstanceOf
[
HttpURLConnection
])
{
conn
=>
autoDisconnect
{
val
url
=
new
URL
(
"https"
,
app
.
currentProfile
.
get
.
route
match
{
case
Acl
.
CHINALIST
=>
"www.qualcomm.cn"
case
_
=>
"www.google.com"
},
"/generate_204"
)
(
if
(
app
.
usingVpnMode
)
url
.
openConnection
()
else
url
.
openConnection
(
new
JavaProxy
(
JavaProxy
.
Type
.
SOCKS
,
new
InetSocketAddress
(
"127.0.0.1"
,
app
.
dataStore
.
portProxy
))))
.
asInstanceOf
[
HttpURLConnection
]
}
{
conn
=>
conn
.
setConnectTimeout
(
5
*
1000
)
conn
.
setReadTimeout
(
5
*
1000
)
conn
.
setInstanceFollowRedirects
(
false
)
...
...
@@ -292,20 +289,20 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
fab
=
findViewById
(
R
.
id
.
fab
).
asInstanceOf
[
FloatingActionButton
]
fabProgressCircle
=
findViewById
(
R
.
id
.
fabProgressCircle
).
asInstanceOf
[
FABProgressCircle
]
fab
.
setOnClickListener
(
_
=>
if
(
state
==
State
.
CONNECTED
)
Utils
.
stopSsService
(
this
)
else
Utils
.
ThrowableFuture
{
if
(
app
.
isLocalEnabled
)
Utils
.
startSsService
(
this
)
else
{
fab
.
setOnClickListener
(
_
=>
if
(
state
==
S
erviceS
tate
.
CONNECTED
)
Utils
.
stopSsService
(
this
)
else
Utils
.
ThrowableFuture
{
if
(
app
.
usingVpnMode
)
{
val
intent
=
VpnService
.
prepare
(
this
)
if
(
intent
!=
null
)
startActivityForResult
(
intent
,
REQUEST_CONNECT
)
else
handler
.
post
(()
=>
onActivityResult
(
REQUEST_CONNECT
,
Activity
.
RESULT_OK
,
null
))
}
}
else
Utils
.
startSsService
(
this
)
})
fab
.
setOnLongClickListener
(
_
=>
{
Utils
.
positionToast
(
Toast
.
makeText
(
this
,
if
(
state
==
State
.
CONNECTED
)
R
.
string
.
stop
else
R
.
string
.
connect
,
Utils
.
positionToast
(
Toast
.
makeText
(
this
,
if
(
state
==
S
erviceS
tate
.
CONNECTED
)
R
.
string
.
stop
else
R
.
string
.
connect
,
Toast
.
LENGTH_SHORT
),
fab
,
getWindow
,
0
,
getResources
.
getDimensionPixelOffset
(
R
.
dimen
.
margin_small
)).
show
()
true
})
changeState
(
State
.
IDLE
)
// reset everything to init state
changeState
(
S
erviceS
tate
.
IDLE
)
// reset everything to init state
handler
.
post
(()
=>
attachService
(
callback
))
app
.
dataStore
.
registerChangeListener
(
this
)
...
...
@@ -345,7 +342,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
}
def
onPreferenceDataStoreChanged
(
store
:
PreferenceDataStore
,
key
:
String
)
:
Unit
=
key
match
{
case
Key
.
isNAT
=>
handler
.
post
(()
=>
{
case
Key
.
serviceMode
=>
handler
.
post
(()
=>
{
detachService
()
attachService
(
callback
)
})
...
...
@@ -361,18 +358,6 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
override
def
onItemClick
(
view
:
View
,
position
:
Int
,
drawerItem
:
IDrawerItem
[
_
,
_
<:
ViewHolder
])
:
Boolean
=
{
if
(
position
==
previousPosition
)
drawer
.
closeDrawer
()
else
drawerItem
.
getIdentifier
match
{
case
DRAWER_PROFILES
=>
displayFragment
(
new
ProfilesFragment
)
case
DRAWER_RECOVERY
=>
app
.
track
(
"GlobalConfigFragment"
,
"reset"
)
Utils
.
stopSsService
(
this
)
val
dialog
=
ProgressDialog
.
show
(
this
,
""
,
getString
(
R
.
string
.
recovering
),
true
,
false
)
val
handler
=
new
Handler
{
override
def
handleMessage
(
msg
:
Message
)
:
Unit
=
if
(
dialog
.
isShowing
&&
!
isDestroyed
)
dialog
.
dismiss
()
}
Utils
.
ThrowableFuture
{
app
.
crashRecovery
()
app
.
copyAssets
()
handler
.
sendEmptyMessage
(
0
)
}
case
DRAWER_GLOBAL_SETTINGS
=>
displayFragment
(
new
GlobalSettingsFragment
)
case
DRAWER_ABOUT
=>
app
.
track
(
TAG
,
"about"
)
...
...
@@ -389,7 +374,7 @@ class MainActivity extends Activity with ServiceBoundContext with Drawer.OnDrawe
super
.
onResume
()
app
.
remoteConfig
.
fetch
()
state
match
{
case
S
tate
.
STOPPING
|
State
.
CONNECTING
=>
case
S
erviceState
.
STOPPING
|
Service
State
.
CONNECTING
=>
case
_
=>
hideCircle
()
}
}
...
...
mobile/src/main/scala/com/github/shadowsocks/ProfileConfigFragment.scala
View file @
265ecc04
...
...
@@ -66,7 +66,7 @@ class ProfileConfigFragment extends PreferenceFragment with OnMenuItemClickListe
findPreference
(
Key
.
password
).
setSummary
(
"\u2022"
*
32
)
}
isProxyApps
=
findPreference
(
Key
.
proxyApps
).
asInstanceOf
[
SwitchPreference
]
isProxyApps
.
setEnabled
(
Utils
.
isLollipopOrAbove
||
app
.
isLocalEnabled
)
isProxyApps
.
setEnabled
(
Utils
.
isLollipopOrAbove
&&
app
.
usingVpnMode
)
isProxyApps
.
setOnPreferenceClickListener
(
_
=>
{
startActivity
(
new
Intent
(
getActivity
,
classOf
[
AppManager
]))
isProxyApps
.
setChecked
(
true
)
...
...
mobile/src/main/scala/com/github/shadowsocks/ProfilesFragment.scala
View file @
265ecc04
...
...
@@ -31,6 +31,7 @@ import android.view.View.OnLongClickListener
import
android.view._
import
android.widget.
{
LinearLayout
,
PopupMenu
,
TextView
,
Toast
}
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.bg.
{
ServiceState
,
TrafficMonitor
}
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.plugin.PluginConfiguration
import
com.github.shadowsocks.utils._
...
...
@@ -56,12 +57,12 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
* Is ProfilesFragment editable at all.
*/
private
def
isEnabled
=
getActivity
.
asInstanceOf
[
MainActivity
].
state
match
{
case
S
tate
.
CONNECTED
|
State
.
STOPPED
=>
true
case
S
erviceState
.
CONNECTED
|
Service
State
.
STOPPED
=>
true
case
_
=>
false
}
private
def
isProfileEditable
(
id
:
=>
Int
)
=
getActivity
.
asInstanceOf
[
MainActivity
].
state
match
{
case
State
.
CONNECTED
=>
id
!=
app
.
dataStore
.
profileId
case
State
.
STOPPED
=>
true
case
S
erviceS
tate
.
CONNECTED
=>
id
!=
app
.
dataStore
.
profileId
case
S
erviceS
tate
.
STOPPED
=>
true
case
_
=>
false
}
...
...
@@ -154,7 +155,7 @@ final class ProfilesFragment extends ToolbarFragment with Toolbar.OnMenuItemClic
app
.
switchProfile
(
item
.
id
)
profilesAdapter
.
refreshId
(
old
)
itemView
.
setSelected
(
true
)
if
(
activity
.
state
==
State
.
CONNECTED
)
Utils
.
reloadSsService
(
activity
)
if
(
activity
.
state
==
S
erviceS
tate
.
CONNECTED
)
Utils
.
reloadSsService
(
activity
)
}
override
def
onMenuItemClick
(
menu
:
MenuItem
)
:
Boolean
=
menu
.
getItemId
match
{
...
...
mobile/src/main/scala/com/github/shadowsocks/QuickToggleShortcut.scala
View file @
265ecc04
...
...
@@ -26,7 +26,8 @@ import android.content.pm.ShortcutManager
import
android.os.
{
Build
,
Bundle
}
import
android.support.v4.content.pm.
{
ShortcutInfoCompat
,
ShortcutManagerCompat
}
import
android.support.v4.graphics.drawable.IconCompat
import
com.github.shadowsocks.utils.
{
State
,
Utils
}
import
com.github.shadowsocks.bg.ServiceState
import
com.github.shadowsocks.utils.Utils
/**
* @author Mygod
...
...
@@ -56,8 +57,8 @@ class QuickToggleShortcut extends Activity with ServiceBoundContext {
override
def
onServiceConnected
()
{
bgService
.
getState
match
{
case
State
.
STOPPED
=>
Utils
.
startSsService
(
this
)
case
State
.
CONNECTED
=>
Utils
.
stopSsService
(
this
)
case
S
erviceS
tate
.
STOPPED
=>
Utils
.
startSsService
(
this
)
case
S
erviceS
tate
.
CONNECTED
=>
Utils
.
stopSsService
(
this
)
case
_
=>
// ignore
}
finish
()
...
...
mobile/src/main/scala/com/github/shadowsocks/ServiceBoundContext.scala
View file @
265ecc04
...
...
@@ -21,10 +21,11 @@
package
com.github.shadowsocks
import
android.content.
{
ComponentName
,
Context
,
Intent
,
ServiceConnection
}
import
android.os.
{
RemoteException
,
IBinder
}
import
com.github.shadowsocks.aidl.
{
IShadowsocksService
Callback
,
IShadowsocksService
}
import
android.os.
{
IBinder
,
RemoteException
}
import
com.github.shadowsocks.aidl.
{
IShadowsocksService
,
IShadowsocksServiceCallback
}
import
com.github.shadowsocks.utils.Action
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.bg.
{
TransproxyService
,
VpnService
}
/**
* @author Mygod
...
...
@@ -85,9 +86,7 @@ trait ServiceBoundContext extends Context with IBinder.DeathRecipient {
protected
def
attachService
(
callback
:
IShadowsocksServiceCallback.Stub
=
null
)
{
this
.
callback
=
callback
if
(
bgService
==
null
)
{
val
s
=
if
(
app
.
isLocalEnabled
)
classOf
[
ShadowsocksLocalService
]
else
classOf
[
ShadowsocksVpnService
]
val
intent
=
new
Intent
(
this
,
s
)
val
intent
=
new
Intent
(
this
,
app
.
serviceClass
)
intent
.
setAction
(
Action
.
SERVICE
)
connection
=
new
ShadowsocksServiceConnection
()
...
...
mobile/src/main/scala/com/github/shadowsocks/ShadowsocksApplication.scala
View file @
265ecc04
...
...
@@ -26,12 +26,14 @@ import java.util.Locale
import
android.annotation.SuppressLint
import
android.app.
{
Application
,
NotificationChannel
,
NotificationManager
}
import
android.content._
import
android.content.pm.
{
PackageInfo
,
PackageManager
}
import
android.content.res.Configuration
import
android.os.
{
Build
,
LocaleList
}
import
android.os.
{
B
inder
,
B
uild
,
LocaleList
}
import
android.support.v7.app.AppCompatDelegate
import
android.util.Log
import
com.evernote.android.job.JobManager
import
com.github.shadowsocks.acl.DonaldTrump
import
com.github.shadowsocks.bg.
{
BaseService
,
ProxyService
,
TransproxyService
,
VpnService
}
import
com.github.shadowsocks.database.
{
DBHelper
,
Profile
,
ProfileManager
}
import
com.github.shadowsocks.preference.OrmLitePreferenceDataStore
import
com.github.shadowsocks.utils.CloseUtils._
...
...
@@ -40,10 +42,8 @@ import com.google.android.gms.analytics.{GoogleAnalytics, HitBuilders, StandardE
import
com.google.firebase.FirebaseApp
import
com.google.firebase.remoteconfig.FirebaseRemoteConfig
import
com.j256.ormlite.logger.LocalLog
import
eu.chainfire.libsuperuser.Shell
import
scala.collection.JavaConversions._
import
scala.collection.mutable.ArrayBuffer
object
ShadowsocksApplication
{
var
app
:
ShadowsocksApplication
=
_
...
...
@@ -66,8 +66,12 @@ class ShadowsocksApplication extends Application {
lazy
val
profileManager
=
new
ProfileManager
(
dbHelper
)
lazy
val
dataStore
=
new
OrmLitePreferenceDataStore
(
dbHelper
)
def
isLocalEnabled
:
Boolean
=
dataStore
.
isNAT
def
isVpnEnabled
:
Boolean
=
!
isLocalEnabled
def
usingVpnMode
:
Boolean
=
dataStore
.
serviceMode
==
Key
.
modeVpn
def
serviceClass
:
Class
[
_
]
=
app
.
dataStore
.
serviceMode
match
{
case
Key
.
modeProxy
=>
classOf
[
ProxyService
]
case
Key
.
modeVpn
=>
classOf
[
VpnService
]
case
Key
.
modeTransproxy
=>
classOf
[
TransproxyService
]
}
// send event
def
track
(
category
:
String
,
action
:
String
)
:
Unit
=
tracker
.
send
(
new
HitBuilders
.
EventBuilder
()
...
...
@@ -154,26 +158,30 @@ class ShadowsocksApplication extends Application {
TcpFastOpen
.
enabled
(
dataStore
.
getBoolean
(
Key
.
tfo
,
TcpFastOpen
.
sendEnabled
))
if
(
Build
.
VERSION
.
SDK_INT
>=
26
)
getSystemService
(
classOf
[
NotificationManager
]).
createNotificationChannels
(
List
(
new
NotificationChannel
(
"service-vpn"
,
getText
(
R
.
string
.
service_vpn
),
NotificationManager
.
IMPORTANCE_MIN
),
new
NotificationChannel
(
"service-local"
,
getText
(
R
.
string
.
service_local
),
NotificationManager
.
IMPORTANCE_LOW
)
))
}
def
crashRecovery
()
{
val
cmd
=
new
ArrayBuffer
[
String
]()
for
(
task
<-
Executable
.
EXECUTABLES
)
{
cmd
.
append
(
"killall lib%s.so"
.
formatLocal
(
Locale
.
ENGLISH
,
task
))
cmd
.
append
(
"rm -f %1$s/%2$s-local.conf %1$s/%2$s-vpn.conf"
.
formatLocal
(
Locale
.
ENGLISH
,
getFilesDir
.
getAbsolutePath
,
task
))
if
(
dataStore
.
getLong
(
Key
.
assetUpdateTime
,
-
1
)
!=
info
.
lastUpdateTime
)
copyAssets
()
// hopefully hashCode = mHandle doesn't change, currently this is true from KitKat to Nougat
lazy
val
userIndex
=
Binder
.
getCallingUserHandle
.
hashCode
if
(!(
1025
to
65535
contains
dataStore
.
portProxy
))
dataStore
.
putInt
(
Key
.
portProxy
,
1080
+
userIndex
)
if
(!(
1025
to
65535
contains
dataStore
.
portLocalDns
))
dataStore
.
putInt
(
Key
.
portLocalDns
,
5450
+
userIndex
)
if
(!(
1025
to
65535
contains
dataStore
.
portTransproxy
))
dataStore
.
putInt
(
Key
.
portTransproxy
,
8200
+
userIndex
)
if
(
Build
.
VERSION
.
SDK_INT
>=
26
)
{
val
nm
=
getSystemService
(
classOf
[
NotificationManager
])
nm
.
createNotificationChannels
(
List
(
new
NotificationChannel
(
"service-vpn"
,
getText
(
R
.
string
.
service_vpn
),
NotificationManager
.
IMPORTANCE_MIN
),
new
NotificationChannel
(
"service-proxy"
,
getText
(
R
.
string
.
service_proxy
),
NotificationManager
.
IMPORTANCE_LOW
),
new
NotificationChannel
(
"service-transproxy"
,
getText
(
R
.
string
.
service_transproxy
),
NotificationManager
.
IMPORTANCE_LOW
)
))
nm
.
deleteNotificationChannel
(
"service-nat"
)
// NAT mode is gone for good
}
Shell
.
SH
.
run
(
cmd
.
toArray
)
}
lazy
val
info
:
PackageInfo
=
getPackageManager
.
getPackageInfo
(
getPackageName
,
PackageManager
.
GET_SIGNATURES
)
def
copyAssets
()
{
val
assetManager
=
getAssets
for
(
dir
<-
List
(
"acl"
,
"overture"
))
{
for
(
dir
<-
Array
(
"acl"
,
"overture"
))
{
var
files
:
Array
[
String
]
=
null
try
files
=
assetManager
.
list
(
dir
)
catch
{
case
e
:
IOException
=>
...
...
@@ -184,11 +192,9 @@ class ShadowsocksApplication extends Application {
autoClose
(
new
FileOutputStream
(
new
File
(
getFilesDir
,
file
)))(
out
=>
IOUtils
.
copy
(
in
,
out
)))
}
dataStore
.
put
Int
(
Key
.
currentVersionCode
,
BuildConfig
.
VERSION_CODE
)
dataStore
.
put
Long
(
Key
.
assetUpdateTime
,
info
.
lastUpdateTime
)
}
def
updateAssets
()
:
Unit
=
if
(
dataStore
.
getInt
(
Key
.
currentVersionCode
,
-
1
)
!=
BuildConfig
.
VERSION_CODE
)
copyAssets
()
def
listenForPackageChanges
(
callback
:
=>
Unit
)
:
BroadcastReceiver
=
{
val
filter
=
new
IntentFilter
(
Intent
.
ACTION_PACKAGE_ADDED
)
filter
.
addAction
(
Intent
.
ACTION_PACKAGE_REMOVED
)
...
...
mobile/src/main/scala/com/github/shadowsocks/ShadowsocksRunnerActivity.scala
View file @
265ecc04
...
...
@@ -46,16 +46,12 @@ class ShadowsocksRunnerActivity extends Activity with ServiceBoundContext {
}
def
startBackgroundService
()
{
if
(
app
.
isLocalEnabled
)
{
if
(
app
.
usingVpnMode
)
VpnService
.
prepare
(
ShadowsocksRunnerActivity
.
this
)
match
{
case
null
=>
onActivityResult
(
REQUEST_CONNECT
,
Activity
.
RESULT_OK
,
null
)
case
intent
=>
startActivityForResult
(
intent
,
REQUEST_CONNECT
)
}
else
{
Utils
.
startSsService
(
this
)
finish
()
}
else
{
val
intent
=
VpnService
.
prepare
(
ShadowsocksRunnerActivity
.
this
)
if
(
intent
!=
null
)
{
startActivityForResult
(
intent
,
REQUEST_CONNECT
)
}
else
{
onActivityResult
(
REQUEST_CONNECT
,
Activity
.
RESULT_OK
,
null
)
}
}
}
...
...
mobile/src/main/scala/com/github/shadowsocks/acl/CustomRulesFragment.scala
View file @
265ecc04
...
...
@@ -15,7 +15,7 @@ import android.view._
import
android.widget.
{
EditText
,
Spinner
,
TextView
,
Toast
}
import
com.futuremind.recyclerviewfastscroll.
{
FastScroller
,
SectionTitleProvider
}
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.
utils.
State
import
com.github.shadowsocks.
bg.Service
State
import
com.github.shadowsocks.widget.UndoSnackbarManager
import
com.github.shadowsocks.
{
MainActivity
,
R
,
ToolbarFragment
}
...
...
@@ -41,8 +41,8 @@ class CustomRulesFragment extends ToolbarFragment with Toolbar.OnMenuItemClickLi
import
CustomRulesFragment._
private
def
isEnabled
=
getActivity
.
asInstanceOf
[
MainActivity
].
state
match
{
case
State
.
CONNECTED
=>
app
.
currentProfile
.
get
.
route
!=
Acl
.
CUSTOM_RULES
case
State
.
STOPPED
=>
true
case
S
erviceS
tate
.
CONNECTED
=>
app
.
currentProfile
.
get
.
route
!=
Acl
.
CUSTOM_RULES
case
S
erviceS
tate
.
STOPPED
=>
true
case
_
=>
false
}
...
...
mobile/src/main/scala/com/github/shadowsocks/BaseService.scala
→
mobile/src/main/scala/com/github/shadowsocks/
bg/
BaseService.scala
View file @
265ecc04
...
...
@@ -18,10 +18,10 @@
/* */
/*******************************************************************************/
package
com.github.shadowsocks
package
com.github.shadowsocks
.bg
import
java.io.
{
File
,
IOException
}
import
java.net.
{
Inet6Address
,
InetAddress
}
import
java.net.
InetAddress
import
java.util
import
java.util.concurrent.TimeUnit
import
java.util.
{
Timer
,
TimerTask
}
...
...
@@ -33,25 +33,27 @@ import android.text.TextUtils
import
android.util.Log
import
android.widget.Toast
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.acl.
Acl
import
com.github.shadowsocks.acl.
{
Acl
,
AclSyncJob
}
import
com.github.shadowsocks.aidl.
{
IShadowsocksService
,
IShadowsocksServiceCallback
}
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.plugin.
{
PluginConfiguration
,
PluginManager
,
PluginOptions
}
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks.
{
GuardedProcess
,
R
}
import
okhttp3.
{
Dns
,
FormBody
,
OkHttpClient
,
Request
}
import
org.json.
{
JSONArray
,
JSONObject
}
import
org.json.
JSONObject
import
scala.collection.JavaConversions._
import
scala.collection.mutable
import
scala.collection.mutable.ArrayBuffer
import
scala.util.Random
trait
BaseService
extends
Service
{
@volatile
private
var
state
=
State
.
STOPPED
protected
val
TAG
:
String
@volatile
private
var
state
=
ServiceState
.
STOPPED
@volatile
protected
var
profile
:
Profile
=
_
@volatile
private
var
plugin
:
PluginOptions
=
_
@volatile
protected
var
pluginPath
:
String
=
_
var
sslocalProcess
:
GuardedProcess
=
_
case
class
NameNotResolvedException
()
extends
IOException
case
class
NullConnectionException
()
extends
NullPointerException
...
...
@@ -64,7 +66,7 @@ trait BaseService extends Service {
lazy
val
handler
=
new
Handler
(
getMainLooper
)
lazy
val
restartHanlder
=
new
Handler
(
getMainLooper
)
private
var
notification
:
S
hadowsocks
Notification
=
_
private
var
notification
:
S
ervice
Notification
=
_
private
val
closeReceiver
:
BroadcastReceiver
=
(
context
:
Context
,
intent
:
Intent
)
=>
intent
.
getAction
match
{
case
Action
.
RELOAD
=>
forceLoad
()
case
_
=>
...
...
@@ -87,11 +89,11 @@ trait BaseService extends Service {
if
(
timer
==
null
)
{
timer
=
new
Timer
(
true
)
timer
.
schedule
(
new
TimerTask
{
def
run
()
:
Unit
=
if
(
state
==
State
.
CONNECTED
&&
TrafficMonitor
.
updateRate
())
updateTrafficRate
()
def
run
()
:
Unit
=
if
(
state
==
S
erviceS
tate
.
CONNECTED
&&
TrafficMonitor
.
updateRate
())
updateTrafficRate
()
},
1000
,
1000
)
}
TrafficMonitor
.
updateRate
()
if
(
state
==
State
.
CONNECTED
)
cb
.
trafficUpdated
(
profile
.
id
,
if
(
state
==
S
erviceS
tate
.
CONNECTED
)
cb
.
trafficUpdated
(
profile
.
id
,
TrafficMonitor
.
txRate
,
TrafficMonitor
.
rxRate
,
TrafficMonitor
.
txTotal
,
TrafficMonitor
.
rxTotal
)
}
...
...
@@ -107,6 +109,11 @@ trait BaseService extends Service {
}
}
def
onBind
(
intent
:
Intent
)
:
IBinder
=
intent
.
getAction
match
{
case
Action
.
SERVICE
=>
binder
case
_
=>
null
}
def
checkProfile
(
profile
:
Profile
)
:
Boolean
=
if
(
TextUtils
.
isEmpty
(
profile
.
host
)
||
TextUtils
.
isEmpty
(
profile
.
password
))
{
stopRunner
(
stopService
=
true
,
getString
(
R
.
string
.
proxy_empty
))
false
...
...
@@ -115,52 +122,61 @@ trait BaseService extends Service {
def
forceLoad
()
:
Unit
=
app
.
currentProfile
.
orNull
match
{
case
null
=>
stopRunner
(
stopService
=
true
,
getString
(
R
.
string
.
profile_empty
))
case
p
=>
if
(
checkProfile
(
p
))
state
match
{
case
State
.
STOPPED
=>
startRunner
()
case
State
.
CONNECTED
=>
case
S
erviceS
tate
.
STOPPED
=>
startRunner
()
case
S
erviceS
tate
.
CONNECTED
=>
stopRunner
(
stopService
=
false
)
startRunner
()
case
s
=>
Log
.
w
(
BaseService
.
this
.
getClass
.
getSimpleName
,
"Illegal state when invoking use: "
+
s
)
}
}
def
connect
()
{
if
(
profile
.
host
==
"198.199.101.152"
)
{
val
client
=
new
OkHttpClient
.
Builder
()
.
dns
(
hostname
=>
Utils
.
resolve
(
hostname
,
enableIPv6
=
false
)
match
{
case
Some
(
ip
)
=>
util
.
Arrays
.
asList
(
InetAddress
.
getByName
(
ip
))
case
_
=>
Dns
.
SYSTEM
.
lookup
(
hostname
)
})
.
connectTimeout
(
10
,
TimeUnit
.
SECONDS
)
.
writeTimeout
(
10
,
TimeUnit
.
SECONDS
)
.
readTimeout
(
30
,
TimeUnit
.
SECONDS
)
.
build
()
val
requestBody
=
new
FormBody
.
Builder
()
.
add
(
"sig"
,
Utils
.
getSignature
(
this
))
.
build
()
val
request
=
new
Request
.
Builder
()
.
url
(
app
.
remoteConfig
.
getString
(
"proxy_url"
))
.
post
(
requestBody
)
.
build
()
val
proxies
=
Random
.
shuffle
(
client
.
newCall
(
request
).
execute
().
body
.
string
.
split
(
'|'
).
toSeq
)
val
proxy
=
proxies
.
head
.
split
(
':'
)
profile
.
host
=
proxy
(
0
).
trim
profile
.
remotePort
=
proxy
(
1
).
trim
.
toInt
profile
.
password
=
proxy
(
2
).
trim
profile
.
method
=
proxy
(
3
).
trim
protected
def
buildAdditionalArguments
(
cmd
:
ArrayBuffer
[
String
])
:
ArrayBuffer
[
String
]
=
cmd
/**
* BaseService will only start ss-local. Child class override this class to start other native processes.
*/
def
startNativeProcesses
()
{
buildShadowsocksConfig
()
val
cmd
=
buildAdditionalArguments
(
ArrayBuffer
[
String
](
new
File
(
getApplicationInfo
.
nativeLibraryDir
,
Executable
.
SS_LOCAL
).
getAbsolutePath
,
"-u"
,
"-b"
,
"127.0.0.1"
,
"-l"
,
app
.
dataStore
.
portProxy
.
toString
,
"-t"
,
"600"
,
"-c"
,
"shadowsocks.json"
))
if
(
profile
.
route
!=
Acl
.
ALL
)
{
cmd
+=
"--acl"
cmd
+=
Acl
.
getFile
(
profile
.
route
match
{
case
Acl
.
CUSTOM_RULES
=>
Acl
.
CUSTOM_RULES_FLATTENED
case
route
=>
route
}).
getAbsolutePath
}
if
(
profile
.
route
==
Acl
.
CUSTOM_RULES
)
Acl
.
save
(
Acl
.
CUSTOM_RULES_FLATTENED
,
Acl
.
customRules
.
flatten
(
10
))
if
(
TcpFastOpen
.
sendEnabled
)
cmd
+=
"--fast-open"
plugin
=
new
PluginConfiguration
(
profile
.
plugin
).
selectedOptions
pluginPath
=
PluginManager
.
init
(
plugin
)
sslocalProcess
=
new
GuardedProcess
(
cmd
:
_
*
).
start
()
}
def
createNotification
()
:
S
hadowsocks
Notification
def
createNotification
()
:
S
ervice
Notification
def
startRunner
()
:
Unit
=
if
(
Build
.
VERSION
.
SDK_INT
>=
26
)
startForegroundService
(
new
Intent
(
this
,
getClass
))
else
startService
(
new
Intent
(
this
,
getClass
))
def
killProcesses
()
{
if
(
sslocalProcess
!=
null
)
{
sslocalProcess
.
destroy
()
sslocalProcess
=
null
}
}
def
stopRunner
(
stopService
:
Boolean
,
msg
:
String
=
null
)
{
// channge the state
changeState
(
ServiceState
.
STOPPING
)
app
.
track
(
TAG
,
"stop"
)
killProcesses
()
// clean up recevier
if
(
closeReceiverRegistered
)
{
unregisterReceiver
(
closeReceiver
)
...
...
@@ -179,7 +195,7 @@ trait BaseService extends Service {
}
// change the state
changeState
(
State
.
STOPPED
,
msg
)
changeState
(
S
erviceS
tate
.
STOPPED
,
msg
)
// stop the service if nothing has bound to it
if
(
stopService
)
stopSelf
()
...
...
@@ -240,16 +256,10 @@ trait BaseService extends Service {
})
}
override
def
onCreate
()
{
super
.
onCreate
()
app
.
updateAssets
()
}
// Service of shadowsocks should always be started explicitly
override
def
onStartCommand
(
intent
:
Intent
,
flags
:
Int
,
startId
:
Int
)
:
Int
=
{
state
match
{
case
S
tate
.
STOPPED
|
State
.
IDLE
=>
case
S
erviceState
.
STOPPED
|
Service
State
.
IDLE
=>
case
_
=>
return
Service
.
START_NOT_STICKY
// ignore request
}
...
...
@@ -278,9 +288,55 @@ trait BaseService extends Service {
notification
=
createNotification
()
app
.
track
(
getClass
.
getSimpleName
,
"start"
)
changeState
(
State
.
CONNECTING
)
changeState
(
ServiceState
.
CONNECTING
)
Utils
.
ThrowableFuture
(
try
{
if
(
profile
.
host
==
"198.199.101.152"
)
{
val
client
=
new
OkHttpClient
.
Builder
()
.
dns
(
hostname
=>
Utils
.
resolve
(
hostname
,
enableIPv6
=
false
)
match
{
case
Some
(
ip
)
=>
util
.
Arrays
.
asList
(
InetAddress
.
getByName
(
ip
))
case
_
=>
Dns
.
SYSTEM
.
lookup
(
hostname
)
})
.
connectTimeout
(
10
,
TimeUnit
.
SECONDS
)
.
writeTimeout
(
10
,
TimeUnit
.
SECONDS
)
.
readTimeout
(
30
,
TimeUnit
.
SECONDS
)
.
build
()
val
requestBody
=
new
FormBody
.
Builder
()
.
add
(
"sig"
,
Utils
.
getSignature
(
this
))
.
build
()
val
request
=
new
Request
.
Builder
()
.
url
(
app
.
remoteConfig
.
getString
(
"proxy_url"
))
.
post
(
requestBody
)
.
build
()
val
proxies
=
Random
.
shuffle
(
client
.
newCall
(
request
).
execute
().
body
.
string
.
split
(
'|'
).
toSeq
)
val
proxy
=
proxies
.
head
.
split
(
':'
)
profile
.
host
=
proxy
(
0
).
trim
profile
.
remotePort
=
proxy
(
1
).
trim
.
toInt
profile
.
password
=
proxy
(
2
).
trim
profile
.
method
=
proxy
(
3
).
trim
}
if
(
profile
.
route
==
Acl
.
CUSTOM_RULES
)
Acl
.
save
(
Acl
.
CUSTOM_RULES_FLATTENED
,
Acl
.
customRules
.
flatten
(
10
))
plugin
=
new
PluginConfiguration
(
profile
.
plugin
).
selectedOptions
pluginPath
=
PluginManager
.
init
(
plugin
)
Utils
.
ThrowableFuture
(
try
connect
()
catch
{
// Clean up
killProcesses
()
if
(!
Utils
.
isNumeric
(
profile
.
host
))
Utils
.
resolve
(
profile
.
host
,
enableIPv6
=
true
)
match
{
case
Some
(
a
)
=>
profile
.
host
=
a
case
None
=>
throw
NameNotResolvedException
()
}
startNativeProcesses
()
if
(
profile
.
route
!=
Acl
.
ALL
&&
profile
.
route
!=
Acl
.
CUSTOM_RULES
)
AclSyncJob
.
schedule
(
profile
.
route
)
changeState
(
ServiceState
.
CONNECTED
)
}
catch
{
case
_:
NameNotResolvedException
=>
stopRunner
(
stopService
=
true
,
getString
(
R
.
string
.
invalid_server
))
case
_:
NullConnectionException
=>
stopRunner
(
stopService
=
true
,
getString
(
R
.
string
.
reboot_required
))
case
exc
:
Throwable
=>
...
...
@@ -309,78 +365,19 @@ trait BaseService extends Service {
}
}
protected
def
buildPluginCommandLine
()
:
ArrayBuffer
[
String
]
=
{
val
result
=
ArrayBuffer
(
pluginPath
)
if
(
TcpFastOpen
.
sendEnabled
)
result
+=
"--fast-open"
result
}
protected
final
def
buildShadowsocksConfig
(
file
:
String
)
:
String
=
{
protected
final
def
buildShadowsocksConfig
()
{
val
config
=
new
JSONObject
()
.
put
(
"server"
,
profile
.
host
)
.
put
(
"server_port"
,
profile
.
remotePort
)
.
put
(
"password"
,
profile
.
password
)
.
put
(
"method"
,
profile
.
method
)
if
(
pluginPath
!=
null
)
config
.
put
(
"plugin"
,
Commandline
.
toString
(
buildPluginCommandLine
()))
.
put
(
"plugin_opts"
,
plugin
.
toString
)
IOUtils
.
writeString
(
new
File
(
getFilesDir
,
file
),
config
.
toString
)
file
}
protected
final
def
buildOvertureConfig
(
file
:
String
)
:
String
=
{
val
config
=
new
JSONObject
()
.
put
(
"BindAddress"
,
"127.0.0.1:"
+
(
profile
.
localPort
+
53
))
.
put
(
"RedirectIPv6Record"
,
true
)
.
put
(
"DomainBase64Decode"
,
true
)
.
put
(
"HostsFile"
,
"hosts"
)
.
put
(
"MinimumTTL"
,
3600
)
.
put
(
"CacheSize"
,
4096
)
def
makeDns
(
name
:
String
,
address
:
String
,
edns
:
Boolean
=
true
)
=
{
val
dns
=
new
JSONObject
()
.
put
(
"Name"
,
name
)
.
put
(
"Address"
,
(
Utils
.
parseNumericAddress
(
address
)
match
{
case
_:
Inet6Address
=>
'['
+
address
+
']'
case
_
=>
address
})
+
":53"
)
.
put
(
"Timeout"
,
6
)
.
put
(
"EDNSClientSubnet"
,
new
JSONObject
().
put
(
"Policy"
,
"disable"
))
if
(
edns
)
dns
.
put
(
"Protocol"
,
"tcp"
)
.
put
(
"Socks5Address"
,
"127.0.0.1:"
+
profile
.
localPort
)
else
dns
.
put
(
"Protocol"
,
"udp"
)
dns
}
val
remoteDns
=
new
JSONArray
(
profile
.
remoteDns
.
split
(
","
).
zipWithIndex
.
map
{
case
(
dns
,
i
)
=>
makeDns
(
"UserDef-"
+
i
,
dns
.
trim
)
})
val
localDns
=
new
JSONArray
(
Array
(
makeDns
(
"Primary-1"
,
"119.29.29.29"
,
edns
=
false
),
makeDns
(
"Primary-2"
,
"114.114.114.114"
,
edns
=
false
)
))
try
{
val
localLinkDns
=
com
.
github
.
shadowsocks
.
utils
.
Dns
.
getDnsResolver
(
this
)
localDns
.
put
(
makeDns
(
"Primary-3"
,
localLinkDns
,
edns
=
false
))
}
catch
{
case
_:
Exception
=>
// Ignore
}
profile
.
route
match
{
case
Acl
.
BYPASS_CHN
|
Acl
.
BYPASS_LAN_CHN
|
Acl
.
GFWLIST
|
Acl
.
CUSTOM_RULES
=>
config
.
put
(
"PrimaryDNS"
,
localDns
)
.
put
(
"AlternativeDNS"
,
remoteDns
)
.
put
(
"IPNetworkFile"
,
"china_ip_list.txt"
)
.
put
(
"DomainFile"
,
"gfwlist.txt"
)
case
Acl
.
CHINALIST
=>
config
.
put
(
"PrimaryDNS"
,
localDns
)
.
put
(
"AlternativeDNS"
,
remoteDns
)
case
_
=>
config
.
put
(
"PrimaryDNS"
,
remoteDns
)
// no need to setup AlternativeDNS in Acl.ALL/BYPASS_LAN mode
.
put
(
"OnlyPrimaryDNS"
,
true
)
if
(
pluginPath
!=
null
)
{
val
pluginCmd
=
ArrayBuffer
(
pluginPath
)
if
(
TcpFastOpen
.
sendEnabled
)
pluginCmd
+=
"--fast-open"
config
.
put
(
"plugin"
,
Commandline
.
toString
(
buildAdditionalArguments
(
pluginCmd
).
toArray
))
.
put
(
"plugin_opts"
,
plugin
.
toString
)
}
IOUtils
.
writeString
(
new
File
(
getFilesDir
,
file
),
config
.
toString
)
file
IOUtils
.
writeString
(
new
File
(
getFilesDir
,
"shadowsocks.json"
),
config
.
toString
)
}
}
mobile/src/main/scala/com/github/shadowsocks/bg/Executable.scala
0 → 100644
View file @
265ecc04
package
com.github.shadowsocks.bg
import
java.util.Locale
import
java.util.concurrent.TimeUnit
import
android.util.Log
/**
* @author Mygod
*/
object
Executable
{
val
REDSOCKS
=
"libredsocks.so"
val
PDNSD
=
"libpdnsd.so"
val
SS_LOCAL
=
"libss-local.so"
val
SS_TUNNEL
=
"libss-tunnel.so"
val
TUN2SOCKS
=
"libtun2socks.so"
val
OVERTURE
=
"liboverture.so"
val
EXECUTABLES
=
Array
(
SS_LOCAL
,
SS_TUNNEL
,
PDNSD
,
REDSOCKS
,
TUN2SOCKS
,
OVERTURE
)
def
killAll
()
{
val
killer
=
new
ProcessBuilder
(
"killall"
+:
EXECUTABLES
:
_
*
).
start
()
if
(!
killer
.
waitFor
(
1
,
TimeUnit
.
SECONDS
))
Log
.
w
(
"killall"
,
"%s didn't exit within 1s. Post-crash clean-up may have failed."
.
formatLocal
(
Locale
.
ENGLISH
,
killer
.
toString
))
}
}
mobile/src/main/scala/com/github/shadowsocks/bg/LocalDnsService.scala
0 → 100644
View file @
265ecc04
package
com.github.shadowsocks.bg
import
java.io.File
import
java.net.Inet6Address
import
com.github.shadowsocks.GuardedProcess
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.acl.Acl
import
com.github.shadowsocks.utils.
{
IOUtils
,
Utils
}
import
org.json.
{
JSONArray
,
JSONObject
}
import
scala.collection.mutable.ArrayBuffer
/**
* Shadowsocks service with local DNS.
*
* @author Mygod
*/
trait
LocalDnsService
extends
BaseService
{
var
overtureProcess
:
GuardedProcess
=
_
override
def
startNativeProcesses
()
{
super
.
startNativeProcesses
()
if
(!
profile
.
udpdns
)
overtureProcess
=
new
GuardedProcess
(
buildAdditionalArguments
(
ArrayBuffer
[
String
](
new
File
(
getApplicationInfo
.
nativeLibraryDir
,
Executable
.
OVERTURE
).
getAbsolutePath
,
"-c"
,
buildOvertureConfig
(
"overture.conf"
)
))
:
_
*
).
start
()
}
private
def
buildOvertureConfig
(
file
:
String
)
:
String
=
{
val
config
=
new
JSONObject
()
.
put
(
"BindAddress"
,
"127.0.0.1:"
+
app
.
dataStore
.
portLocalDns
)
.
put
(
"RedirectIPv6Record"
,
true
)
.
put
(
"DomainBase64Decode"
,
true
)
.
put
(
"HostsFile"
,
"hosts"
)
.
put
(
"MinimumTTL"
,
3600
)
.
put
(
"CacheSize"
,
4096
)
def
makeDns
(
name
:
String
,
address
:
String
,
edns
:
Boolean
=
true
)
=
{
val
dns
=
new
JSONObject
()
.
put
(
"Name"
,
name
)
.
put
(
"Address"
,
(
Utils
.
parseNumericAddress
(
address
)
match
{
case
_:
Inet6Address
=>
'['
+
address
+
']'
case
_
=>
address
})
+
":53"
)
.
put
(
"Timeout"
,
6
)
.
put
(
"EDNSClientSubnet"
,
new
JSONObject
().
put
(
"Policy"
,
"disable"
))
if
(
edns
)
dns
.
put
(
"Protocol"
,
"tcp"
)
.
put
(
"Socks5Address"
,
"127.0.0.1:"
+
app
.
dataStore
.
portProxy
)
else
dns
.
put
(
"Protocol"
,
"udp"
)
dns
}
val
remoteDns
=
new
JSONArray
(
profile
.
remoteDns
.
split
(
","
).
zipWithIndex
.
map
{
case
(
dns
,
i
)
=>
makeDns
(
"UserDef-"
+
i
,
dns
.
trim
)
})
val
localDns
=
new
JSONArray
(
Array
(
makeDns
(
"Primary-1"
,
"119.29.29.29"
,
edns
=
false
),
makeDns
(
"Primary-2"
,
"114.114.114.114"
,
edns
=
false
)
))
try
{
val
localLinkDns
=
com
.
github
.
shadowsocks
.
utils
.
Dns
.
getDnsResolver
(
this
)
localDns
.
put
(
makeDns
(
"Primary-3"
,
localLinkDns
,
edns
=
false
))
}
catch
{
case
_:
Exception
=>
// Ignore
}
profile
.
route
match
{
case
Acl
.
BYPASS_CHN
|
Acl
.
BYPASS_LAN_CHN
|
Acl
.
GFWLIST
|
Acl
.
CUSTOM_RULES
=>
config
.
put
(
"PrimaryDNS"
,
localDns
)
.
put
(
"AlternativeDNS"
,
remoteDns
)
.
put
(
"IPNetworkFile"
,
"china_ip_list.txt"
)
.
put
(
"DomainFile"
,
"gfwlist.txt"
)
case
Acl
.
CHINALIST
=>
config
.
put
(
"PrimaryDNS"
,
localDns
)
.
put
(
"AlternativeDNS"
,
remoteDns
)
case
_
=>
config
.
put
(
"PrimaryDNS"
,
remoteDns
)
// no need to setup AlternativeDNS in Acl.ALL/BYPASS_LAN mode
.
put
(
"OnlyPrimaryDNS"
,
true
)
}
IOUtils
.
writeString
(
new
File
(
getFilesDir
,
file
),
config
.
toString
)
file
}
override
def
killProcesses
()
{
super
.
killProcesses
()
if
(
overtureProcess
!=
null
)
{
overtureProcess
.
destroy
()
overtureProcess
=
null
}
}
}
mobile/src/main/scala/com/github/shadowsocks/bg/ProxyService.scala
0 → 100644
View file @
265ecc04
package
com.github.shadowsocks.bg
/**
* Shadowsocks service at its minimum.
*
* @author Mygod
*/
class
ProxyService
extends
BaseService
{
val
TAG
=
"ShadowsocksProxyService"
def
createNotification
()
=
new
ServiceNotification
(
this
,
profile
.
name
,
"service-proxy"
,
true
)
}
mobile/src/main/scala/com/github/shadowsocks/
Shadowsocks
Notification.scala
→
mobile/src/main/scala/com/github/shadowsocks/
bg/Service
Notification.scala
View file @
265ecc04
...
...
@@ -18,7 +18,7 @@
/* */
/*******************************************************************************/
package
com.github.shadowsocks
package
com.github.shadowsocks
.bg
import
java.util.Locale
...
...
@@ -28,15 +28,15 @@ import android.os.{Build, PowerManager}
import
android.support.v4.app.NotificationCompat
import
android.support.v4.app.NotificationCompat.BigTextStyle
import
android.support.v4.content.ContextCompat
import
android.support.v4.os.BuildCompat
import
com.github.shadowsocks.aidl.IShadowsocksServiceCallback.Stub
import
com.github.shadowsocks.utils.
{
Action
,
State
,
TrafficMonitor
,
Utils
}
import
com.github.shadowsocks.utils.
{
Action
,
Utils
}
import
com.github.shadowsocks.
{
MainActivity
,
R
}
/**
* @author Mygod
*/
class
S
hadowsocks
Notification
(
private
val
service
:
BaseService
,
profileName
:
String
,
channel
:
String
,
visible
:
Boolean
=
false
)
{
class
S
ervice
Notification
(
private
val
service
:
BaseService
,
profileName
:
String
,
channel
:
String
,
visible
:
Boolean
=
false
)
{
private
val
keyGuard
=
service
.
getSystemService
(
Context
.
KEYGUARD_SERVICE
).
asInstanceOf
[
KeyguardManager
]
private
lazy
val
nm
=
service
.
getSystemService
(
Context
.
NOTIFICATION_SERVICE
).
asInstanceOf
[
NotificationManager
]
private
lazy
val
callback
=
new
Stub
{
...
...
@@ -76,7 +76,7 @@ class ShadowsocksNotification(private val service: BaseService, profileName: Str
service
.
registerReceiver
(
lockReceiver
,
screenFilter
)
private
def
update
(
action
:
String
,
forceShow
:
Boolean
=
false
)
=
if
(
forceShow
||
service
.
getState
==
State
.
CONNECTED
)
action
match
{
if
(
forceShow
||
service
.
getState
==
S
erviceS
tate
.
CONNECTED
)
action
match
{
case
Intent
.
ACTION_SCREEN_OFF
=>
setVisible
(
visible
&&
!
Utils
.
isLollipopOrAbove
,
forceShow
)
unregisterCallback
()
// unregister callback to save battery
...
...
mobile/src/main/scala/com/github/shadowsocks/bg/ServiceState.scala
0 → 100644
View file @
265ecc04
package
com.github.shadowsocks.bg
/**
* @author Mygod
*/
object
ServiceState
{
/**
* This state will never be broadcast by the service. This state is only used to indicate that the current context
* hasn't bound to any context.
*/
val
IDLE
=
0
val
CONNECTING
=
1
val
CONNECTED
=
2
val
STOPPING
=
3
val
STOPPED
=
4
}
mobile/src/main/scala/com/github/shadowsocks/
Shadowsocks
TileService.scala
→
mobile/src/main/scala/com/github/shadowsocks/
bg/Service
TileService.scala
View file @
265ecc04
...
...
@@ -18,20 +18,20 @@
/* */
/*******************************************************************************/
package
com.github.shadowsocks
package
com.github.shadowsocks
.bg
import
android.annotation.TargetApi
import
android.graphics.drawable.Icon
import
android.service.quicksettings.
{
Tile
,
TileService
}
import
com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import
com.github.shadowsocks.utils.
{
State
,
Utils
}
import
com.github.shadowsocks.utils.Utils
import
com.github.shadowsocks.
{
R
,
ServiceBoundContext
}
/**
* @author Mygod
*/
@TargetApi
(
24
)
final
class
ShadowsocksTileService
extends
TileService
with
ServiceBoundContext
{
final
class
ServiceTileService
extends
TileService
with
ServiceBoundContext
{
private
lazy
val
iconIdle
=
Icon
.
createWithResource
(
this
,
R
.
drawable
.
ic_start_idle
).
setTint
(
0x80ffffff
)
private
lazy
val
iconBusy
=
Icon
.
createWithResource
(
this
,
R
.
drawable
.
ic_start_busy
)
private
lazy
val
iconConnected
=
Icon
.
createWithResource
(
this
,
R
.
drawable
.
ic_start_connected
)
...
...
@@ -41,11 +41,11 @@ final class ShadowsocksTileService extends TileService with ServiceBoundContext
val
tile
=
getQsTile
if
(
tile
!=
null
)
{
state
match
{
case
State
.
STOPPED
=>
case
S
erviceS
tate
.
STOPPED
=>
tile
.
setIcon
(
iconIdle
)
tile
.
setLabel
(
getString
(
R
.
string
.
app_name
))
tile
.
setState
(
Tile
.
STATE_INACTIVE
)
case
State
.
CONNECTED
=>
case
S
erviceS
tate
.
CONNECTED
=>
tile
.
setIcon
(
iconConnected
)
tile
.
setLabel
(
if
(
profileName
==
null
)
getString
(
R
.
string
.
app_name
)
else
profileName
)
tile
.
setState
(
Tile
.
STATE_ACTIVE
)
...
...
@@ -74,8 +74,8 @@ final class ShadowsocksTileService extends TileService with ServiceBoundContext
override
def
onClick
()
:
Unit
=
if
(
isLocked
)
unlockAndRun
(
toggle
)
else
toggle
()
private
def
toggle
()
=
if
(
bgService
!=
null
)
bgService
.
getState
match
{
case
State
.
STOPPED
=>
Utils
.
startSsService
(
this
)
case
State
.
CONNECTED
=>
Utils
.
stopSsService
(
this
)
case
S
erviceS
tate
.
STOPPED
=>
Utils
.
startSsService
(
this
)
case
S
erviceS
tate
.
CONNECTED
=>
Utils
.
stopSsService
(
this
)
case
_
=>
// ignore
}
}
mobile/src/main/scala/com/github/shadowsocks/
utils
/TrafficMonitor.scala
→
mobile/src/main/scala/com/github/shadowsocks/
bg
/TrafficMonitor.scala
View file @
265ecc04
...
...
@@ -18,7 +18,7 @@
/* */
/*******************************************************************************/
package
com.github.shadowsocks.
utils
package
com.github.shadowsocks.
bg
import
java.text.DecimalFormat
...
...
mobile/src/main/scala/com/github/shadowsocks/
utils
/TrafficMonitorThread.scala
→
mobile/src/main/scala/com/github/shadowsocks/
bg
/TrafficMonitorThread.scala
View file @
265ecc04
...
...
@@ -18,7 +18,7 @@
/* */
/*******************************************************************************/
package
com.github.shadowsocks.
utils
package
com.github.shadowsocks.
bg
import
java.io.
{
File
,
IOException
}
import
java.nio.
{
ByteBuffer
,
ByteOrder
}
...
...
mobile/src/main/scala/com/github/shadowsocks/
ShadowsocksLocal
Service.scala
→
mobile/src/main/scala/com/github/shadowsocks/
bg/Transproxy
Service.scala
View file @
265ecc04
...
...
@@ -18,104 +18,80 @@
/* */
/*******************************************************************************/
package
com.github.shadowsocks
package
com.github.shadowsocks
.bg
import
java.io.File
import
java.net.
{
Inet6Address
,
InetAddress
}
import
java.util.Locale
import
android.app.Service
import
android.content._
import
android.os._
import
android.util.Log
import
com.github.shadowsocks.GuardedProcess
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.acl.
{
Acl
,
AclSyncJob
}
import
com.github.shadowsocks.database.Profile
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks.utils.IOUtils
import
scala.collection.JavaConversions._
import
scala.collection.mutable.ArrayBuffer
class
ShadowsocksLocalService
extends
BaseService
{
val
TAG
=
"ShadowsocksLocalService"
object
TransproxyService
{
private
val
REDSOCKS_CONFIG
=
"base {\n"
+
" log_debug = off;\n"
+
" log_info = off;\n"
+
" log = stderr;\n"
+
" daemon = off;\n"
+
" redirector = iptables;\n"
+
"}\n"
+
"redsocks {\n"
+
" local_ip = 127.0.0.1;\n"
+
" local_port = %d;\n"
+
" ip = 127.0.0.1;\n"
+
" port = %d;\n"
+
" type = socks5;\n"
+
"}\n"
}
var
sslocalProcess
:
GuardedProcess
=
_
class
TransproxyService
extends
LocalDnsService
{
import
TransproxyService._
def
startShadowsocksDaemon
()
{
val
cmd
=
ArrayBuffer
[
String
](
getApplicationInfo
.
nativeLibraryDir
+
"/libss-local.so"
,
"-b"
,
"127.0.0.1"
,
"-l"
,
profile
.
localPort
.
toString
,
"-t"
,
"600"
,
"-c"
,
buildShadowsocksConfig
(
"ss-local-local.conf"
))
val
TAG
=
"ShadowsocksTransproxyService"
if
(
TcpFastOpen
.
sendEnabled
)
cmd
+=
"--fast-open"
var
sstunnelProcess
:
GuardedProcess
=
_
var
redsocksProcess
:
GuardedProcess
=
_
if
(
profile
.
route
!=
Acl
.
ALL
)
{
cmd
+=
"--acl"
cmd
+=
Acl
.
getFile
(
profile
.
route
match
{
case
Acl
.
CUSTOM_RULES
=>
Acl
.
CUSTOM_RULES_FLATTENED
case
route
=>
route
}).
getAbsolutePath
}
def
startDNSTunnel
()
{
val
cmd
=
ArrayBuffer
[
String
](
new
File
(
getApplicationInfo
.
nativeLibraryDir
,
Executable
.
SS_TUNNEL
).
getAbsolutePath
,
"-t"
,
"10"
,
"-b"
,
"127.0.0.1"
,
"-u"
,
"-l"
,
app
.
dataStore
.
portLocalDns
.
toString
,
// ss-tunnel listens on the same port as overture
"-L"
,
profile
.
remoteDns
.
split
(
","
).
head
.
trim
+
":53"
,
"-c"
,
"shadowsocks.json"
)
// config is already built by BaseService
ss
loca
lProcess
=
new
GuardedProcess
(
cmd
:
_
*
).
start
()
ss
tunne
lProcess
=
new
GuardedProcess
(
cmd
:
_
*
).
start
()
}
/** Called when the activity is first created. */
def
handleConnection
()
{
startShadowsocksDaemon
()
def
startRedsocksDaemon
()
{
IOUtils
.
writeString
(
new
File
(
getFilesDir
,
"redsocks.conf"
),
REDSOCKS_CONFIG
.
formatLocal
(
Locale
.
ENGLISH
,
app
.
dataStore
.
portTransproxy
,
app
.
dataStore
.
portProxy
))
redsocksProcess
=
new
GuardedProcess
(
new
File
(
getApplicationInfo
.
nativeLibraryDir
,
Executable
.
REDSOCKS
).
getAbsolutePath
,
"-c"
,
"redsocks.conf"
).
start
()
}
def
onBind
(
intent
:
Intent
)
:
IBinder
=
{
Log
.
d
(
TAG
,
"onBind"
)
if
(
Action
.
SERVICE
==
intent
.
getAction
)
{
binder
}
else
{
null
}
override
def
startNativeProcesses
()
{
startRedsocksDaemon
()
super
.
startNativeProcesses
()
if
(
profile
.
udpdns
)
startDNSTunnel
()
}
def
killProcesses
()
{
if
(
sslocalProcess
!=
null
)
{
sslocalProcess
.
destroy
()
sslocalProcess
=
null
override
def
killProcesses
()
{
super
.
killProcesses
()
if
(
sstunnelProcess
!=
null
)
{
sstunnelProcess
.
destroy
()
sstunnelProcess
=
null
}
}
override
def
connect
()
{
super
.
connect
()
// Clean up
killProcesses
()
if
(!
Utils
.
isNumeric
(
profile
.
host
))
Utils
.
resolve
(
profile
.
host
,
enableIPv6
=
true
)
match
{
case
Some
(
a
)
=>
profile
.
host
=
a
case
None
=>
throw
NameNotResolvedException
()
if
(
redsocksProcess
!=
null
)
{
redsocksProcess
.
destroy
()
redsocksProcess
=
null
}
handleConnection
()
if
(
profile
.
route
!=
Acl
.
ALL
&&
profile
.
route
!=
Acl
.
CUSTOM_RULES
)
AclSyncJob
.
schedule
(
profile
.
route
)
changeState
(
State
.
CONNECTED
)
}
override
def
createNotification
()
=
new
ShadowsocksNotification
(
this
,
profile
.
name
,
"service-local"
,
true
)
override
def
stopRunner
(
stopService
:
Boolean
,
msg
:
String
=
null
)
{
// channge the state
changeState
(
State
.
STOPPING
)
app
.
track
(
TAG
,
"stop"
)
// reset NAT
killProcesses
()
super
.
stopRunner
(
stopService
,
msg
)
}
def
createNotification
()
=
new
ServiceNotification
(
this
,
profile
.
name
,
"service-transproxy"
,
true
)
}
mobile/src/main/scala/com/github/shadowsocks/
Shadowsocks
VpnService.scala
→
mobile/src/main/scala/com/github/shadowsocks/
bg/
VpnService.scala
View file @
265ecc04
...
...
@@ -18,78 +18,49 @@
/* */
/*******************************************************************************/
package
com.github.shadowsocks
package
com.github.shadowsocks
.bg
import
java.io.File
import
java.util.Locale
import
android.app.Service
import
android.content.
_
import
android.content.
Intent
import
android.content.pm.PackageManager.NameNotFoundException
import
android.net.
VpnService
import
android.os.
_
import
android.net.
{
VpnService
=>
BaseVpnService
}
import
android.os.
{
IBinder
,
ParcelFileDescriptor
}
import
android.util.Log
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.acl.
{
Acl
,
AclSyncJob
,
Subnet
}
import
com.github.shadowsocks.utils._
import
com.github.shadowsocks._
import
com.github.shadowsocks.acl.
{
Acl
,
Subnet
}
import
com.github.shadowsocks.utils.Utils
import
scala.collection.mutable.ArrayBuffer
class
ShadowsocksVpnService
extends
VpnService
with
Base
Service
{
class
VpnService
extends
BaseVpnService
with
LocalDns
Service
{
val
TAG
=
"ShadowsocksVpnService"
val
VPN_MTU
=
1500
val
PRIVATE_VLAN
=
"26.26.26.%s"
val
PRIVATE_VLAN6
=
"fdfe:dcba:9876::%s"
var
conn
:
ParcelFileDescriptor
=
_
var
vpnThread
:
Shadowsocks
VpnThread
=
_
var
vpnThread
:
VpnThread
=
_
var
sslocalProcess
:
GuardedProcess
=
_
var
overtureProcess
:
GuardedProcess
=
_
var
tun2socksProcess
:
GuardedProcess
=
_
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
onBind
(
intent
:
Intent
)
:
IBinder
=
intent
.
getAction
match
{
case
BaseVpnService
.
SERVICE_INTERFACE
=>
super
[
VpnService
].
onBind
(
intent
)
case
_
=>
super
[
LocalDnsService
].
onBind
(
intent
)
}
override
def
onRevoke
()
{
stopRunner
(
stopService
=
true
)
}
override
def
stopRunner
(
stopService
:
Boolean
,
msg
:
String
=
null
)
{
override
def
killProcesses
()
{
if
(
vpnThread
!=
null
)
{
vpnThread
.
stopThread
()
vpnThread
=
null
}
// channge the state
changeState
(
State
.
STOPPING
)
app
.
track
(
TAG
,
"stop"
)
// reset VPN
killProcesses
()
// close connections
if
(
conn
!=
null
)
{
conn
.
close
()
conn
=
null
}
super
.
stopRunner
(
stopService
,
msg
)
}
def
killProcesses
()
{
if
(
sslocalProcess
!=
null
)
{
sslocalProcess
.
destroy
()
sslocalProcess
=
null
}
super
.
killProcesses
()
if
(
tun2socksProcess
!=
null
)
{
tun2socksProcess
.
destroy
()
tun2socksProcess
=
null
...
...
@@ -98,85 +69,41 @@ class ShadowsocksVpnService extends VpnService with BaseService {
overtureProcess
.
destroy
()
overtureProcess
=
null
}
// close connections
if
(
conn
!=
null
)
{
conn
.
close
()
conn
=
null
}
}
override
def
onStartCommand
(
intent
:
Intent
,
flags
:
Int
,
startId
:
Int
)
:
Int
=
{
override
def
onStartCommand
(
intent
:
Intent
,
flags
:
Int
,
startId
:
Int
)
:
Int
=
if
(
app
.
usingVpnMode
)
// ensure the VPNService is prepared
if
(
VpnService
.
prepare
(
this
)
!=
null
)
{
if
(
Base
VpnService
.
prepare
(
this
)
!=
null
)
{
val
i
=
new
Intent
(
this
,
classOf
[
ShadowsocksRunnerActivity
])
i
.
addFlags
(
Intent
.
FLAG_ACTIVITY_NEW_TASK
)
startActivity
(
i
)
stopRunner
(
stopService
=
true
)
Service
.
START_NOT_STICKY
}
else
super
.
onStartCommand
(
intent
,
flags
,
startId
)
else
{
// system or other apps are trying to start this service but other services could be running
stopSelf
()
Service
.
START_NOT_STICKY
}
override
def
createNotification
()
=
new
ShadowsocksNotification
(
this
,
profile
.
name
,
"service-vpn"
)
override
def
connect
()
{
super
.
connect
()
vpnThread
=
new
ShadowsocksVpnThread
(
this
)
vpnThread
.
start
()
// reset the context
killProcesses
()
// Resolve the server address
if
(!
Utils
.
isNumeric
(
profile
.
host
))
Utils
.
resolve
(
profile
.
host
,
enableIPv6
=
true
)
match
{
case
Some
(
addr
)
=>
profile
.
host
=
addr
case
None
=>
throw
NameNotResolvedException
()
}
handleConnection
()
changeState
(
State
.
CONNECTED
)
if
(
profile
.
route
!=
Acl
.
ALL
&&
profile
.
route
!=
Acl
.
CUSTOM_RULES
)
AclSyncJob
.
schedule
(
profile
.
route
)
}
override
def
createNotification
()
=
new
ServiceNotification
(
this
,
profile
.
name
,
"service-vpn"
)
/** Called when the activity is first created. */
def
handleConnection
()
{
startShadowsocksDaemon
()
override
def
startNativeProcesses
()
{
vpnThread
=
new
VpnThread
(
this
)
vpnThread
.
start
()
if
(!
profile
.
udpdns
)
{
startDnsDaemon
()
}
super
.
startNativeProcesses
()
val
fd
=
startVpn
()
if
(!
sendFd
(
fd
))
throw
new
Exception
(
"sendFd failed"
)
}
override
protected
def
buildPluginCommandLine
()
:
ArrayBuffer
[
String
]
=
super
.
buildPluginCommandLine
()
+=
"-V"
def
startShadowsocksDaemon
()
{
val
cmd
=
ArrayBuffer
[
String
](
getApplicationInfo
.
nativeLibraryDir
+
"/libss-local.so"
,
"-V"
,
"-u"
,
"-b"
,
"127.0.0.1"
,
"-l"
,
profile
.
localPort
.
toString
,
"-t"
,
"600"
,
"-c"
,
buildShadowsocksConfig
(
"ss-local-vpn.conf"
))
if
(
profile
.
route
!=
Acl
.
ALL
)
{
cmd
+=
"--acl"
cmd
+=
Acl
.
getFile
(
profile
.
route
match
{
case
Acl
.
CUSTOM_RULES
=>
Acl
.
CUSTOM_RULES_FLATTENED
case
route
=>
route
}).
getAbsolutePath
}
if
(
TcpFastOpen
.
sendEnabled
)
cmd
+=
"--fast-open"
sslocalProcess
=
new
GuardedProcess
(
cmd
:
_
*
).
start
()
}
def
startDnsDaemon
()
{
overtureProcess
=
new
GuardedProcess
(
getApplicationInfo
.
nativeLibraryDir
+
"/liboverture.so"
,
"-c"
,
buildOvertureConfig
(
"overture-vpn.conf"
),
"-V"
)
.
start
()
}
override
protected
def
buildAdditionalArguments
(
cmd
:
ArrayBuffer
[
String
])
:
ArrayBuffer
[
String
]
=
cmd
+=
"-V"
def
startVpn
()
:
Int
=
{
...
...
@@ -229,10 +156,10 @@ class ShadowsocksVpnService extends VpnService with BaseService {
val
fd
=
conn
.
getFd
var
cmd
=
ArrayBuffer
[
String
](
getApplicationInfo
.
nativeLibraryDir
+
"/libtun2socks.so"
,
var
cmd
=
ArrayBuffer
[
String
](
new
File
(
getApplicationInfo
.
nativeLibraryDir
,
Executable
.
TUN2SOCKS
).
getAbsolutePath
,
"--netif-ipaddr"
,
PRIVATE_VLAN
.
formatLocal
(
Locale
.
ENGLISH
,
"2"
),
"--netif-netmask"
,
"255.255.255.0"
,
"--socks-server-addr"
,
"127.0.0.1:"
+
profile
.
localPort
,
"--socks-server-addr"
,
"127.0.0.1:"
+
app
.
dataStore
.
portProxy
,
"--tunfd"
,
fd
.
toString
,
"--tunmtu"
,
VPN_MTU
.
toString
,
"--sock-path"
,
"sock_path"
,
...
...
@@ -244,8 +171,7 @@ class ShadowsocksVpnService extends VpnService with BaseService {
cmd
+=
"--enable-udprelay"
if
(!
profile
.
udpdns
)
cmd
+=
(
"--dnsgw"
,
"%s:%d"
.
formatLocal
(
Locale
.
ENGLISH
,
"127.0.0.1"
,
profile
.
localPort
+
53
))
cmd
+=
(
"--dnsgw"
,
"%s:%d"
.
formatLocal
(
Locale
.
ENGLISH
,
"127.0.0.1"
,
app
.
dataStore
.
portLocalDns
))
tun2socksProcess
=
new
GuardedProcess
(
cmd
:
_
*
).
start
(()
=>
sendFd
(
fd
))
...
...
mobile/src/main/scala/com/github/shadowsocks/
Shadowsocks
VpnThread.scala
→
mobile/src/main/scala/com/github/shadowsocks/
bg/
VpnThread.scala
View file @
265ecc04
...
...
@@ -18,7 +18,7 @@
/* */
/*******************************************************************************/
package
com.github.shadowsocks
package
com.github.shadowsocks
.bg
import
java.io.
{
File
,
FileDescriptor
,
IOException
}
import
java.lang.reflect.Method
...
...
@@ -26,14 +26,15 @@ import java.util.concurrent.Executors
import
android.net.
{
LocalServerSocket
,
LocalSocket
,
LocalSocketAddress
}
import
android.util.Log
import
com.github.shadowsocks.JniHelper
import
com.github.shadowsocks.ShadowsocksApplication.app
object
Shadowsocks
VpnThread
{
object
VpnThread
{
val
getInt
:
Method
=
classOf
[
FileDescriptor
].
getDeclaredMethod
(
"getInt$"
)
}
class
ShadowsocksVpnThread
(
service
:
Shadowsocks
VpnService
)
extends
Thread
{
import
Shadowsocks
VpnThread._
class
VpnThread
(
service
:
VpnService
)
extends
Thread
{
import
VpnThread._
val
TAG
=
"ShadowsocksVpnService"
val
protect
=
new
File
(
service
.
getFilesDir
,
"protect_path"
)
...
...
mobile/src/main/scala/com/github/shadowsocks/database/DBHelper.scala
View file @
265ecc04
...
...
@@ -22,11 +22,12 @@ package com.github.shadowsocks.database
import
java.nio.ByteBuffer
import
android.content.
{
Context
,
SharedPreferences
}
import
android.content.
Context
import
android.content.pm.ApplicationInfo
import
android.database.sqlite.SQLiteDatabase
import
android.preference.PreferenceManager
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.preference.OrmLitePreferenceDataStore
import
com.github.shadowsocks.utils.Key
import
com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper
import
com.j256.ormlite.dao.Dao
...
...
@@ -50,7 +51,7 @@ object DBHelper {
}
class
DBHelper
(
val
context
:
Context
)
extends
OrmLiteSqliteOpenHelper
(
context
,
DBHelper
.
PROFILE
,
null
,
2
3
)
{
extends
OrmLiteSqliteOpenHelper
(
context
,
DBHelper
.
PROFILE
,
null
,
2
4
)
{
import
DBHelper._
lazy
val
profileDao
:
Dao
[
Profile
,
Int
]
=
getDao
(
classOf
[
Profile
])
...
...
@@ -148,12 +149,8 @@ class DBHelper(val context: Context)
val
old
=
PreferenceManager
.
getDefaultSharedPreferences
(
app
)
kvPairDao
.
createOrUpdate
(
new
KeyValuePair
(
Key
.
id
,
TYPE_INT
,
ByteBuffer
.
allocate
(
4
).
putInt
(
old
.
getInt
(
Key
.
id
,
0
)).
array
()))
kvPairDao
.
createOrUpdate
(
new
KeyValuePair
(
Key
.
isNAT
,
TYPE_BOOLEAN
,
ByteBuffer
.
allocate
(
1
).
put
((
if
(
old
.
getBoolean
(
Key
.
isNAT
,
false
))
1
else
0
).
toByte
).
array
()))
kvPairDao
.
createOrUpdate
(
new
KeyValuePair
(
Key
.
tfo
,
TYPE_BOOLEAN
,
ByteBuffer
.
allocate
(
1
).
put
((
if
(
old
.
getBoolean
(
Key
.
tfo
,
false
))
1
else
0
).
toByte
).
array
()))
kvPairDao
.
createOrUpdate
(
new
KeyValuePair
(
Key
.
currentVersionCode
,
TYPE_INT
,
ByteBuffer
.
allocate
(
4
).
putInt
(-
1
).
array
()))
}
}
catch
{
case
ex
:
Exception
=>
...
...
mobile/src/main/scala/com/github/shadowsocks/database/Profile.scala
View file @
265ecc04
...
...
@@ -23,7 +23,6 @@ package com.github.shadowsocks.database
import
java.util.Locale
import
android.net.Uri
import
android.os.Binder
import
android.util.Base64
import
com.github.shadowsocks.plugin.PluginConfiguration
import
com.github.shadowsocks.preference.OrmLitePreferenceDataStore
...
...
@@ -40,10 +39,6 @@ class Profile {
@DatabaseField
var
host
:
String
=
"198.199.101.152"
// hopefully hashCode = mHandle doesn't change, currently this is true from KitKat to Nougat
@DatabaseField
var
localPort
:
Int
=
1080
+
Binder
.
getCallingUserHandle
.
hashCode
@DatabaseField
var
remotePort
:
Int
=
8388
...
...
@@ -111,7 +106,6 @@ class Profile {
def
serialize
(
store
:
OrmLitePreferenceDataStore
)
{
store
.
putString
(
Key
.
name
,
name
)
store
.
putString
(
Key
.
host
,
host
)
store
.
putInt
(
Key
.
localPort
,
localPort
)
store
.
putInt
(
Key
.
remotePort
,
remotePort
)
store
.
putString
(
Key
.
password
,
password
)
store
.
putString
(
Key
.
route
,
route
)
...
...
@@ -129,7 +123,6 @@ class Profile {
// It's assumed that default values are never used, so 0/false/null is always used even if that isn't the case
name
=
store
.
getString
(
Key
.
name
,
null
)
host
=
store
.
getString
(
Key
.
host
,
null
)
localPort
=
store
.
getInt
(
Key
.
localPort
,
0
)
remotePort
=
store
.
getInt
(
Key
.
remotePort
,
0
)
password
=
store
.
getString
(
Key
.
password
,
null
)
method
=
store
.
getString
(
Key
.
method
,
null
)
...
...
mobile/src/main/scala/com/github/shadowsocks/plugin/PluginManager.scala
View file @
265ecc04
...
...
@@ -28,8 +28,7 @@ object PluginManager {
* If you don't plan to publish any plugin but is developing/has developed some, it's not necessary to add your
* public key yet since it will also automatically trust packages signed by the same signatures, e.g. debug keys.
*/
lazy
val
trustedSignatures
:
Set
[
Signature
]
=
app
.
getPackageManager
.
getPackageInfo
(
app
.
getPackageName
,
PackageManager
.
GET_SIGNATURES
).
signatures
.
toSet
+
lazy
val
trustedSignatures
:
Set
[
Signature
]
=
app
.
info
.
signatures
.
toSet
+
new
Signature
(
Base64
.
decode
(
// @Mygod
"""
|MIIDWzCCAkOgAwIBAgIEUzfv8DANBgkqhkiG9w0BAQsFADBdMQswCQYDVQQGEwJD
...
...
mobile/src/main/scala/com/github/shadowsocks/plugin/ResolvedPlugin.scala
View file @
265ecc04
...
...
@@ -19,6 +19,5 @@ abstract class ResolvedPlugin(resolveInfo: ResolveInfo, packageManager: PackageM
override
final
lazy
val
icon
:
Drawable
=
resolveInfo
.
loadIcon
(
packageManager
)
override
final
lazy
val
defaultConfig
:
String
=
metaData
.
getString
(
PluginContract
.
METADATA_KEY_DEFAULT_CONFIG
)
override
def
packageName
:
String
=
resolveInfo
.
resolvePackageName
override
final
lazy
val
trusted
:
Boolean
=
packageManager
.
getPackageInfo
(
packageName
,
PackageManager
.
GET_SIGNATURES
)
.
signatures
.
exists
(
PluginManager
.
trustedSignatures
.
contains
)
override
final
lazy
val
trusted
:
Boolean
=
app
.
info
.
signatures
.
exists
(
PluginManager
.
trustedSignatures
.
contains
)
}
mobile/src/main/scala/com/github/shadowsocks/preference/OrmLitePreferenceDataStore.scala
View file @
265ecc04
...
...
@@ -100,8 +100,10 @@ final class OrmLitePreferenceDataStore(dbHelper: DBHelper) extends PreferenceDat
def
profileId
:
Int
=
getInt
(
Key
.
id
,
0
)
def
profileId_=
(
i
:
Int
)
:
Unit
=
putInt
(
Key
.
id
,
i
)
def
isNAT
:
Boolean
=
getBoolean
(
Key
.
isNAT
)
def
isNAT_=
(
value
:
Boolean
)
:
Unit
=
putBoolean
(
Key
.
isNAT
,
value
)
def
serviceMode
:
String
=
getString
(
Key
.
serviceMode
,
Key
.
modeVpn
)
def
portProxy
:
Int
=
getInt
(
Key
.
portProxy
,
0
)
def
portLocalDns
:
Int
=
getInt
(
Key
.
portLocalDns
,
0
)
def
portTransproxy
:
Int
=
getInt
(
Key
.
portTransproxy
,
0
)
def
proxyApps
:
Boolean
=
getBoolean
(
Key
.
proxyApps
)
def
proxyApps_=
(
value
:
Boolean
)
:
Unit
=
putBoolean
(
Key
.
proxyApps
,
value
)
...
...
mobile/src/main/scala/com/github/shadowsocks/utils/Constants.scala
View file @
265ecc04
...
...
@@ -20,40 +20,20 @@
package
com.github.shadowsocks.utils
object
Executable
{
val
REDSOCKS
=
"redsocks"
val
PDNSD
=
"pdnsd"
val
SS_LOCAL
=
"ss-local"
val
SS_TUNNEL
=
"ss-tunnel"
val
TUN2SOCKS
=
"tun2socks"
val
EXECUTABLES
=
Array
(
SS_LOCAL
,
SS_TUNNEL
,
PDNSD
,
REDSOCKS
,
TUN2SOCKS
)
}
object
ConfigUtils
{
val
REDSOCKS
=
"base {\n"
+
" log_debug = off;\n"
+
" log_info = off;\n"
+
" log = stderr;\n"
+
" daemon = off;\n"
+
" redirector = iptables;\n"
+
"}\n"
+
"redsocks {\n"
+
" local_ip = 127.0.0.1;\n"
+
" local_port = 8123;\n"
+
" ip = 127.0.0.1;\n"
+
" port = %d;\n"
+
" type = socks5;\n"
+
"}\n"
}
object
Key
{
val
id
=
"profileId"
val
name
=
"profileName"
val
individual
=
"Proxyed"
val
isNAT
=
"isNAT"
val
serviceMode
=
"serviceMode"
val
modeProxy
=
"proxy"
val
modeVpn
=
"vpn"
val
modeTransproxy
=
"transproxy"
val
portProxy
=
"portProxy"
val
portLocalDns
=
"portLocalDns"
val
portTransproxy
=
"portTransproxy"
val
route
=
"route"
val
isAutoConnect
=
"isAutoConnect"
...
...
@@ -67,7 +47,6 @@ object Key {
val
password
=
"sitekey"
val
method
=
"encMethod"
val
remotePort
=
"remotePortNum"
val
localPort
=
"localPortNum"
val
remoteDns
=
"remoteDns"
val
plugin
=
"plugin"
...
...
@@ -76,19 +55,7 @@ object Key {
val
dirty
=
"profileDirty"
val
tfo
=
"tcp_fastopen"
val
currentVersionCode
=
"currentVersionCode"
}
object
State
{
/**
* This state will never be broadcast by the service. This state is only used to indicate that the current context
* hasn't bound to any context.
*/
val
IDLE
=
0
val
CONNECTING
=
1
val
CONNECTED
=
2
val
STOPPING
=
3
val
STOPPED
=
4
val
assetUpdateTime
=
"assetUpdateTime"
}
object
Action
{
...
...
mobile/src/main/scala/com/github/shadowsocks/utils/Utils.scala
View file @
265ecc04
...
...
@@ -33,7 +33,8 @@ import android.view.View.MeasureSpec
import
android.view.
{
Gravity
,
View
,
Window
}
import
android.widget.Toast
import
com.github.shadowsocks.ShadowsocksApplication.app
import
com.github.shadowsocks.
{
BuildConfig
,
ShadowsocksLocalService
,
ShadowsocksVpnService
}
import
com.github.shadowsocks.bg.
{
ProxyService
,
TransproxyService
,
VpnService
}
import
com.github.shadowsocks.BuildConfig
import
org.xbill.DNS._
import
scala.collection.JavaConversions._
...
...
@@ -47,11 +48,8 @@ object Utils {
def
isLollipopOrAbove
:
Boolean
=
Build
.
VERSION
.
SDK_INT
>=
Build
.
VERSION_CODES
.
LOLLIPOP
def
getSignature
(
context
:
Context
)
:
String
=
{
val
info
=
context
.
getPackageManager
.
getPackageInfo
(
context
.
getPackageName
,
PackageManager
.
GET_SIGNATURES
)
val
mdg
=
MessageDigest
.
getInstance
(
"SHA-1"
)
mdg
.
update
(
info
.
signatures
(
0
).
toByteArray
)
mdg
.
update
(
app
.
info
.
signatures
(
0
).
toByteArray
)
new
String
(
Base64
.
encode
(
mdg
.
digest
,
0
))
}
...
...
@@ -107,7 +105,7 @@ object Utils {
}
def
resolve
(
host
:
String
,
enableIPv6
:
Boolean
)
:
Option
[
String
]
=
(
if
(
enableIPv6
&&
Utils
.
isIPv6Support
)
resolve
(
host
,
Type
.
AAAA
)
else
None
).
orElse
(
resolve
(
host
,
Type
.
A
))
(
if
(
enableIPv6
&&
isIPv6Support
)
resolve
(
host
,
Type
.
AAAA
)
else
None
).
orElse
(
resolve
(
host
,
Type
.
A
))
.
orElse
(
resolve
(
host
))
private
lazy
val
isNumericMethod
=
classOf
[
InetAddress
].
getMethod
(
"isNumeric"
,
classOf
[
String
])
...
...
@@ -137,8 +135,7 @@ object Utils {
}
def
startSsService
(
context
:
Context
)
{
val
intent
=
new
Intent
(
context
,
if
(
app
.
dataStore
.
isNAT
)
classOf
[
ShadowsocksLocalService
]
else
classOf
[
ShadowsocksVpnService
])
val
intent
=
new
Intent
(
context
,
app
.
serviceClass
)
if
(
Build
.
VERSION
.
SDK_INT
>=
26
)
context
.
startForegroundService
(
intent
)
else
context
.
startService
(
intent
)
}
def
reloadSsService
(
context
:
Context
)
:
Unit
=
context
.
sendBroadcast
(
new
Intent
(
Action
.
RELOAD
))
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment