Unverified Commit fd1683c0 authored by initrider's avatar initrider Committed by GitHub

Merge pull request #5 from shadowsocks/master

Sync
parents 9c7f3867 8335a580
FROM circleci/android:api-28-ndk
RUN \
wget -O /tmp/go.tgz "https://golang.org/dl/go1.11.linux-amd64.tar.gz"; \
sudo tar -C /usr/local -xzf /tmp/go.tgz; \
rm /tmp/go.tgz
ENV PATH /usr/local/go/bin:$PATH
version: 2
jobs:
build:
working_directory: ~/code
docker:
- image: shadowsocks/shadowsocks-android:circleci
environment:
JVM_OPTS: -Xmx3500m
GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy="in-process"
steps:
- checkout
- run: git submodule update --init --recursive
- run: pushd core/src/overture; ./make.bash 21; popd
- restore_cache:
key: jars-{{ checksum "build.gradle" }}
- run:
name: Run Build and Tests
command: ./gradlew assembleDebug check
- save_cache:
paths:
- ~/.gradle
- ~/.android/build-cache
key: jars-{{ checksum "build.gradle" }}
- store_artifacts:
path: mobile/build/outputs/apk
destination: apk/mobile
- store_artifacts:
path: tv/build/outputs/apk
destination: apk/tv
- store_artifacts:
path: core/build/reports
destination: reports/core
- store_artifacts:
path: mobile/build/reports
destination: reports/mobile
- store_artifacts:
path: plugin/build/reports
destination: reports/plugin
- store_artifacts:
path: tv/build/reports
destination: reports/tv
Please read FAQ then answer these questions before submitting your issue. Thanks! ---
name: Bug report
about: Create a report to help us improve
### Environment ---
* Android version: *e.g. "7.0.0_r14", more detailed description is preferred* Please read contributing guidelines. Thanks.
* Device: *e.g. Google Pixel XL*
* Shadowsocks version: *version code "v3.0.0" or commit ID "a073f85"*
* Last version that did not exhibit the issue (if applicable):
### Configuration **Describe the bug**
A clear and concise description of what the bug is.
_Put an `x` inside the [ ] that applies._ **To Reproduce**
Steps to reproduce the behavior:
1. Go to '...'
2. Click on '....'
3. Scroll down to '....'
4. See error
**Expected behavior**
A clear and concise description of what you expected to happen.
**Screenshots**
If applicable, add screenshots to help explain your problem.
**Smartphone (please complete the following information):**
- Android version: [e.g. "7.0.0_r14", more detailed description is preferred]
- Device: [e.g. Google Pixel XL]
- Version: [e.g. version code "v3.0.0" or commit ID "a073f85"]
- Last version that did not exhibit the issue: [not applicable]
**Configuration**
Put an `x` inside the [ ] that applies.
* [ ] IPv4 server address * [ ] IPv4 server address
* [ ] IPv6 server address * [ ] IPv6 server address
...@@ -34,11 +54,5 @@ _Put an `x` inside the [ ] that applies._ ...@@ -34,11 +54,5 @@ _Put an `x` inside the [ ] that applies._
* [ ] TCP Fast Open * [ ] TCP Fast Open
* If you're not using VPN mode, please supply more details here: * If you're not using VPN mode, please supply more details here:
### What did you do? **Additional context**
Add any other context about the problem here.
### What did you expect to see?
### What did you see instead?
---
name: Feature request
about: Suggest an idea for this project
---
Please read contributing guidelines. Thanks.
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
**Additional context**
Add any other context or screenshots about the feature request here.
---
name: Questions/Support requests/Other
about: You should probably use the forum
---
Please use the forum at https://discourse.shadowsocks.org/c/implementations/shadowsocks-android instead. Your issue will probably be closed.
...@@ -26,7 +26,7 @@ The exclamation mark in the Wi-Fi/cellular icon appears because the system fails ...@@ -26,7 +26,7 @@ The exclamation mark in the Wi-Fi/cellular icon appears because the system fails
1. Some ROM has broken VPNService implementation, especially for IPv6; 1. Some ROM has broken VPNService implementation, especially for IPv6;
2. Some ROM has aggressive (or called broken) background service killing policy; 2. Some ROM has aggressive (or called broken) background service killing policy;
3. Some ROM like [Flyme](https://github.com/shadowsocks/shadowsocks-android/issues/1589) has **very** broken direct boot support; 3. Some ROM like [Flyme](https://github.com/shadowsocks/shadowsocks-android/issues/1821) is basically broken **in every way possible**;
4. If you have Xposed framework and/or battery saver apps, it's likely that this app wouldn't work well with these either. 4. If you have Xposed framework and/or battery saver apps, it's likely that this app wouldn't work well with these either.
* Fixes for MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772) * Fixes for MIUI: [#772](https://github.com/shadowsocks/shadowsocks-android/issues/772)
...@@ -36,6 +36,7 @@ The exclamation mark in the Wi-Fi/cellular icon appears because the system fails ...@@ -36,6 +36,7 @@ The exclamation mark in the Wi-Fi/cellular icon appears because the system fails
* Samsung and/or Brevent: [#1410](https://github.com/shadowsocks/shadowsocks-android/issues/1410) * Samsung and/or Brevent: [#1410](https://github.com/shadowsocks/shadowsocks-android/issues/1410)
* Another Samsung: [#1712](https://github.com/shadowsocks/shadowsocks-android/issues/1712) * Another Samsung: [#1712](https://github.com/shadowsocks/shadowsocks-android/issues/1712)
* Don't install this app on SD card because of permission issues: [#1124 (comment)](https://github.com/shadowsocks/shadowsocks-android/issues/1124#issuecomment-307556453) * Don't install this app on SD card because of permission issues: [#1124 (comment)](https://github.com/shadowsocks/shadowsocks-android/issues/1124#issuecomment-307556453)
* `INTERACT_ACROSS_USERS` permission missing: [#1184](https://github.com/shadowsocks/shadowsocks-android/issues/1184)
### How to pause Shadowsocks service? ### How to pause Shadowsocks service?
...@@ -55,12 +56,6 @@ More details: https://kb.adguard.com/en/android/solving-problems/battery ...@@ -55,12 +56,6 @@ More details: https://kb.adguard.com/en/android/solving-problems/battery
Allow this app to consume background data in app settings. Allow this app to consume background data in app settings.
### Why Camera permission is required on devices below Android 6.0?
To scan the QR code through the integrated QR scanner.
By the way, upgrade your Android system already.
### How to use Transproxy mode? ### How to use Transproxy mode?
1. Install [AFWall+](https://github.com/ukanth/afwall); 1. Install [AFWall+](https://github.com/ukanth/afwall);
......
### Type of changes
_Put an `x` inside the [ ] that applies._
* [ ] Bugfix
* [ ] New feature
* [ ] Translation
* [ ] General refinement
* Other:
### Details
Please provide more details about changes if necessary.
...@@ -14,16 +14,8 @@ core/src/overture/bin ...@@ -14,16 +14,8 @@ core/src/overture/bin
core/src/overture/pkg core/src/overture/pkg
core/src/overture/src/github.com/Sirupsen core/src/overture/src/github.com/Sirupsen
core/src/overture/src/github.com/mattn
core/src/overture/src/github.com/miekg core/src/overture/src/github.com/miekg
core/src/overture/src/github.com/shawn1m
core/src/overture/src/github.com/tools
core/src/overture/src/golang.org core/src/overture/src/golang.org
core/src/overture/src/gopkg.in
# work in progress
tv/
things/
# release apks # release apks
*.apk *.apk
...@@ -30,9 +30,6 @@ ...@@ -30,9 +30,6 @@
[submodule "core/src/main/jni/libev"] [submodule "core/src/main/jni/libev"]
path = core/src/main/jni/libev path = core/src/main/jni/libev
url = https://github.com/shadowsocks/libev.git url = https://github.com/shadowsocks/libev.git
[submodule "core/src/overture/go"]
path = core/src/overture/go
url = https://github.com/shadowsocks/go.git
[submodule "core/src/overture/src/github.com/shadowsocks/overture"] [submodule "core/src/overture/src/github.com/shadowsocks/overture"]
path = core/src/overture/src/github.com/shadowsocks/overture path = core/src/overture/src/github.com/shadowsocks/overture
url = https://github.com/shadowsocks/overture.git url = https://github.com/shadowsocks/overture.git
language: android sudo: required
jdk: addons:
- oraclejdk8 artifacts: true
env: services:
global: - docker
- GOROOT_BOOTSTRAP=$GOROOT
before_cache: before_install:
- rm -f $HOME/.gradle/caches/modules-2/modules-2.lock - docker pull shadowsocks/shadowsocks-android:circleci
- rm -fr $HOME/.gradle/caches/*/plugin-resolution/ - chmod -R a+wrx .
cache:
directories:
- $HOME/.gradle/caches/
- $HOME/.gradle/wrapper/
install:
- pushd $ANDROID_HOME
- rm -rf tools
- wget -q https://dl.google.com/android/repository/sdk-tools-linux-4333796.zip -O tools.zip
- unzip -q tools.zip
- popd
- touch $HOME/.android/repositories.cfg
- yes | sdkmanager
"build-tools;27.0.3"
ndk-bundle
"platforms;android-27"
>/dev/null
script: script:
- ./gradlew assembleDebug check - docker run --rm -v ${PWD}:/build shadowsocks/shadowsocks-android:circleci bash -c "cd /build; ./gradlew assembleDebug check"
Please check [FAQ](.github/faq.md) before submitting new issues. # Submitting Issues
And just in case you don't know, * Questions and general discussions should go to [Discourse forum](https://discourse.shadowsocks.org/c/implementations/shadowsocks-android);
[don't ask questions on issues](https://medium.com/@methane/why-you-must-not-ask-questions-on-github-issues-51d741d83fde). * Search for your issue here and at the forum and check [FAQ](https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md) before submitting new issues;
* Always provide information asked in the template unless you know what you're doing;
* Logcat would also be helpful.
## Shadowsocks for Android ## Shadowsocks for Android
[![Build Status](https://api.travis-ci.org/shadowsocks/shadowsocks-android.svg)](https://travis-ci.org/shadowsocks/shadowsocks-android) [![Build Status](https://travis-ci.com/shadowsocks/shadowsocks-android.svg?branch=master)](https://travis-ci.com/shadowsocks/shadowsocks-android)
[![API](https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=21) [![API](https://img.shields.io/badge/API-21%2B-brightgreen.svg?style=flat)](https://android-arsenal.com/api?level=21)
[![Releases](https://img.shields.io/github/downloads/shadowsocks/shadowsocks-android/total.svg)](https://github.com/shadowsocks/shadowsocks-android/releases) [![Releases](https://img.shields.io/github/downloads/shadowsocks/shadowsocks-android/total.svg)](https://github.com/shadowsocks/shadowsocks-android/releases)
[![Codacy Badge](https://api.codacy.com/project/badge/Grade/1a21d48d466644cdbcb57a1889abea5b)](https://www.codacy.com/app/shadowsocks/shadowsocks-android?utm_source=github.com&utm_medium=referral&utm_content=shadowsocks/shadowsocks-android&utm_campaign=Badge_Grade) [![Codacy Badge](https://api.codacy.com/project/badge/Grade/1a21d48d466644cdbcb57a1889abea5b)](https://www.codacy.com/app/shadowsocks/shadowsocks-android?utm_source=github.com&utm_medium=referral&utm_content=shadowsocks/shadowsocks-android&utm_campaign=Badge_Grade)
...@@ -13,9 +13,8 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Kotlin. ...@@ -13,9 +13,8 @@ A [shadowsocks](http://shadowsocks.org) client for Android, written in Kotlin.
### PREREQUISITES ### PREREQUISITES
* JDK 1.8 * JDK 1.8
* Go 1.4+ * Go 1.11+
* Android SDK * Android SDK
- Build Tools 27+
- Android NDK r16+ - Android NDK r16+
### BUILD ### BUILD
...@@ -26,17 +25,18 @@ but probably painful. Further contributions regarding building on Windows are al ...@@ -26,17 +25,18 @@ but probably painful. Further contributions regarding building on Windows are al
* Set environment variable `ANDROID_HOME` to `/path/to/android-sdk` * Set environment variable `ANDROID_HOME` to `/path/to/android-sdk`
* (optional) Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk` (default: `$ANDROID_HOME/ndk-bundle`) * (optional) Set environment variable `ANDROID_NDK_HOME` to `/path/to/android-ndk` (default: `$ANDROID_HOME/ndk-bundle`)
* Set environment variable `GOROOT_BOOTSTRAP` to `/path/to/go`
* Clone the repo using `git clone --recurse-submodules <repo>` or update submodules using `git submodule update --init --recursive` * Clone the repo using `git clone --recurse-submodules <repo>` or update submodules using `git submodule update --init --recursive`
* Build it using Android Studio or gradle script * Build it using Android Studio or gradle script
### TRANSLATE ### BUILD WITH DOCKER
Translators can go to [POEditor](https://poeditor.com/join/project/u5VHO9vhSf) to help translate shadowsocks-android. Guidelines: ```bash
mkdir build
sudo chown 3434:3434 build
docker run --rm -v ${PWD}/build:/build shadowsocks/shadowsocks-android:circleci bash -c "cd /build; git clone https://github.com/shadowsocks/shadowsocks-android; cd shadowsocks-android; git submodule update --init --recursive; ./gradlew assembleDebug"
```
* It's okay to leave some strings untranslated if you think it should use the same string as English (US). ### [TRANSLATE](https://discourse.shadowsocks.org/t/poeditor-translation-main-thread/30)
* `faq_url` should not be changed. If you'd like to translate FAQ, submit a pull request with the translated [`faq.md`](https://github.com/shadowsocks/shadowsocks-android/blob/master/.github/faq.md) (it should be named properly, e.g. `.github/faq.zh-CN.md`). Administrators will take care of the rest.
* Do not add/edit/remove comments.
## OPEN SOURCE LICENSES ## OPEN SOURCE LICENSES
......
...@@ -4,27 +4,32 @@ apply plugin: 'com.github.ben-manes.versions' ...@@ -4,27 +4,32 @@ apply plugin: 'com.github.ben-manes.versions'
buildscript { buildscript {
ext { ext {
kotlinVersion = '1.2.31' kotlinVersion = '1.3.11'
minSdkVersion = 21 minSdkVersion = 21
sdkVersion = 27 sdkVersion = 28
buildToolsVersion = '27.0.3' compileSdkVersion = 28
supportLibraryVersion = '27.1.0' buildToolsVersion = '28.0.3'
takisoftFixVersion = '27.1.0.0' roomVersion = '2.0.0'
playServicesVersion = '12.0.1' workVersion = '1.0.0-beta01'
preferencexVersion = '1.0.0'
junitVersion = '4.12' junitVersion = '4.12'
androidTestVersion = '1.0.1' androidTestVersion = '1.1.1'
androidEspressoVersion = '3.0.1' androidEspressoVersion = '3.1.1'
} }
repositories { repositories {
google() google()
jcenter() jcenter()
maven {
url 'https://maven.fabric.io/public'
}
} }
dependencies { dependencies {
classpath 'com.android.tools.build:gradle:3.1.0' classpath 'com.android.tools.build:gradle:3.2.1'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.17.0' classpath 'com.github.ben-manes:gradle-versions-plugin:0.20.0'
classpath 'com.google.gms:google-services:3.2.1' classpath 'com.google.gms:google-services:4.2.0'
classpath 'io.fabric.tools:gradle:1.27.0'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion" classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
} }
} }
......
import org.apache.tools.ant.taskdefs.condition.Os import org.apache.tools.ant.taskdefs.condition.Os
apply plugin: 'com.android.library' apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-kapt'
android { android {
buildToolsVersion rootProject.buildToolsVersion buildToolsVersion rootProject.buildToolsVersion
compileSdkVersion rootProject.sdkVersion compileSdkVersion rootProject.compileSdkVersion
defaultConfig { defaultConfig {
minSdkVersion rootProject.minSdkVersion minSdkVersion rootProject.minSdkVersion
...@@ -12,7 +14,7 @@ android { ...@@ -12,7 +14,7 @@ android {
versionCode 1 versionCode 1
versionName "1.0.0" versionName "1.0.0"
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner" testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
externalNativeBuild { externalNativeBuild {
ndkBuild { ndkBuild {
...@@ -20,6 +22,11 @@ android { ...@@ -20,6 +22,11 @@ android {
arguments "-j${Runtime.runtime.availableProcessors()}" arguments "-j${Runtime.runtime.availableProcessors()}"
} }
} }
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
} }
buildTypes { buildTypes {
...@@ -56,15 +63,26 @@ task goClean(type: Exec) { ...@@ -56,15 +63,26 @@ task goClean(type: Exec) {
} }
tasks.whenTaskAdded { task -> tasks.whenTaskAdded { task ->
if ((task.name == 'generateJsonModelDebug' || if ((task.name == 'generateJsonModelDebug' || task.name == 'generateJsonModelRelease')) {
task.name == 'generateJsonModelRelease')) {
task.dependsOn(goBuild) task.dependsOn(goBuild)
} }
} }
dependencies { dependencies {
api project(':plugin') api project(':plugin')
api "android.arch.work:work-runtime-ktx:$workVersion"
api "androidx.preference:preference:1.0.0"
api "androidx.room:room-runtime:$roomVersion"
api 'com.crashlytics.sdk.android:crashlytics:2.9.7'
api 'com.google.firebase:firebase-config:16.1.2'
api 'com.google.firebase:firebase-core:16.0.6'
api 'com.squareup.okhttp3:okhttp:3.12.0'
api "com.takisoft.preferencex:preferencex:$preferencexVersion"
api 'eu.chainfire:libsuperuser:1.0.0.201811281328'
kapt "androidx.room:room-compiler:$roomVersion"
testImplementation "androidx.room:room-testing:$roomVersion"
testImplementation "junit:junit:$junitVersion" testImplementation "junit:junit:$junitVersion"
androidTestImplementation "com.android.support.test:runner:$androidTestVersion" androidTestImplementation "android.arch.work:work-testing:$workVersion"
androidTestImplementation "com.android.support.test.espresso:espresso-core:$androidEspressoVersion" androidTestImplementation "androidx.test:runner:$androidTestVersion"
androidTestImplementation "androidx.test.espresso:espresso-core:$androidEspressoVersion"
} }
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<lint> <lint>
<issue id="InvalidPackage">
<ignore regexp="ormlite-core" />
</issue>
<issue id="ImpliedQuantity" severity="warning" /> <issue id="ImpliedQuantity" severity="warning" />
<issue id="ExtraTranslation" severity="warning" /> <issue id="ExtraTranslation" severity="warning" />
<issue id="MissingDefaultResource" severity="warning" />
<issue id="MissingTranslation" severity="informational" /> <issue id="MissingTranslation" severity="informational" />
</lint> </lint>
{
"formatVersion": 1,
"database": {
"version": 26,
"identityHash": "a69accf94393d71abdb60873d4ea74a2",
"entities": [
{
"tableName": "Profile",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `method` TEXT NOT NULL, `route` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `proxyApps` INTEGER NOT NULL, `bypass` INTEGER NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `individual` TEXT NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `plugin` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "host",
"columnName": "host",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remotePort",
"columnName": "remotePort",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "password",
"columnName": "password",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "method",
"columnName": "method",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "route",
"columnName": "route",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "remoteDns",
"columnName": "remoteDns",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "proxyApps",
"columnName": "proxyApps",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "bypass",
"columnName": "bypass",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "udpdns",
"columnName": "udpdns",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "ipv6",
"columnName": "ipv6",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "individual",
"columnName": "individual",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "tx",
"columnName": "tx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "rx",
"columnName": "rx",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "userOrder",
"columnName": "userOrder",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "plugin",
"columnName": "plugin",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"a69accf94393d71abdb60873d4ea74a2\")"
]
}
}
\ No newline at end of file
{
"formatVersion": 1,
"database": {
"version": 3,
"identityHash": "f1aab1fb633378621635c344dbc8ac7b",
"entities": [
{
"tableName": "KeyValuePair",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"fields": [
{
"fieldPath": "key",
"columnName": "key",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "valueType",
"columnName": "valueType",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "value",
"columnName": "value",
"affinity": "BLOB",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"key"
],
"autoGenerate": false
},
"indices": [],
"foreignKeys": []
}
],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, \"f1aab1fb633378621635c344dbc8ac7b\")"
]
}
}
\ No newline at end of file
package com.github.shadowsocks;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumented test, which will execute on an Android device.
*
* @see <a href="http://d.android.com/tools/testing">Testing documentation</a>
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.github.shadowsocks.core.test", appContext.getPackageName());
}
}
...@@ -34,6 +34,6 @@ class AclTest { ...@@ -34,6 +34,6 @@ class AclTest {
@Test @Test
fun parse() { fun parse() {
Assert.assertEquals(INPUT1, Acl().fromReader(INPUT1.reader()).toString()); Assert.assertEquals(INPUT1, Acl().fromReader(INPUT1.reader()).toString())
} }
} }
...@@ -20,17 +20,17 @@ ...@@ -20,17 +20,17 @@
package com.github.shadowsocks.database package com.github.shadowsocks.database
import android.net.Uri import androidx.core.net.toUri
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
class ProfileTest { class ProfileTest {
@Test @Test
fun parsing() { fun parsing() {
val results = Profile.findAll("garble ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4#example-server garble") val results = Profile.findAllUrls("garble ss://YmYtY2ZiOnRlc3RAMTkyLjE2OC4xMDAuMTo4ODg4#example-server garble")
.toList() .toList()
Assert.assertEquals(1, results.size) Assert.assertEquals(1, results.size)
Assert.assertEquals(Uri.parse("ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888#example-server"), Assert.assertEquals("ss://YmYtY2ZiOnRlc3Q@192.168.100.1:8888#example-server".toUri(),
results.single().toUri()) results.single().toUri())
} }
} }
package com.github.shadowsocks.utils package com.github.shadowsocks.utils
import java.net.InetAddress
import org.junit.Assert import org.junit.Assert
import org.junit.Test import org.junit.Test
import java.net.InetAddress
class SubnetTest { class SubnetTest {
@Test @Test
......
<manifest xmlns:android="http://schemas.android.com/apk/res/android" <manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.github.shadowsocks.core"/> xmlns:tools="http://schemas.android.com/tools"
package="com.github.shadowsocks.core"
android:installLocation="internalOnly">
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE"/>
<uses-permission android:name="android.permission.CHANGE_NETWORK_STATE"/>
<uses-permission android:name="android.permission.FOREGROUND_SERVICE"/>
<uses-permission android:name="android.permission.INTERNET"/>
<uses-permission android:name="android.permission.RECEIVE_BOOT_COMPLETED"/>
<uses-permission android:name="android.permission.WAKE_LOCK"/>
<uses-feature android:name="android.software.leanback"
android:required="false"/>
<application
android:icon="@mipmap/ic_launcher"
android:allowBackup="true"
android:backupAgent="com.github.shadowsocks.ConfigBackupHelper"
android:fullBackupContent="@xml/backup_descriptor"
android:fullBackupOnly="true"
android:label="@string/app_name"
android:supportsRtl="true"
android:networkSecurityConfig="@xml/network_security_config"
android:banner="@mipmap/banner">
<meta-data android:name="android.webkit.WebView.EnableSafeBrowsing"
android:value="true" />
<meta-data android:name="com.google.android.backup.api_key"
android:value="AEdPqrEAAAAI_zVxZthz2HDuz9toTvkYvL0L5GA-OjeUIfBeXg"/>
<service
android:name="com.github.shadowsocks.bg.VpnService"
android:process=":bg"
android:directBootAware="true"
android:label="@string/app_name"
android:permission="android.permission.BIND_VPN_SERVICE"
android:exported="false">
<intent-filter>
<action android:name="android.net.VpnService"/>
</intent-filter>
</service>
<service
android:name="com.github.shadowsocks.bg.TransproxyService"
android:process=":bg"
android:directBootAware="true"
android:exported="false">
</service>
<service
android:name="com.github.shadowsocks.bg.ProxyService"
android:process=":bg"
android:directBootAware="true"
android:exported="false">
</service>
<activity
android:name="com.github.shadowsocks.VpnRequestActivity"
android:theme="@style/Theme.AppCompat.Translucent"
android:excludeFromRecents="true"
android:taskAffinity=""
android:launchMode="singleTask"/>
<receiver android:name="com.github.shadowsocks.BootReceiver"
android:process=":bg"
android:directBootAware="true"
android:enabled="false">
<intent-filter>
<action android:name="android.intent.action.BOOT_COMPLETED"/>
<action android:name="android.intent.action.LOCKED_BOOT_COMPLETED" />
</intent-filter>
</receiver>
<provider android:name="com.crashlytics.android.CrashlyticsInitProvider"
tools:node="remove"/>
<!-- https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/work/workmanager/src/main/AndroidManifest.xml -->
<provider android:name="androidx.work.impl.WorkManagerInitializer"
tools:node="remove"/>
<service android:name="androidx.work.impl.background.systemalarm.SystemAlarmService"
android:process=":bg"
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<service android:name="androidx.work.impl.background.systemjob.SystemJobService"
android:process=":bg"
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.utils.ForceStopRunnable$BroadcastReceiver"
android:process=":bg"
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryChargingProxy"
android:process=":bg"
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$BatteryNotLowProxy"
android:process=":bg"
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$StorageNotLowProxy"
android:process=":bg"
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxy$NetworkStateProxy"
android:process=":bg"
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.RescheduleReceiver"
android:process=":bg"
android:directBootAware="true"
tools:replace="android:directBootAware"/>
<receiver android:name="androidx.work.impl.background.systemalarm.ConstraintProxyUpdateReceiver"
android:process=":bg"
android:directBootAware="true"
tools:replace="android:directBootAware"/>
</application>
</manifest>
...@@ -2,7 +2,7 @@ package com.github.shadowsocks.aidl; ...@@ -2,7 +2,7 @@ package com.github.shadowsocks.aidl;
interface IShadowsocksServiceCallback { interface IShadowsocksServiceCallback {
oneway void stateChanged(int state, String profileName, String msg); oneway void stateChanged(int state, String profileName, String msg);
oneway void trafficUpdated(int profileId, long txRate, long rxRate, long txTotal, long rxTotal); oneway void trafficUpdated(long profileId, long txRate, long rxRate, long txTotal, long rxTotal);
// Traffic data has persisted to database, listener should refetch their data from database // Traffic data has persisted to database, listener should refetch their data from database
oneway void trafficPersisted(int profileId); oneway void trafficPersisted(long profileId);
} }
...@@ -48,6 +48,14 @@ ...@@ -48,6 +48,14 @@
1.192.0.0/13 1.192.0.0/13
1.202.0.0/15 1.202.0.0/15
1.204.0.0/14 1.204.0.0/14
8.128.0.0/10
8.208.32.0/19
8.208.64.0/18
8.208.128.0/17
8.209.0.0/16
8.210.0.0/15
8.212.0.0/14
8.216.0.0/13
14.0.0.0/21 14.0.0.0/21
14.0.12.0/22 14.0.12.0/22
14.1.0.0/22 14.1.0.0/22
...@@ -64,7 +72,7 @@ ...@@ -64,7 +72,7 @@
14.130.0.0/15 14.130.0.0/15
14.134.0.0/15 14.134.0.0/15
14.144.0.0/12 14.144.0.0/12
14.192.56.0/21 14.192.60.0/22
14.192.76.0/22 14.192.76.0/22
14.196.0.0/15 14.196.0.0/15
14.204.0.0/15 14.204.0.0/15
...@@ -154,6 +162,7 @@ ...@@ -154,6 +162,7 @@
39.96.0.0/13 39.96.0.0/13
39.104.0.0/14 39.104.0.0/14
39.108.0.0/16 39.108.0.0/16
39.109.120.0/23
39.128.0.0/10 39.128.0.0/10
40.0.176.0/20 40.0.176.0/20
40.0.247.0/24 40.0.247.0/24
...@@ -269,7 +278,6 @@ ...@@ -269,7 +278,6 @@
43.225.140.0/22 43.225.140.0/22
43.225.172.0/22 43.225.172.0/22
43.225.180.0/22 43.225.180.0/22
43.225.184.0/22
43.225.208.0/22 43.225.208.0/22
43.225.216.0/21 43.225.216.0/21
43.225.224.0/20 43.225.224.0/20
...@@ -548,7 +556,6 @@ ...@@ -548,7 +556,6 @@
45.113.52.0/22 45.113.52.0/22
45.113.56.0/22 45.113.56.0/22
45.113.72.0/22 45.113.72.0/22
45.113.108.0/22
45.113.144.0/21 45.113.144.0/21
45.113.168.0/22 45.113.168.0/22
45.113.176.0/22 45.113.176.0/22
...@@ -687,7 +694,6 @@ ...@@ -687,7 +694,6 @@
45.126.212.0/22 45.126.212.0/22
45.126.220.0/22 45.126.220.0/22
45.127.8.0/21 45.127.8.0/21
45.127.36.0/22
45.127.96.0/22 45.127.96.0/22
45.127.116.0/22 45.127.116.0/22
45.127.124.0/22 45.127.124.0/22
...@@ -743,6 +749,7 @@ ...@@ -743,6 +749,7 @@
45.251.160.0/19 45.251.160.0/19
45.251.192.0/19 45.251.192.0/19
45.251.224.0/22 45.251.224.0/22
45.251.240.0/22
45.252.0.0/19 45.252.0.0/19
45.252.32.0/20 45.252.32.0/20
45.252.48.0/22 45.252.48.0/22
...@@ -788,10 +795,10 @@ ...@@ -788,10 +795,10 @@
45.255.224.0/20 45.255.224.0/20
45.255.240.0/21 45.255.240.0/21
45.255.248.0/22 45.255.248.0/22
46.61.179.170/31
46.248.24.0/23 46.248.24.0/23
47.92.0.0/14 47.92.0.0/14
47.96.0.0/11 47.96.0.0/11
47.252.128.0/17
49.4.0.0/14 49.4.0.0/14
49.51.0.0/16 49.51.0.0/16
49.52.0.0/14 49.52.0.0/14
...@@ -811,6 +818,8 @@ ...@@ -811,6 +818,8 @@
49.246.224.0/19 49.246.224.0/19
52.80.0.0/14 52.80.0.0/14
52.94.249.0/27 52.94.249.0/27
52.95.216.104/30
52.130.0.0/15
54.222.0.0/15 54.222.0.0/15
54.231.208.0/20 54.231.208.0/20
54.240.224.0/24 54.240.224.0/24
...@@ -879,6 +888,7 @@ ...@@ -879,6 +888,7 @@
61.4.80.0/20 61.4.80.0/20
61.4.176.0/20 61.4.176.0/20
61.8.160.0/20 61.8.160.0/20
61.14.4.0/22
61.14.212.0/22 61.14.212.0/22
61.14.216.0/21 61.14.216.0/21
61.14.240.0/21 61.14.240.0/21
...@@ -890,14 +900,26 @@ ...@@ -890,14 +900,26 @@
61.48.0.0/13 61.48.0.0/13
61.87.192.0/18 61.87.192.0/18
61.128.0.0/10 61.128.0.0/10
61.213.145.106/32
61.232.0.0/14 61.232.0.0/14
61.236.0.0/15 61.236.0.0/15
61.240.0.0/14 61.240.0.0/14
62.234.0.0/16 62.234.0.0/16
64.85.27.0/24 64.85.27.0/24
65.55.60.184/30
68.79.0.0/18
69.230.192.0/18
69.231.128.0/18
69.234.192.0/18
69.235.128.0/18
71.131.192.0/18
71.132.0.0/18
71.136.64.0/18
71.137.0.0/18
72.163.248.0/22 72.163.248.0/22
81.68.0.0/14
82.156.0.0/15
87.254.207.0/24 87.254.207.0/24
91.234.36.0/24
93.183.14.0/24 93.183.14.0/24
93.183.18.0/24 93.183.18.0/24
94.191.0.0/17 94.191.0.0/17
...@@ -1052,7 +1074,6 @@ ...@@ -1052,7 +1074,6 @@
103.14.32.0/22 103.14.32.0/22
103.14.84.0/22 103.14.84.0/22
103.14.100.0/22 103.14.100.0/22
103.14.112.0/22
103.14.132.0/22 103.14.132.0/22
103.14.136.0/22 103.14.136.0/22
103.14.156.0/22 103.14.156.0/22
...@@ -1418,7 +1439,6 @@ ...@@ -1418,7 +1439,6 @@
103.57.136.0/22 103.57.136.0/22
103.57.196.0/22 103.57.196.0/22
103.58.24.0/22 103.58.24.0/22
103.58.182.0/23
103.59.76.0/22 103.59.76.0/22
103.59.100.0/22 103.59.100.0/22
103.59.112.0/20 103.59.112.0/20
...@@ -1621,7 +1641,6 @@ ...@@ -1621,7 +1641,6 @@
103.80.184.0/22 103.80.184.0/22
103.80.192.0/22 103.80.192.0/22
103.80.200.0/22 103.80.200.0/22
103.80.212.0/22
103.80.232.0/22 103.80.232.0/22
103.81.4.0/22 103.81.4.0/22
103.81.8.0/22 103.81.8.0/22
...@@ -1702,7 +1721,6 @@ ...@@ -1702,7 +1721,6 @@
103.88.64.0/22 103.88.64.0/22
103.88.72.0/22 103.88.72.0/22
103.88.96.0/21 103.88.96.0/21
103.88.139.0/24
103.88.152.0/23 103.88.152.0/23
103.88.164.0/22 103.88.164.0/22
103.88.176.0/22 103.88.176.0/22
...@@ -1868,6 +1886,7 @@ ...@@ -1868,6 +1886,7 @@
103.101.184.0/22 103.101.184.0/22
103.102.76.0/22 103.102.76.0/22
103.102.80.0/22 103.102.80.0/22
103.102.163.0/24
103.102.168.0/21 103.102.168.0/21
103.102.180.0/22 103.102.180.0/22
103.102.184.0/21 103.102.184.0/21
...@@ -1919,6 +1938,145 @@ ...@@ -1919,6 +1938,145 @@
103.106.196.0/22 103.106.196.0/22
103.106.202.0/23 103.106.202.0/23
103.106.212.0/22 103.106.212.0/22
103.106.244.0/22
103.106.252.0/22
103.107.0.0/22
103.107.8.0/24
103.107.28.0/22
103.107.32.0/22
103.107.44.0/22
103.107.72.0/22
103.107.108.0/22
103.107.164.0/22
103.107.168.0/22
103.107.188.0/22
103.107.192.0/22
103.107.208.0/20
103.108.52.0/22
103.108.64.0/22
103.108.160.0/21
103.108.184.0/23
103.108.188.0/23
103.108.192.0/21
103.108.208.0/21
103.108.224.0/22
103.108.244.0/22
103.108.251.0/24
103.109.20.0/22
103.109.48.0/22
103.109.88.0/22
103.109.106.0/23
103.109.248.0/22
103.110.32.0/22
103.110.80.0/23
103.110.92.0/22
103.110.100.0/22
103.110.116.0/22
103.110.127.0/24
103.110.128.0/23
103.110.131.0/24
103.110.132.0/22
103.110.136.0/22
103.110.152.0/21
103.110.188.0/22
103.110.204.0/22
103.111.38.0/23
103.111.64.0/22
103.111.172.0/22
103.111.252.0/22
103.112.28.0/22
103.112.68.0/22
103.112.72.0/22
103.112.88.0/21
103.112.96.0/22
103.112.108.0/22
103.112.112.0/21
103.112.140.0/22
103.112.172.0/22
103.112.184.0/22
103.112.208.0/22
103.113.4.0/22
103.113.92.0/22
103.113.144.0/22
103.113.220.0/22
103.113.232.0/21
103.114.4.0/22
103.114.28.0/22
103.114.68.0/22
103.114.72.0/22
103.114.100.0/22
103.114.132.0/22
103.114.148.0/22
103.114.156.0/22
103.114.176.0/22
103.114.212.0/22
103.114.236.0/22
103.114.240.0/22
103.115.16.0/22
103.115.40.0/21
103.115.48.0/20
103.115.64.0/21
103.115.92.0/22
103.115.120.0/22
103.115.148.0/22
103.115.204.0/23
103.115.248.0/22
103.116.20.0/22
103.116.40.0/22
103.116.64.0/22
103.116.72.0/21
103.116.92.0/22
103.116.120.0/22
103.116.128.0/22
103.116.132.0/23
103.116.148.0/22
103.116.184.0/22
103.116.206.0/23
103.116.220.0/22
103.116.224.0/21
103.117.16.0/22
103.117.72.0/22
103.117.88.0/22
103.117.132.0/22
103.117.136.0/22
103.117.188.0/22
103.117.220.0/22
103.118.19.0/24
103.118.36.0/22
103.118.52.0/22
103.118.56.0/21
103.118.64.0/21
103.118.72.0/22
103.118.88.0/22
103.118.173.0/24
103.118.192.0/19
103.118.240.0/20
103.119.0.0/22
103.119.12.0/22
103.119.16.0/22
103.119.28.0/22
103.119.44.0/22
103.119.104.0/22
103.119.115.0/24
103.119.156.0/22
103.119.180.0/22
103.119.200.0/22
103.119.224.0/22
103.120.52.0/22
103.120.72.0/22
103.120.76.0/24
103.120.88.0/22
103.120.96.0/21
103.120.140.0/22
103.120.196.0/22
103.120.224.0/22
103.121.52.0/22
103.121.92.0/22
103.121.160.0/21
103.121.250.0/24
103.121.252.0/22
103.129.52.0/22
103.129.148.0/22
103.192.0.0/19 103.192.0.0/19
103.192.48.0/21 103.192.48.0/21
103.192.56.0/22 103.192.56.0/22
...@@ -2091,7 +2249,7 @@ ...@@ -2091,7 +2249,7 @@
103.211.168.0/22 103.211.168.0/22
103.211.192.0/22 103.211.192.0/22
103.211.220.0/22 103.211.220.0/22
103.211.224.0/21 103.211.224.0/22
103.211.248.0/22 103.211.248.0/22
103.212.0.0/20 103.212.0.0/20
103.212.32.0/22 103.212.32.0/22
...@@ -2120,7 +2278,6 @@ ...@@ -2120,7 +2278,6 @@
103.214.84.0/22 103.214.84.0/22
103.214.168.0/22 103.214.168.0/22
103.214.212.0/22 103.214.212.0/22
103.214.224.0/22
103.214.240.0/21 103.214.240.0/21
103.215.28.0/22 103.215.28.0/22
103.215.32.0/21 103.215.32.0/21
...@@ -2207,7 +2364,6 @@ ...@@ -2207,7 +2364,6 @@
103.223.188.0/22 103.223.188.0/22
103.223.192.0/18 103.223.192.0/18
103.224.0.0/22 103.224.0.0/22
103.224.16.0/22
103.224.40.0/21 103.224.40.0/21
103.224.60.0/22 103.224.60.0/22
103.224.80.0/22 103.224.80.0/22
...@@ -2491,8 +2647,6 @@ ...@@ -2491,8 +2647,6 @@
106.108.0.0/14 106.108.0.0/14
106.112.0.0/12 106.112.0.0/12
106.224.0.0/12 106.224.0.0/12
107.153.91.0/24
107.153.92.0/23
109.71.4.0/24 109.71.4.0/24
109.244.0.0/16 109.244.0.0/16
110.6.0.0/15 110.6.0.0/15
...@@ -2615,6 +2769,7 @@ ...@@ -2615,6 +2769,7 @@
113.136.0.0/13 113.136.0.0/13
113.194.0.0/15 113.194.0.0/15
113.197.100.0/22 113.197.100.0/22
113.197.104.0/22
113.200.0.0/15 113.200.0.0/15
113.202.0.0/16 113.202.0.0/16
113.204.0.0/14 113.204.0.0/14
...@@ -2730,7 +2885,7 @@ ...@@ -2730,7 +2885,7 @@
116.207.0.0/16 116.207.0.0/16
116.208.0.0/14 116.208.0.0/14
116.212.160.0/20 116.212.160.0/20
116.213.40.0/21 116.213.44.0/22
116.213.64.0/18 116.213.64.0/18
116.213.128.0/17 116.213.128.0/17
116.214.32.0/19 116.214.32.0/19
...@@ -2774,7 +2929,6 @@ ...@@ -2774,7 +2929,6 @@
117.103.72.0/21 117.103.72.0/21
117.103.128.0/20 117.103.128.0/20
117.104.168.0/21 117.104.168.0/21
117.104.188.0/24
117.106.0.0/15 117.106.0.0/15
117.112.0.0/13 117.112.0.0/13
117.120.64.0/18 117.120.64.0/18
...@@ -3076,7 +3230,6 @@ ...@@ -3076,7 +3230,6 @@
124.150.137.0/24 124.150.137.0/24
124.151.0.0/16 124.151.0.0/16
124.152.0.0/16 124.152.0.0/16
124.156.0.0/16
124.160.0.0/13 124.160.0.0/13
124.172.0.0/14 124.172.0.0/14
124.192.0.0/15 124.192.0.0/15
...@@ -3116,7 +3269,11 @@ ...@@ -3116,7 +3269,11 @@
129.204.0.0/16 129.204.0.0/16
129.211.0.0/16 129.211.0.0/16
129.223.254.0/24 129.223.254.0/24
130.214.218.0/23
131.228.96.0/24 131.228.96.0/24
131.253.12.0/29
131.253.12.80/28
131.253.12.240/29
132.232.0.0/16 132.232.0.0/16
132.237.134.0/24 132.237.134.0/24
132.237.150.0/24 132.237.150.0/24
...@@ -3157,6 +3314,7 @@ ...@@ -3157,6 +3314,7 @@
140.75.0.0/16 140.75.0.0/16
140.101.208.0/24 140.101.208.0/24
140.143.0.0/16 140.143.0.0/16
140.179.0.0/16
140.205.0.0/16 140.205.0.0/16
140.206.0.0/15 140.206.0.0/15
140.210.0.0/16 140.210.0.0/16
...@@ -3176,11 +3334,9 @@ ...@@ -3176,11 +3334,9 @@
144.12.0.0/16 144.12.0.0/16
144.36.146.0/23 144.36.146.0/23
144.48.8.0/21 144.48.8.0/21
144.48.56.0/22
144.48.64.0/22 144.48.64.0/22
144.48.88.0/22 144.48.88.0/22
144.48.156.0/22 144.48.156.0/22
144.48.172.0/22
144.48.180.0/22 144.48.180.0/22
144.48.184.0/22 144.48.184.0/22
144.48.204.0/22 144.48.204.0/22
...@@ -3195,7 +3351,7 @@ ...@@ -3195,7 +3351,7 @@
146.56.192.0/18 146.56.192.0/18
146.196.56.0/22 146.196.56.0/22
146.196.68.0/22 146.196.68.0/22
146.196.72.0/21 146.196.72.0/22
146.196.92.0/22 146.196.92.0/22
146.196.112.0/21 146.196.112.0/21
146.196.124.0/22 146.196.124.0/22
...@@ -3203,9 +3359,10 @@ ...@@ -3203,9 +3359,10 @@
146.222.79.0/24 146.222.79.0/24
146.222.81.0/24 146.222.81.0/24
146.222.94.0/24 146.222.94.0/24
147.243.13.32/27
147.243.13.64/27
148.70.0.0/16 148.70.0.0/16
150.0.0.0/16 150.0.0.0/16
150.109.0.0/16
150.115.0.0/16 150.115.0.0/16
150.121.0.0/16 150.121.0.0/16
150.122.0.0/16 150.122.0.0/16
...@@ -3214,6 +3371,7 @@ ...@@ -3214,6 +3371,7 @@
150.129.216.0/22 150.129.216.0/22
150.129.252.0/22 150.129.252.0/22
150.138.0.0/15 150.138.0.0/15
150.158.0.0/16
150.223.0.0/16 150.223.0.0/16
150.242.0.0/21 150.242.0.0/21
150.242.8.0/22 150.242.8.0/22
...@@ -3237,6 +3395,10 @@ ...@@ -3237,6 +3395,10 @@
150.242.240.0/21 150.242.240.0/21
150.242.248.0/22 150.242.248.0/22
150.255.0.0/16 150.255.0.0/16
152.32.136.0/21
152.32.144.0/20
152.32.160.0/19
152.32.192.0/18
152.104.128.0/17 152.104.128.0/17
152.136.0.0/16 152.136.0.0/16
153.0.0.0/16 153.0.0.0/16
...@@ -3274,6 +3436,7 @@ ...@@ -3274,6 +3436,7 @@
157.148.0.0/16 157.148.0.0/16
157.156.0.0/16 157.156.0.0/16
157.255.0.0/16 157.255.0.0/16
159.75.0.0/16
159.153.120.0/22 159.153.120.0/22
159.226.0.0/16 159.226.0.0/16
160.19.208.0/21 160.19.208.0/21
...@@ -3295,8 +3458,9 @@ ...@@ -3295,8 +3458,9 @@
161.163.176.0/24 161.163.176.0/24
161.163.178.0/23 161.163.178.0/23
161.163.180.0/22 161.163.180.0/22
161.189.0.0/16
161.207.0.0/16 161.207.0.0/16
162.62.0.0/16 162.14.0.0/16
162.105.0.0/16 162.105.0.0/16
163.0.0.0/16 163.0.0.0/16
163.47.4.0/22 163.47.4.0/22
...@@ -3332,7 +3496,6 @@ ...@@ -3332,7 +3496,6 @@
168.159.158.0/24 168.159.158.0/24
168.160.0.0/16 168.160.0.0/16
168.230.0.0/24 168.230.0.0/24
170.106.0.0/16
170.179.0.0/16 170.179.0.0/16
170.225.224.0/23 170.225.224.0/23
170.252.152.0/21 170.252.152.0/21
...@@ -3368,6 +3531,7 @@ ...@@ -3368,6 +3531,7 @@
175.176.156.0/22 175.176.156.0/22
175.176.176.0/22 175.176.176.0/22
175.176.188.0/22 175.176.188.0/22
175.176.192.0/22
175.178.0.0/16 175.178.0.0/16
175.184.128.0/18 175.184.128.0/18
175.185.0.0/16 175.185.0.0/16
...@@ -3466,6 +3630,7 @@ ...@@ -3466,6 +3630,7 @@
183.184.0.0/13 183.184.0.0/13
183.192.0.0/10 183.192.0.0/10
185.109.236.0/24 185.109.236.0/24
188.65.16.0/22
188.131.128.0/17 188.131.128.0/17
192.11.23.0/24 192.11.23.0/24
192.11.26.0/24 192.11.26.0/24
...@@ -3489,12 +3654,10 @@ ...@@ -3489,12 +3654,10 @@
192.140.208.0/21 192.140.208.0/21
192.144.128.0/17 192.144.128.0/17
192.163.11.0/24 192.163.11.0/24
192.200.24.0/24
192.232.97.0/24 192.232.97.0/24
193.20.64.0/22 193.20.64.0/22
193.112.0.0/16 193.112.0.0/16
194.138.202.0/23 194.138.202.0/23
195.78.82.0/23
198.175.100.0/22 198.175.100.0/22
198.208.17.0/24 198.208.17.0/24
199.7.72.0/24 199.7.72.0/24
...@@ -3796,7 +3959,11 @@ ...@@ -3796,7 +3959,11 @@
202.94.74.0/24 202.94.74.0/24
202.94.81.0/24 202.94.81.0/24
202.94.92.0/22 202.94.92.0/22
202.95.0.0/19 202.95.1.0/24
202.95.2.0/23
202.95.4.0/22
202.95.8.0/21
202.95.16.0/20
202.95.240.0/21 202.95.240.0/21
202.95.252.0/22 202.95.252.0/22
202.96.0.0/12 202.96.0.0/12
...@@ -3893,7 +4060,6 @@ ...@@ -3893,7 +4060,6 @@
202.164.96.0/19 202.164.96.0/19
202.165.96.0/21 202.165.96.0/21
202.165.104.0/22 202.165.104.0/22
202.165.110.0/23
202.165.176.0/20 202.165.176.0/20
202.165.208.0/20 202.165.208.0/20
202.165.239.0/24 202.165.239.0/24
...@@ -4887,7 +5053,7 @@ ...@@ -4887,7 +5053,7 @@
203.189.6.0/23 203.189.6.0/23
203.189.112.0/22 203.189.112.0/22
203.189.192.0/19 203.189.192.0/19
203.189.232.0/21 203.189.232.0/22
203.189.240.0/22 203.189.240.0/22
203.190.96.0/20 203.190.96.0/20
203.190.249.0/24 203.190.249.0/24
...@@ -5059,7 +5225,6 @@ ...@@ -5059,7 +5225,6 @@
223.116.0.0/15 223.116.0.0/15
223.120.0.0/13 223.120.0.0/13
223.128.0.0/15 223.128.0.0/15
223.130.8.0/22
223.144.0.0/12 223.144.0.0/12
223.160.0.0/14 223.160.0.0/14
223.166.0.0/15 223.166.0.0/15
......
...@@ -6,15 +6,14 @@ ...@@ -6,15 +6,14 @@
[bypass_all] [bypass_all]
[proxy_list] [proxy_list]
# Telegram IPs$ # Telegram IPs$
91.108.56.0/23
91.108.56.0/22
91.108.4.0/22 91.108.4.0/22
149.154.172.0/22 91.108.8.0/21
149.154.168.0/22 91.108.16.0/21
149.154.164.0/22 91.108.36.0/22
91.108.56.0/22
109.239.140.0/24 109.239.140.0/24
149.154.160.0/20
14.102.250.18 14.102.250.18
14.102.250.19 14.102.250.19
174.142.105.153 174.142.105.153
...@@ -561,9 +560,7 @@ ...@@ -561,9 +560,7 @@
(^|\.)blogs\.libraryinformationtechnology\.com$ (^|\.)blogs\.libraryinformationtechnology\.com$
(^|\.)blogs\.tampabay\.com$ (^|\.)blogs\.tampabay\.com$
(^|\.)blogs\.yahoo\.co\.jp$ (^|\.)blogs\.yahoo\.co\.jp$
(^|\.)blogspot\.com$ (^|\.)blogspot(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)blogspot\.hk$
(^|\.)blogspot\.jp$
(^|\.)blogtd\.net$ (^|\.)blogtd\.net$
(^|\.)blogtd\.org$ (^|\.)blogtd\.org$
(^|\.)bloodshed\.net$ (^|\.)bloodshed\.net$
...@@ -1802,61 +1799,8 @@ ...@@ -1802,61 +1799,8 @@
(^|\.)goodtv\.com\.tw$ (^|\.)goodtv\.com\.tw$
(^|\.)goodtv\.tv$ (^|\.)goodtv\.tv$
(^|\.)goofind\.com$ (^|\.)goofind\.com$
(^|\.)google\.ae$ (^|\.)google(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)google\.am$ (^|\.)googleapis(\.[a-zA-Z0-9]([a-zA-Z0-9-]{0,61}[a-zA-Z0-9])?){1,2}$
(^|\.)google\.as$
(^|\.)google\.at$
(^|\.)google\.az$
(^|\.)google\.ba$
(^|\.)google\.be$
(^|\.)google\.bg$
(^|\.)google\.ca$
(^|\.)google\.calstate\.edu$
(^|\.)google\.cd$
(^|\.)google\.ci$
(^|\.)google\.co\.id$
(^|\.)google\.co\.jp$
(^|\.)google\.co\.kr$
(^|\.)google\.co\.ma$
(^|\.)google\.co\.uk$
(^|\.)google\.com$
(^|\.)google\.de$
(^|\.)google\.dj$
(^|\.)google\.dk$
(^|\.)google\.es$
(^|\.)google\.fi$
(^|\.)google\.fm$
(^|\.)google\.fr$
(^|\.)google\.gg$
(^|\.)google\.gl$
(^|\.)google\.gr$
(^|\.)google\.ie$
(^|\.)google\.is$
(^|\.)google\.it$
(^|\.)google\.jo$
(^|\.)google\.kz$
(^|\.)google\.lv$
(^|\.)google\.mn$
(^|\.)google\.ms$
(^|\.)google\.nl$
(^|\.)google\.no$
(^|\.)google\.nu$
(^|\.)google\.ro$
(^|\.)google\.ru$
(^|\.)google\.rw$
(^|\.)google\.sc$
(^|\.)google\.sh$
(^|\.)google\.sk$
(^|\.)google\.sm$
(^|\.)google\.sn$
(^|\.)google\.tk$
(^|\.)google\.tm$
(^|\.)google\.to$
(^|\.)google\.tt$
(^|\.)google\.vu$
(^|\.)google\.ws$
(^|\.)googleapis\.cn$
(^|\.)googleapis\.com$
(^|\.)googleapps\.com$ (^|\.)googleapps\.com$
(^|\.)googlearth\.com$ (^|\.)googlearth\.com$
(^|\.)googleartproject\.com$ (^|\.)googleartproject\.com$
......
0.0.0.0/8
10.0.0.0/8
100.64.0.0/10
127.0.0.0/8
169.254.0.0/16
172.16.0.0/12
192.0.0.0/29
192.0.2.0/24
192.88.99.0/24
192.168.0.0/16
198.18.0.0/15
198.51.100.0/24
203.0.113.0/24
224.0.0.0/3
1.0.1.0/24 1.0.1.0/24
1.0.2.0/23 1.0.2.0/23
1.0.8.0/21 1.0.8.0/21
...@@ -59,6 +45,8 @@ ...@@ -59,6 +45,8 @@
1.192.0.0/13 1.192.0.0/13
1.202.0.0/15 1.202.0.0/15
1.204.0.0/14 1.204.0.0/14
8.128.0.0/10
8.208.0.0/12
14.0.0.0/21 14.0.0.0/21
14.0.12.0/22 14.0.12.0/22
14.1.0.0/22 14.1.0.0/22
...@@ -165,6 +153,7 @@ ...@@ -165,6 +153,7 @@
39.96.0.0/13 39.96.0.0/13
39.104.0.0/14 39.104.0.0/14
39.108.0.0/16 39.108.0.0/16
39.109.120.0/23
39.128.0.0/10 39.128.0.0/10
40.0.176.0/20 40.0.176.0/20
40.0.247.0/24 40.0.247.0/24
...@@ -280,7 +269,6 @@ ...@@ -280,7 +269,6 @@
43.225.140.0/22 43.225.140.0/22
43.225.172.0/22 43.225.172.0/22
43.225.180.0/22 43.225.180.0/22
43.225.184.0/22
43.225.208.0/22 43.225.208.0/22
43.225.216.0/21 43.225.216.0/21
43.225.224.0/20 43.225.224.0/20
...@@ -559,7 +547,6 @@ ...@@ -559,7 +547,6 @@
45.113.52.0/22 45.113.52.0/22
45.113.56.0/22 45.113.56.0/22
45.113.72.0/22 45.113.72.0/22
45.113.108.0/22
45.113.144.0/21 45.113.144.0/21
45.113.168.0/22 45.113.168.0/22
45.113.176.0/22 45.113.176.0/22
...@@ -698,7 +685,6 @@ ...@@ -698,7 +685,6 @@
45.126.212.0/22 45.126.212.0/22
45.126.220.0/22 45.126.220.0/22
45.127.8.0/21 45.127.8.0/21
45.127.36.0/22
45.127.96.0/22 45.127.96.0/22
45.127.116.0/22 45.127.116.0/22
45.127.124.0/22 45.127.124.0/22
...@@ -799,10 +785,10 @@ ...@@ -799,10 +785,10 @@
45.255.224.0/20 45.255.224.0/20
45.255.240.0/21 45.255.240.0/21
45.255.248.0/22 45.255.248.0/22
46.61.179.170/31
46.248.24.0/23 46.248.24.0/23
47.92.0.0/14 47.92.0.0/14
47.96.0.0/11 47.96.0.0/11
47.252.128.0/17
49.4.0.0/14 49.4.0.0/14
49.51.0.0/16 49.51.0.0/16
49.52.0.0/14 49.52.0.0/14
...@@ -822,6 +808,8 @@ ...@@ -822,6 +808,8 @@
49.246.224.0/19 49.246.224.0/19
52.80.0.0/14 52.80.0.0/14
52.94.249.0/27 52.94.249.0/27
52.95.216.104/30
52.130.0.0/15
54.222.0.0/15 54.222.0.0/15
54.231.208.0/20 54.231.208.0/20
54.240.224.0/24 54.240.224.0/24
...@@ -890,6 +878,7 @@ ...@@ -890,6 +878,7 @@
61.4.80.0/20 61.4.80.0/20
61.4.176.0/20 61.4.176.0/20
61.8.160.0/20 61.8.160.0/20
61.14.4.0/22
61.14.212.0/22 61.14.212.0/22
61.14.216.0/21 61.14.216.0/21
61.14.240.0/21 61.14.240.0/21
...@@ -901,14 +890,25 @@ ...@@ -901,14 +890,25 @@
61.48.0.0/13 61.48.0.0/13
61.87.192.0/18 61.87.192.0/18
61.128.0.0/10 61.128.0.0/10
61.213.145.106/32
61.232.0.0/14 61.232.0.0/14
61.236.0.0/15 61.236.0.0/15
61.240.0.0/14 61.240.0.0/14
62.234.0.0/16 62.234.0.0/16
64.85.27.0/24 64.85.27.0/24
68.79.0.0/18
69.230.192.0/18
69.231.128.0/18
69.234.192.0/18
69.235.128.0/18
71.131.192.0/18
71.132.0.0/18
71.136.64.0/18
71.137.0.0/18
72.163.248.0/22 72.163.248.0/22
81.68.0.0/14
82.156.0.0/15
87.254.207.0/24 87.254.207.0/24
91.234.36.0/24
93.183.14.0/24 93.183.14.0/24
93.183.18.0/24 93.183.18.0/24
94.191.0.0/17 94.191.0.0/17
...@@ -1063,7 +1063,6 @@ ...@@ -1063,7 +1063,6 @@
103.14.32.0/22 103.14.32.0/22
103.14.84.0/22 103.14.84.0/22
103.14.100.0/22 103.14.100.0/22
103.14.112.0/22
103.14.132.0/22 103.14.132.0/22
103.14.136.0/22 103.14.136.0/22
103.14.156.0/22 103.14.156.0/22
...@@ -1632,7 +1631,6 @@ ...@@ -1632,7 +1631,6 @@
103.80.184.0/22 103.80.184.0/22
103.80.192.0/22 103.80.192.0/22
103.80.200.0/22 103.80.200.0/22
103.80.212.0/22
103.80.232.0/22 103.80.232.0/22
103.81.4.0/22 103.81.4.0/22
103.81.8.0/22 103.81.8.0/22
...@@ -1713,7 +1711,6 @@ ...@@ -1713,7 +1711,6 @@
103.88.64.0/22 103.88.64.0/22
103.88.72.0/22 103.88.72.0/22
103.88.96.0/21 103.88.96.0/21
103.88.139.0/24
103.88.152.0/23 103.88.152.0/23
103.88.164.0/22 103.88.164.0/22
103.88.176.0/22 103.88.176.0/22
...@@ -1879,6 +1876,7 @@ ...@@ -1879,6 +1876,7 @@
103.101.184.0/22 103.101.184.0/22
103.102.76.0/22 103.102.76.0/22
103.102.80.0/22 103.102.80.0/22
103.102.163.0/24
103.102.168.0/21 103.102.168.0/21
103.102.180.0/22 103.102.180.0/22
103.102.184.0/21 103.102.184.0/21
...@@ -1930,6 +1928,128 @@ ...@@ -1930,6 +1928,128 @@
103.106.196.0/22 103.106.196.0/22
103.106.202.0/23 103.106.202.0/23
103.106.212.0/22 103.106.212.0/22
103.106.244.0/22
103.106.252.0/22
103.107.0.0/22
103.107.8.0/24
103.107.28.0/22
103.107.32.0/22
103.107.44.0/22
103.107.72.0/22
103.107.108.0/22
103.107.164.0/22
103.107.168.0/22
103.107.188.0/22
103.107.192.0/22
103.107.208.0/20
103.108.52.0/22
103.108.64.0/22
103.108.160.0/21
103.108.184.0/23
103.108.188.0/23
103.108.192.0/21
103.108.208.0/21
103.108.224.0/22
103.108.244.0/22
103.108.251.0/24
103.109.20.0/22
103.109.48.0/22
103.109.88.0/22
103.109.106.0/23
103.109.248.0/22
103.110.32.0/22
103.110.80.0/23
103.110.92.0/22
103.110.100.0/22
103.110.116.0/22
103.110.127.0/24
103.110.128.0/23
103.110.131.0/24
103.110.132.0/22
103.110.136.0/22
103.110.152.0/21
103.110.188.0/22
103.110.204.0/22
103.111.38.0/23
103.111.64.0/22
103.111.172.0/22
103.111.252.0/22
103.112.28.0/22
103.112.68.0/22
103.112.72.0/22
103.112.88.0/21
103.112.96.0/22
103.112.108.0/22
103.112.112.0/21
103.112.140.0/22
103.112.172.0/22
103.112.184.0/22
103.112.208.0/22
103.113.4.0/22
103.113.92.0/22
103.113.144.0/22
103.113.220.0/22
103.113.232.0/21
103.114.4.0/22
103.114.28.0/22
103.114.68.0/22
103.114.72.0/22
103.114.100.0/22
103.114.132.0/22
103.114.148.0/22
103.114.156.0/22
103.114.176.0/22
103.114.212.0/22
103.114.236.0/22
103.114.240.0/22
103.115.16.0/22
103.115.40.0/21
103.115.48.0/20
103.115.64.0/21
103.115.92.0/22
103.115.120.0/22
103.115.148.0/22
103.115.204.0/23
103.115.248.0/22
103.116.20.0/22
103.116.40.0/22
103.116.64.0/22
103.116.72.0/21
103.116.92.0/22
103.116.120.0/22
103.116.128.0/22
103.116.132.0/23
103.116.148.0/22
103.116.184.0/22
103.116.206.0/23
103.116.220.0/22
103.116.224.0/21
103.117.16.0/22
103.117.72.0/22
103.117.88.0/22
103.117.132.0/22
103.117.136.0/22
103.117.188.0/22
103.117.220.0/22
103.118.19.0/24
103.118.36.0/22
103.118.52.0/22
103.118.56.0/21
103.118.64.0/21
103.118.72.0/22
103.118.88.0/22
103.118.173.0/24
103.118.192.0/19
103.118.240.0/20
103.119.0.0/22
103.119.12.0/22
103.119.16.0/22
103.119.28.0/22
103.119.44.0/22
103.119.104.0/22
103.119.115.0/24
103.119.156.0/22
103.119.180.0/22
103.192.0.0/19 103.192.0.0/19
103.192.48.0/21 103.192.48.0/21
103.192.56.0/22 103.192.56.0/22
...@@ -2102,7 +2222,7 @@ ...@@ -2102,7 +2222,7 @@
103.211.168.0/22 103.211.168.0/22
103.211.192.0/22 103.211.192.0/22
103.211.220.0/22 103.211.220.0/22
103.211.224.0/21 103.211.224.0/22
103.211.248.0/22 103.211.248.0/22
103.212.0.0/20 103.212.0.0/20
103.212.32.0/22 103.212.32.0/22
...@@ -2131,7 +2251,6 @@ ...@@ -2131,7 +2251,6 @@
103.214.84.0/22 103.214.84.0/22
103.214.168.0/22 103.214.168.0/22
103.214.212.0/22 103.214.212.0/22
103.214.224.0/22
103.214.240.0/21 103.214.240.0/21
103.215.28.0/22 103.215.28.0/22
103.215.32.0/21 103.215.32.0/21
...@@ -2218,7 +2337,6 @@ ...@@ -2218,7 +2337,6 @@
103.223.188.0/22 103.223.188.0/22
103.223.192.0/18 103.223.192.0/18
103.224.0.0/22 103.224.0.0/22
103.224.16.0/22
103.224.40.0/21 103.224.40.0/21
103.224.60.0/22 103.224.60.0/22
103.224.80.0/22 103.224.80.0/22
...@@ -2741,7 +2859,7 @@ ...@@ -2741,7 +2859,7 @@
116.207.0.0/16 116.207.0.0/16
116.208.0.0/14 116.208.0.0/14
116.212.160.0/20 116.212.160.0/20
116.213.40.0/21 116.213.44.0/22
116.213.64.0/18 116.213.64.0/18
116.213.128.0/17 116.213.128.0/17
116.214.32.0/19 116.214.32.0/19
...@@ -2785,7 +2903,6 @@ ...@@ -2785,7 +2903,6 @@
117.103.72.0/21 117.103.72.0/21
117.103.128.0/20 117.103.128.0/20
117.104.168.0/21 117.104.168.0/21
117.104.188.0/24
117.106.0.0/15 117.106.0.0/15
117.112.0.0/13 117.112.0.0/13
117.120.64.0/18 117.120.64.0/18
...@@ -3127,6 +3244,8 @@ ...@@ -3127,6 +3244,8 @@
129.204.0.0/16 129.204.0.0/16
129.211.0.0/16 129.211.0.0/16
129.223.254.0/24 129.223.254.0/24
129.226.0.0/16
130.214.218.0/23
131.228.96.0/24 131.228.96.0/24
132.232.0.0/16 132.232.0.0/16
132.237.134.0/24 132.237.134.0/24
...@@ -3168,6 +3287,7 @@ ...@@ -3168,6 +3287,7 @@
140.75.0.0/16 140.75.0.0/16
140.101.208.0/24 140.101.208.0/24
140.143.0.0/16 140.143.0.0/16
140.179.0.0/16
140.205.0.0/16 140.205.0.0/16
140.206.0.0/15 140.206.0.0/15
140.210.0.0/16 140.210.0.0/16
...@@ -3187,11 +3307,9 @@ ...@@ -3187,11 +3307,9 @@
144.12.0.0/16 144.12.0.0/16
144.36.146.0/23 144.36.146.0/23
144.48.8.0/21 144.48.8.0/21
144.48.56.0/22
144.48.64.0/22 144.48.64.0/22
144.48.88.0/22 144.48.88.0/22
144.48.156.0/22 144.48.156.0/22
144.48.172.0/22
144.48.180.0/22 144.48.180.0/22
144.48.184.0/22 144.48.184.0/22
144.48.204.0/22 144.48.204.0/22
...@@ -3206,7 +3324,7 @@ ...@@ -3206,7 +3324,7 @@
146.56.192.0/18 146.56.192.0/18
146.196.56.0/22 146.196.56.0/22
146.196.68.0/22 146.196.68.0/22
146.196.72.0/21 146.196.72.0/22
146.196.92.0/22 146.196.92.0/22
146.196.112.0/21 146.196.112.0/21
146.196.124.0/22 146.196.124.0/22
...@@ -3216,7 +3334,6 @@ ...@@ -3216,7 +3334,6 @@
146.222.94.0/24 146.222.94.0/24
148.70.0.0/16 148.70.0.0/16
150.0.0.0/16 150.0.0.0/16
150.109.0.0/16
150.115.0.0/16 150.115.0.0/16
150.121.0.0/16 150.121.0.0/16
150.122.0.0/16 150.122.0.0/16
...@@ -3285,6 +3402,7 @@ ...@@ -3285,6 +3402,7 @@
157.148.0.0/16 157.148.0.0/16
157.156.0.0/16 157.156.0.0/16
157.255.0.0/16 157.255.0.0/16
159.75.0.0/16
159.153.120.0/22 159.153.120.0/22
159.226.0.0/16 159.226.0.0/16
160.19.208.0/21 160.19.208.0/21
...@@ -3306,8 +3424,16 @@ ...@@ -3306,8 +3424,16 @@
161.163.176.0/24 161.163.176.0/24
161.163.178.0/23 161.163.178.0/23
161.163.180.0/22 161.163.180.0/22
161.189.0.0/16
161.207.0.0/16 161.207.0.0/16
162.62.0.0/16 162.14.0.0/16
162.62.32.0/19
162.62.64.0/18
162.62.132.0/22
162.62.136.0/21
162.62.144.0/20
162.62.160.0/19
162.62.192.0/18
162.105.0.0/16 162.105.0.0/16
163.0.0.0/16 163.0.0.0/16
163.47.4.0/22 163.47.4.0/22
...@@ -3343,7 +3469,6 @@ ...@@ -3343,7 +3469,6 @@
168.159.158.0/24 168.159.158.0/24
168.160.0.0/16 168.160.0.0/16
168.230.0.0/24 168.230.0.0/24
170.106.0.0/16
170.179.0.0/16 170.179.0.0/16
170.225.224.0/23 170.225.224.0/23
170.252.152.0/21 170.252.152.0/21
...@@ -3379,6 +3504,7 @@ ...@@ -3379,6 +3504,7 @@
175.176.156.0/22 175.176.156.0/22
175.176.176.0/22 175.176.176.0/22
175.176.188.0/22 175.176.188.0/22
175.176.192.0/22
175.178.0.0/16 175.178.0.0/16
175.184.128.0/18 175.184.128.0/18
175.185.0.0/16 175.185.0.0/16
...@@ -3477,6 +3603,7 @@ ...@@ -3477,6 +3603,7 @@
183.184.0.0/13 183.184.0.0/13
183.192.0.0/10 183.192.0.0/10
185.109.236.0/24 185.109.236.0/24
185.252.218.0/23
188.131.128.0/17 188.131.128.0/17
192.11.23.0/24 192.11.23.0/24
192.11.26.0/24 192.11.26.0/24
...@@ -3500,12 +3627,11 @@ ...@@ -3500,12 +3627,11 @@
192.140.208.0/21 192.140.208.0/21
192.144.128.0/17 192.144.128.0/17
192.163.11.0/24 192.163.11.0/24
192.200.24.0/24
192.232.97.0/24 192.232.97.0/24
193.20.64.0/22 193.20.64.0/22
193.22.14.0/23
193.112.0.0/16 193.112.0.0/16
194.138.202.0/23 194.138.202.0/23
195.78.82.0/23
198.175.100.0/22 198.175.100.0/22
198.208.17.0/24 198.208.17.0/24
199.7.72.0/24 199.7.72.0/24
...@@ -3904,7 +4030,6 @@ ...@@ -3904,7 +4030,6 @@
202.164.96.0/19 202.164.96.0/19
202.165.96.0/21 202.165.96.0/21
202.165.104.0/22 202.165.104.0/22
202.165.110.0/23
202.165.176.0/20 202.165.176.0/20
202.165.208.0/20 202.165.208.0/20
202.165.239.0/24 202.165.239.0/24
...@@ -4898,7 +5023,7 @@ ...@@ -4898,7 +5023,7 @@
203.189.6.0/23 203.189.6.0/23
203.189.112.0/22 203.189.112.0/22
203.189.192.0/19 203.189.192.0/19
203.189.232.0/21 203.189.232.0/22
203.189.240.0/22 203.189.240.0/22
203.190.96.0/20 203.190.96.0/20
203.190.249.0/24 203.190.249.0/24
...@@ -5070,7 +5195,6 @@ ...@@ -5070,7 +5195,6 @@
223.116.0.0/15 223.116.0.0/15
223.120.0.0/13 223.120.0.0/13
223.128.0.0/15 223.128.0.0/15
223.130.8.0/22
223.144.0.0/12 223.144.0.0/12
223.160.0.0/14 223.160.0.0/14
223.166.0.0/15 223.166.0.0/15
......
...@@ -25,7 +25,7 @@ import android.content.ComponentName ...@@ -25,7 +25,7 @@ import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import com.github.shadowsocks.App.Companion.app import com.github.shadowsocks.Core.app
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
class BootReceiver : BroadcastReceiver() { class BootReceiver : BroadcastReceiver() {
...@@ -45,6 +45,6 @@ class BootReceiver : BroadcastReceiver() { ...@@ -45,6 +45,6 @@ class BootReceiver : BroadcastReceiver() {
Intent.ACTION_LOCKED_BOOT_COMPLETED -> true // constant will be folded so no need to do version checks Intent.ACTION_LOCKED_BOOT_COMPLETED -> true // constant will be folded so no need to do version checks
else -> return else -> return
} }
if (DataStore.directBootAware == locked) app.startService() if (DataStore.directBootAware == locked) Core.startService()
} }
} }
/*******************************************************************************
* *
* Copyright (C) 2018 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2018 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* 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/>. *
* *
*******************************************************************************/
package com.github.shadowsocks
import android.app.Application
import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.admin.DevicePolicyManager
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.content.pm.PackageInfo
import android.content.pm.PackageManager
import android.os.Build
import android.os.Handler
import android.os.Looper
import android.os.UserManager
import android.util.Log
import androidx.annotation.RequiresApi
import androidx.core.content.ContextCompat
import androidx.core.content.getSystemService
import androidx.work.WorkManager
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.core.R
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.database.ProfileManager
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.*
import com.google.firebase.FirebaseApp
import com.google.firebase.analytics.FirebaseAnalytics
import com.google.firebase.remoteconfig.FirebaseRemoteConfig
import io.fabric.sdk.android.Fabric
import java.io.File
import java.io.IOException
import kotlin.reflect.KClass
object Core {
const val TAG = "Core"
lateinit var app: Application
lateinit var configureIntent: (Context) -> PendingIntent
val handler by lazy { Handler(Looper.getMainLooper()) }
val packageInfo: PackageInfo by lazy { getPackageInfo(app.packageName) }
val deviceStorage by lazy { if (Build.VERSION.SDK_INT < 24) app else DeviceStorageApp(app) }
val remoteConfig: FirebaseRemoteConfig by lazy { FirebaseRemoteConfig.getInstance() }
val analytics: FirebaseAnalytics by lazy { FirebaseAnalytics.getInstance(deviceStorage) }
val directBootSupported by lazy {
Build.VERSION.SDK_INT >= 24 && app.getSystemService<DevicePolicyManager>()?.storageEncryptionStatus ==
DevicePolicyManager.ENCRYPTION_STATUS_ACTIVE_PER_USER
}
val currentProfile: Profile? get() =
if (DataStore.directBootAware) DirectBoot.getDeviceProfile() else ProfileManager.getProfile(DataStore.profileId)
fun switchProfile(id: Long): Profile {
val result = ProfileManager.getProfile(id) ?: ProfileManager.createProfile()
DataStore.profileId = result.id
return result
}
fun init(app: Application, configureClass: KClass<out Any>) {
this.app = app
this.configureIntent = {
PendingIntent.getActivity(it, 0, Intent(it, configureClass.java)
.setFlags(Intent.FLAG_ACTIVITY_REORDER_TO_FRONT), 0)
}
if (Build.VERSION.SDK_INT >= 24) { // migrate old files
deviceStorage.moveDatabaseFrom(app, Key.DB_PUBLIC)
val old = Acl.getFile(Acl.CUSTOM_RULES, app)
if (old.canRead()) {
Acl.getFile(Acl.CUSTOM_RULES).writeText(old.readText())
old.delete()
}
}
Fabric.with(deviceStorage, Crashlytics()) // multiple processes needs manual set-up
FirebaseApp.initializeApp(deviceStorage)
remoteConfig.setDefaults(R.xml.default_configs)
remoteConfig.fetch().addOnCompleteListener {
if (it.isSuccessful) remoteConfig.activateFetched() else {
Log.e(TAG, "Failed to fetch config")
Crashlytics.logException(it.exception)
}
}
WorkManager.initialize(deviceStorage, androidx.work.Configuration.Builder().build())
// handle data restored/crash
if (Build.VERSION.SDK_INT >= 24 && DataStore.directBootAware &&
app.getSystemService<UserManager>()?.isUserUnlocked == true) DirectBoot.flushTrafficStats()
if (DataStore.tcpFastOpen) TcpFastOpen.enabledAsync(true)
if (DataStore.publicStore.getLong(Key.assetUpdateTime, -1) != packageInfo.lastUpdateTime) {
val assetManager = app.assets
for (dir in arrayOf("acl", "overture"))
try {
for (file in assetManager.list(dir)!!) assetManager.open("$dir/$file").use { input ->
File(deviceStorage.noBackupFilesDir, file).outputStream().use { output -> input.copyTo(output) }
}
} catch (e: IOException) {
printLog(e)
}
DataStore.publicStore.putLong(Key.assetUpdateTime, packageInfo.lastUpdateTime)
}
updateNotificationChannels()
}
fun updateNotificationChannels() {
if (Build.VERSION.SDK_INT >= 26) @RequiresApi(26) {
val nm = app.getSystemService<NotificationManager>()!!
nm.createNotificationChannels(listOf(
NotificationChannel("service-vpn", app.getText(R.string.service_vpn),
NotificationManager.IMPORTANCE_LOW),
NotificationChannel("service-proxy", app.getText(R.string.service_proxy),
NotificationManager.IMPORTANCE_LOW),
NotificationChannel("service-transproxy", app.getText(R.string.service_transproxy),
NotificationManager.IMPORTANCE_LOW)))
nm.deleteNotificationChannel("service-nat") // NAT mode is gone for good
}
}
fun getPackageInfo(packageName: String) = app.packageManager.getPackageInfo(packageName,
if (Build.VERSION.SDK_INT >= 28) PackageManager.GET_SIGNING_CERTIFICATES
else @Suppress("DEPRECATION") PackageManager.GET_SIGNATURES)!!
fun startService() = ContextCompat.startForegroundService(app, Intent(app, BaseService.serviceClass.java))
fun reloadService() = app.sendBroadcast(Intent(Action.RELOAD))
fun stopService() = app.sendBroadcast(Intent(Action.CLOSE))
fun listenForPackageChanges(onetime: Boolean = true, callback: () -> Unit): BroadcastReceiver {
val filter = IntentFilter(Intent.ACTION_PACKAGE_ADDED)
filter.addAction(Intent.ACTION_PACKAGE_REMOVED)
filter.addDataScheme("package")
val result = object : BroadcastReceiver() {
override fun onReceive(context: Context, intent: Intent) {
if (intent.getBooleanExtra(Intent.EXTRA_REPLACING, false)) return
callback()
if (onetime) app.unregisterReceiver(this)
}
}
app.registerReceiver(result, filter)
return result
}
}
...@@ -38,39 +38,17 @@ ...@@ -38,39 +38,17 @@
package com.github.shadowsocks; package com.github.shadowsocks;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.Nullable;
import android.system.ErrnoException; import android.system.ErrnoException;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
public class JniHelper { public class JniHelper {
static { static {
System.loadLibrary("jni-helper"); System.loadLibrary("jni-helper");
} }
@Deprecated // Use Process.destroy() since API 24 public static native void sendFd(int fd, @NonNull String path) throws ErrnoException;
public static void sigtermCompat(@NonNull Process process) throws Exception {
if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
int errno = sigterm(process);
if (errno != 0) throw new ErrnoException("kill", errno);
}
@Deprecated // only implemented for before API 24
public static boolean waitForCompat(@NonNull Process process, long millis) throws Exception {
if (Build.VERSION.SDK_INT >= 24) throw new UnsupportedOperationException("Never call this method in OpenJDK!");
final Object mutex = getExitValueMutex(process);
synchronized (mutex) {
if (getExitValue(process) == null) mutex.wait(millis);
return getExitValue(process) != null;
}
}
public static native int sigkill(int pid);
private static native int sigterm(Process process);
private static native Integer getExitValue(Process process);
private static native Object getExitValueMutex(Process process);
public static native int sendFd(int fd, @NonNull String path);
public static native void close(int fd);
@Nullable @Nullable
public static native byte[] parseNumericAddress(@NonNull String str); public static native byte[] parseNumericAddress(@NonNull String str);
} }
...@@ -20,10 +20,12 @@ ...@@ -20,10 +20,12 @@
package com.github.shadowsocks package com.github.shadowsocks
import android.app.Fragment
import android.content.ComponentName import android.content.ComponentName
import android.content.Context import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.ServiceConnection import android.content.ServiceConnection
import android.os.DeadObjectException
import android.os.IBinder import android.os.IBinder
import android.os.RemoteException import android.os.RemoteException
import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.aidl.IShadowsocksService
...@@ -40,7 +42,7 @@ class ShadowsocksConnection(private val instance: Interface) : ServiceConnection ...@@ -40,7 +42,7 @@ class ShadowsocksConnection(private val instance: Interface) : ServiceConnection
interface Interface : IBinder.DeathRecipient { interface Interface : IBinder.DeathRecipient {
val serviceCallback: IShadowsocksServiceCallback? get() = null val serviceCallback: IShadowsocksServiceCallback? get() = null
val connection: ShadowsocksConnection val connection: ShadowsocksConnection
get() = connections.getOrPut(this, { ShadowsocksConnection(this) }) get() = connections.getOrPut(this) { ShadowsocksConnection(this) }
val listenForDeath get() = false val listenForDeath get() = false
fun onServiceConnected(service: IShadowsocksService) { } fun onServiceConnected(service: IShadowsocksService) { }
...@@ -56,13 +58,20 @@ class ShadowsocksConnection(private val instance: Interface) : ServiceConnection ...@@ -56,13 +58,20 @@ class ShadowsocksConnection(private val instance: Interface) : ServiceConnection
private var connectionActive = false private var connectionActive = false
private var callbackRegistered = false private var callbackRegistered = false
private var binder: IBinder? = null private var binder: IBinder? = null
private val context by lazy {
when (instance) {
is Fragment -> instance.activity
else -> instance as Context
}
}
var listeningForBandwidth = false var listeningForBandwidth = false
set(value) { set(value) {
val service = service val service = service
if (listeningForBandwidth != value && service != null && instance.serviceCallback != null) if (listeningForBandwidth != value && service != null && instance.serviceCallback != null)
if (value) service.startListeningForBandwidth(instance.serviceCallback) if (value) service.startListeningForBandwidth(instance.serviceCallback) else try {
else service.stopListeningForBandwidth(instance.serviceCallback) service.stopListeningForBandwidth(instance.serviceCallback)
} catch (_: DeadObjectException) { }
field = value field = value
} }
var service: IShadowsocksService? = null var service: IShadowsocksService? = null
...@@ -98,15 +107,15 @@ class ShadowsocksConnection(private val instance: Interface) : ServiceConnection ...@@ -98,15 +107,15 @@ class ShadowsocksConnection(private val instance: Interface) : ServiceConnection
fun connect() { fun connect() {
if (connectionActive) return if (connectionActive) return
connectionActive = true connectionActive = true
val intent = Intent(instance as Context, BaseService.serviceClass.java).setAction(Action.SERVICE) val intent = Intent(context, BaseService.serviceClass.java).setAction(Action.SERVICE)
instance.bindService(intent, this, Context.BIND_AUTO_CREATE) context.bindService(intent, this, Context.BIND_AUTO_CREATE)
} }
fun disconnect() { fun disconnect() {
unregisterCallback() unregisterCallback()
instance.onServiceDisconnected() instance.onServiceDisconnected()
if (connectionActive) try { if (connectionActive) try {
(instance as Context).unbindService(this) context.unbindService(this)
} catch (_: IllegalArgumentException) { } // ignore } catch (_: IllegalArgumentException) { } // ignore
connectionActive = false connectionActive = false
if (instance.listenForDeath) binder?.unlinkToDeath(instance, 0) if (instance.listenForDeath) binder?.unlinkToDeath(instance, 0)
......
...@@ -22,16 +22,18 @@ package com.github.shadowsocks ...@@ -22,16 +22,18 @@ package com.github.shadowsocks
import android.app.KeyguardManager import android.app.KeyguardManager
import android.content.BroadcastReceiver import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.net.VpnService import android.net.VpnService
import android.os.Bundle import android.os.Bundle
import android.support.v7.app.AppCompatActivity
import android.util.Log import android.util.Log
import com.github.shadowsocks.App.Companion.app import android.widget.Toast
import androidx.appcompat.app.AppCompatActivity
import androidx.core.content.getSystemService
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.aidl.IShadowsocksService import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.bg.BaseService import com.github.shadowsocks.bg.BaseService
import com.github.shadowsocks.core.R
import com.github.shadowsocks.utils.broadcastReceiver import com.github.shadowsocks.utils.broadcastReceiver
class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface { class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface {
...@@ -48,8 +50,7 @@ class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface ...@@ -48,8 +50,7 @@ class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface
finish() finish()
return return
} }
val km = getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager if (getSystemService<KeyguardManager>()!!.isKeyguardLocked) {
if (km.inKeyguardRestrictedInputMode()) {
receiver = broadcastReceiver { _, _ -> connection.connect() } receiver = broadcastReceiver { _, _ -> connection.connect() }
registerReceiver(receiver, IntentFilter(Intent.ACTION_USER_PRESENT)) registerReceiver(receiver, IntentFilter(Intent.ACTION_USER_PRESENT))
} else connection.connect() } else connection.connect()
...@@ -62,7 +63,10 @@ class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface ...@@ -62,7 +63,10 @@ class VpnRequestActivity : AppCompatActivity(), ShadowsocksConnection.Interface
} }
override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) {
if (resultCode == RESULT_OK) app.startService() else Log.e(TAG, "Failed to start VpnService") if (resultCode == RESULT_OK) Core.startService() else {
Toast.makeText(this, R.string.vpn_permission_denied, Toast.LENGTH_LONG).show()
Crashlytics.log(Log.ERROR, TAG, "Failed to start VpnService from onActivityResult: $data")
}
finish() finish()
} }
......
...@@ -21,9 +21,10 @@ ...@@ -21,9 +21,10 @@
package com.github.shadowsocks.acl package com.github.shadowsocks.acl
import android.content.Context import android.content.Context
import android.support.v7.util.SortedList
import android.util.Log import android.util.Log
import com.github.shadowsocks.App.Companion.app import androidx.recyclerview.widget.SortedList
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Subnet import com.github.shadowsocks.utils.Subnet
import com.github.shadowsocks.utils.asIterable import com.github.shadowsocks.utils.asIterable
...@@ -45,25 +46,22 @@ class Acl { ...@@ -45,25 +46,22 @@ class Acl {
val networkAclParser = "^IMPORT_URL\\s*<(.+)>\\s*$".toRegex() val networkAclParser = "^IMPORT_URL\\s*<(.+)>\\s*$".toRegex()
fun getFile(id: String, context: Context = app.deviceContext) = File(context.filesDir, id + ".acl") fun getFile(id: String, context: Context = Core.deviceStorage) = File(context.noBackupFilesDir, "$id.acl")
var customRules: Acl var customRules: Acl
get() { get() {
val acl = Acl() val acl = Acl()
val str = DataStore.publicStore.getString(CUSTOM_RULES) val str = DataStore.publicStore.getString(CUSTOM_RULES)
if (str != null) { if (str != null) acl.fromReader(str.reader(), true)
acl.fromReader(str.reader())
if (!acl.bypass) { if (!acl.bypass) {
acl.subnets.clear()
acl.hostnames.clear()
acl.bypass = true acl.bypass = true
acl.subnets.clear()
} }
} else acl.bypass = true
return acl return acl
} }
set(value) = DataStore.publicStore.putString(CUSTOM_RULES, if ((!value.bypass || set(value) = DataStore.publicStore.putString(CUSTOM_RULES,
value.subnets.size() == 0 && value.hostnames.size() == 0) && value.urls.size() == 0) if ((!value.bypass || value.subnets.size() == 0) && value.bypassHostnames.size() == 0 &&
null else value.toString()) value.proxyHostnames.size() == 0 && value.urls.size() == 0) null else value.toString())
fun save(id: String, acl: Acl) = getFile(id).writeText(acl.toString()) fun save(id: String, acl: Acl) = getFile(id).writeText(acl.toString())
} }
...@@ -88,31 +86,30 @@ class Acl { ...@@ -88,31 +86,30 @@ class Acl {
override fun compareNonNull(o1: URL, o2: URL): Int = ordering.compare(o1, o2) override fun compareNonNull(o1: URL, o2: URL): Int = ordering.compare(o1, o2)
} }
val hostnames = SortedList(String::class.java, StringSorter) val bypassHostnames = SortedList(String::class.java, StringSorter)
val proxyHostnames = SortedList(String::class.java, StringSorter)
val subnets = SortedList(Subnet::class.java, SubnetSorter) val subnets = SortedList(Subnet::class.java, SubnetSorter)
val urls = SortedList(URL::class.java, URLSorter) val urls = SortedList(URL::class.java, URLSorter)
var bypass = false var bypass = false
fun clear(): Acl {
hostnames.clear()
subnets.clear()
urls.clear()
return this
}
fun fromAcl(other: Acl): Acl { fun fromAcl(other: Acl): Acl {
clear() bypassHostnames.clear()
for (item in other.hostnames.asIterable()) hostnames.add(item) for (item in other.bypassHostnames.asIterable()) bypassHostnames.add(item)
proxyHostnames.clear()
for (item in other.proxyHostnames.asIterable()) proxyHostnames.add(item)
subnets.clear()
for (item in other.subnets.asIterable()) subnets.add(item) for (item in other.subnets.asIterable()) subnets.add(item)
urls.clear()
for (item in other.urls.asIterable()) urls.add(item) for (item in other.urls.asIterable()) urls.add(item)
bypass = other.bypass bypass = other.bypass
return this return this
} }
fun fromReader(reader: Reader, defaultBypass: Boolean = false): Acl { fun fromReader(reader: Reader, defaultBypass: Boolean = false): Acl {
clear() bypassHostnames.clear()
proxyHostnames.clear()
subnets.clear()
urls.clear()
bypass = defaultBypass bypass = defaultBypass
val proxyHostnames by lazy { SortedList(String::class.java, StringSorter) }
val bypassHostnames by lazy { SortedList(String::class.java, StringSorter) }
val bypassSubnets by lazy { SortedList(Subnet::class.java, SubnetSorter) } val bypassSubnets by lazy { SortedList(Subnet::class.java, SubnetSorter) }
val proxySubnets by lazy { SortedList(Subnet::class.java, SubnetSorter) } val proxySubnets by lazy { SortedList(Subnet::class.java, SubnetSorter) }
var hostnames: SortedList<String>? = if (defaultBypass) proxyHostnames else bypassHostnames var hostnames: SortedList<String>? = if (defaultBypass) proxyHostnames else bypassHostnames
...@@ -121,7 +118,7 @@ class Acl { ...@@ -121,7 +118,7 @@ class Acl {
for (line in it) { for (line in it) {
@Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN") @Suppress("PLATFORM_CLASS_MAPPED_TO_KOTLIN")
val blocks = (line as java.lang.String).split("#", 2) val blocks = (line as java.lang.String).split("#", 2)
val url = networkAclParser.matchEntire(blocks.getOrElse(1, {""}))?.groupValues?.getOrNull(1) val url = networkAclParser.matchEntire(blocks.getOrElse(1) { "" })?.groupValues?.getOrNull(1)
if (url != null) urls.add(URL(url)) if (url != null) urls.add(URL(url))
val input = blocks[0].trim() val input = blocks[0].trim()
when (input) { when (input) {
...@@ -146,13 +143,12 @@ class Acl { ...@@ -146,13 +143,12 @@ class Acl {
} }
} }
} }
for (item in (if (bypass) proxyHostnames else bypassHostnames).asIterable()) this.hostnames.add(item)
for (item in (if (bypass) proxySubnets else bypassSubnets).asIterable()) this.subnets.add(item) for (item in (if (bypass) proxySubnets else bypassSubnets).asIterable()) this.subnets.add(item)
return this return this
} }
fun fromId(id: String): Acl = try { fun fromId(id: String): Acl = try {
fromReader(Acl.getFile(id).bufferedReader()) fromReader(getFile(id).bufferedReader())
} catch (_: IOException) { this } } catch (_: IOException) { this }
fun flatten(depth: Int): Acl { fun flatten(depth: Int): Acl {
...@@ -160,18 +156,18 @@ class Acl { ...@@ -160,18 +156,18 @@ class Acl {
val child = Acl() val child = Acl()
try { try {
child.fromReader(url.openStream().bufferedReader(), bypass).flatten(depth - 1) child.fromReader(url.openStream().bufferedReader(), bypass).flatten(depth - 1)
} catch (_: IOException) { } catch (e: IOException) {
e.printStackTrace()
continue continue
} }
if (bypass != child.bypass) { if (bypass != child.bypass) {
Log.w(TAG, "Imported network ACL has a conflicting mode set. " + Crashlytics.log(Log.WARN, TAG, "Imported network ACL has a conflicting mode set. " +
"This will probably not work as intended. URL: $url") "This will probably not work as intended. URL: $url")
// rules for the different mode are discarded child.subnets.clear() // subnets for the different mode are discarded
child.hostnames.clear()
child.subnets.clear()
child.bypass = bypass child.bypass = bypass
} }
for (item in child.hostnames.asIterable()) hostnames.add(item) for (item in child.bypassHostnames.asIterable()) bypassHostnames.add(item)
for (item in child.proxyHostnames.asIterable()) proxyHostnames.add(item)
for (item in child.subnets.asIterable()) subnets.add(item) for (item in child.subnets.asIterable()) subnets.add(item)
} }
urls.clear() urls.clear()
...@@ -180,11 +176,21 @@ class Acl { ...@@ -180,11 +176,21 @@ class Acl {
override fun toString(): String { override fun toString(): String {
val result = StringBuilder() val result = StringBuilder()
result.append(if (bypass) "[bypass_all]\n[proxy_list]\n" else "[proxy_all]\n[bypass_list]\n") result.append(if (bypass) "[bypass_all]\n" else "[proxy_all]\n")
result.append(subnets.asIterable().joinToString("\n")) val bypassList = (if (bypass) bypassHostnames.asIterable().asSequence() else
subnets.asIterable().asSequence().map(Subnet::toString) + proxyHostnames.asIterable().asSequence()).toList()
val proxyList = (if (bypass) subnets.asIterable().asSequence().map(Subnet::toString) +
proxyHostnames.asIterable().asSequence() else bypassHostnames.asIterable().asSequence()).toList()
if (bypassList.isNotEmpty()) {
result.append("[bypass_list]\n")
result.append(bypassList.joinToString("\n"))
result.append('\n') result.append('\n')
result.append(hostnames.asIterable().joinToString("\n")) }
if (proxyList.isNotEmpty()) {
result.append("[proxy_list]\n")
result.append(proxyList.joinToString("\n"))
result.append('\n') result.append('\n')
}
result.append(urls.asIterable().joinToString("") { "#IMPORT_URL <$it>\n" }) result.append(urls.asIterable().joinToString("") { "#IMPORT_URL <$it>\n" })
return result.toString() return result.toString()
} }
......
...@@ -20,49 +20,38 @@ ...@@ -20,49 +20,38 @@
package com.github.shadowsocks.acl package com.github.shadowsocks.acl
import android.util.Log import android.content.Context
import com.evernote.android.job.Job import androidx.work.*
import com.evernote.android.job.JobCreator
import com.evernote.android.job.JobRequest
import java.io.IOException import java.io.IOException
import java.net.URL import java.net.URL
import java.util.concurrent.TimeUnit import java.util.concurrent.TimeUnit
class AclSyncJob(private val route: String) : Job() { class AclSyncer(context: Context, workerParams: WorkerParameters) : Worker(context, workerParams) {
companion object : JobCreator { companion object {
private const val TAG = "AclSyncJob" private const val KEY_ROUTE = "route"
private const val MAX_RESCHEDULE = 3
override fun create(tag: String): AclSyncJob? { fun schedule(route: String) = WorkManager.getInstance().enqueue(OneTimeWorkRequestBuilder<AclSyncer>().run {
val parts = tag.split(':', limit = 2) setInputData(Data.Builder().putString(KEY_ROUTE, route).build())
return when (parts[0]) { setConstraints(Constraints.Builder()
TAG -> AclSyncJob(parts[1]) .setRequiredNetworkType(NetworkType.UNMETERED)
else -> {
Log.w(TAG, "Unknown job tag: " + tag)
null
}
}
}
fun schedule(route: String): Int = JobRequest.Builder(TAG + ':' + route)
.setExecutionWindow(TimeUnit.SECONDS.toMillis(10), TimeUnit.DAYS.toMillis(28))
.setRequirementsEnforced(true)
.setRequiredNetworkType(JobRequest.NetworkType.UNMETERED)
.setRequiresCharging(true) .setRequiresCharging(true)
.setUpdateCurrent(true) .build())
.build().schedule() setInitialDelay(10, TimeUnit.SECONDS)
build()
})
} }
override fun onRunJob(params: Params): Result = try { override fun doWork(): Result = try {
val route = inputData.getString(KEY_ROUTE)!!
val acl = URL("https://shadowsocks.org/acl/android/v1/$route.acl").openStream().bufferedReader() val acl = URL("https://shadowsocks.org/acl/android/v1/$route.acl").openStream().bufferedReader()
.use { it.readText() } .use { it.readText() }
Acl.getFile(route).printWriter().use { it.write(acl) } Acl.getFile(route).printWriter().use { it.write(acl) }
Result.SUCCESS Result.success()
} catch (e: IOException) { } catch (e: IOException) {
e.printStackTrace() e.printStackTrace()
if (params.failureCount < AclSyncJob.MAX_RESCHEDULE) Result.RESCHEDULE else Result.FAILURE Result.retry()
} catch (e: Exception) { // unknown failures, probably shouldn't retry } catch (e: Exception) { // unknown failures, probably shouldn't retry
e.printStackTrace() e.printStackTrace()
Result.FAILURE Result.failure()
} }
} }
...@@ -20,10 +20,12 @@ ...@@ -20,10 +20,12 @@
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import android.system.ErrnoException
import android.system.Os
import android.text.TextUtils import android.text.TextUtils
import android.util.Log import android.util.Log
import com.github.shadowsocks.App.Companion.app import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.JniHelper import com.github.shadowsocks.Core.app
import java.io.File import java.io.File
import java.io.FileNotFoundException import java.io.FileNotFoundException
...@@ -34,7 +36,7 @@ object Executable { ...@@ -34,7 +36,7 @@ object Executable {
const val TUN2SOCKS = "libtun2socks.so" const val TUN2SOCKS = "libtun2socks.so"
const val OVERTURE = "liboverture.so" const val OVERTURE = "liboverture.so"
val EXECUTABLES = setOf(SS_LOCAL, SS_TUNNEL, REDSOCKS, TUN2SOCKS, OVERTURE) private val EXECUTABLES = setOf(SS_LOCAL, SS_TUNNEL, REDSOCKS, TUN2SOCKS, OVERTURE)
fun killAll() { fun killAll() {
for (process in File("/proc").listFiles { _, name -> TextUtils.isDigitsOnly(name) }) { for (process in File("/proc").listFiles { _, name -> TextUtils.isDigitsOnly(name) }) {
...@@ -43,9 +45,14 @@ object Executable { ...@@ -43,9 +45,14 @@ object Executable {
} catch (ignore: FileNotFoundException) { } catch (ignore: FileNotFoundException) {
continue continue
}.split(Character.MIN_VALUE, limit = 2).first()) }.split(Character.MIN_VALUE, limit = 2).first())
if (exe.parent == app.applicationInfo.nativeLibraryDir && EXECUTABLES.contains(exe.name)) { if (exe.parent == app.applicationInfo.nativeLibraryDir && EXECUTABLES.contains(exe.name)) try {
val errno = JniHelper.sigkill(process.name.toInt()) Os.kill(process.name.toInt(), 9) // SIGKILL
if (errno != 0) Log.w("kill", "SIGKILL ${exe.absolutePath} (${process.name}) failed with $errno") } catch (e: ErrnoException) {
if (e.errno != 3) { // ESRCH
e.printStackTrace()
Crashlytics.log(Log.WARN, "kill", "SIGKILL ${exe.absolutePath} (${process.name}) failed")
Crashlytics.logException(e)
}
} }
} }
} }
......
...@@ -22,49 +22,58 @@ package com.github.shadowsocks.bg ...@@ -22,49 +22,58 @@ package com.github.shadowsocks.bg
import android.os.Build import android.os.Build
import android.os.SystemClock import android.os.SystemClock
import android.system.ErrnoException
import android.system.Os
import android.util.Log import android.util.Log
import com.github.shadowsocks.App.Companion.app import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.BuildConfig import com.github.shadowsocks.Core
import com.github.shadowsocks.JniHelper
import com.github.shadowsocks.utils.Commandline import com.github.shadowsocks.utils.Commandline
import com.github.shadowsocks.utils.thread import com.github.shadowsocks.utils.thread
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.InputStream import java.io.InputStream
import java.util.concurrent.Semaphore import java.util.concurrent.ArrayBlockingQueue
import java.util.concurrent.atomic.AtomicReference
import java.util.concurrent.TimeUnit
class GuardedProcess(private val cmd: List<String>) { class GuardedProcessPool {
companion object { companion object Dummy : IOException("Oopsie the developer has made a no-no") {
private const val TAG = "GuardedProcess" private const val TAG = "GuardedProcessPool"
private val ProcessImpl by lazy { Class.forName("java.lang.ProcessManager\$ProcessImpl") }
private val pid by lazy { ProcessImpl.getDeclaredField("pid").apply { isAccessible = true } }
private val exitValueMutex by lazy {
ProcessImpl.getDeclaredField("exitValueMutex").apply { isAccessible = true }
}
} }
private lateinit var guardThread: Thread private inner class Guard(private val cmd: List<String>, private val onRestartCallback: (() -> Unit)?) {
@Volatile val cmdName = File(cmd.first()).nameWithoutExtension
private var isDestroyed = false val excQueue = ArrayBlockingQueue<IOException>(1) // ArrayBlockingQueue doesn't want null
@Volatile private var pushed = false
private lateinit var process: Process
private val name = File(cmd.first()).nameWithoutExtension
private fun streamLogger(input: InputStream, logger: (String, String) -> Int) = thread("StreamLogger-$name") { private fun streamLogger(input: InputStream, logger: (String, String) -> Int) =
thread("StreamLogger-$cmdName") {
try { try {
input.bufferedReader().useLines { it.forEach { logger(TAG, it) } } input.bufferedReader().forEachLine { logger(TAG, it) }
} catch (_: IOException) { } // ignore } catch (_: IOException) { } // ignore
} }
private fun pushException(ioException: IOException?) {
if (pushed) return
excQueue.put(ioException ?: Dummy)
pushed = true
}
fun start(onRestartCallback: (() -> Unit)? = null): GuardedProcess { fun looper(host: HashSet<Thread>) {
val semaphore = Semaphore(1) var process: Process? = null
semaphore.acquire()
var ioException: IOException? = null
guardThread = thread("GuardThread-$name") {
try { try {
var callback: (() -> Unit)? = null var callback: (() -> Unit)? = null
while (!isDestroyed) { while (guardThreads.get() === host) {
if (BuildConfig.DEBUG) Log.d(TAG, "start process: " + Commandline.toString(cmd)) Crashlytics.log(Log.DEBUG, TAG, "start process: " + Commandline.toString(cmd))
val startTime = SystemClock.elapsedRealtime() val startTime = SystemClock.elapsedRealtime()
process = ProcessBuilder(cmd) process = ProcessBuilder(cmd)
.redirectErrorStream(true) .redirectErrorStream(true)
.directory(app.deviceContext.filesDir) .directory(Core.deviceStorage.noBackupFilesDir)
.start() .start()
streamLogger(process.inputStream, Log::i) streamLogger(process.inputStream, Log::i)
...@@ -72,44 +81,79 @@ class GuardedProcess(private val cmd: List<String>) { ...@@ -72,44 +81,79 @@ class GuardedProcess(private val cmd: List<String>) {
if (callback == null) callback = onRestartCallback else callback() if (callback == null) callback = onRestartCallback else callback()
semaphore.release() pushException(null)
process.waitFor() process.waitFor()
synchronized(this) {
if (SystemClock.elapsedRealtime() - startTime < 1000) { if (SystemClock.elapsedRealtime() - startTime < 1000) {
Log.w(TAG, "process exit too fast, stop guard: " + Commandline.toString(cmd)) Crashlytics.log(Log.WARN, TAG, "process exit too fast, stop guard: $cmdName")
isDestroyed = true break
}
} }
} }
} catch (_: InterruptedException) { } catch (_: InterruptedException) {
if (BuildConfig.DEBUG) Log.d(TAG, "thread interrupt, destroy process: " + Commandline.toString(cmd)) Crashlytics.log(Log.DEBUG, TAG, "thread interrupt, destroy process: $cmdName")
destroyProcess()
} catch (e: IOException) { } catch (e: IOException) {
ioException = e pushException(e)
} finally { } finally {
semaphore.release() if (process != null) {
if (Build.VERSION.SDK_INT < 24) {
val pid = pid.get(process) as Int
try {
Os.kill(pid, 15) // SIGTERM
} catch (e: ErrnoException) {
if (e.errno != 3) throw e // ESRCH
}
val mutex = exitValueMutex.get(process) as Object
synchronized(mutex) {
try {
process.exitValue()
} catch (e: IllegalThreadStateException) {
mutex.wait(500)
}
}
}
process.destroy() // kill the process
if (Build.VERSION.SDK_INT >= 26) {
val isKilled = process.waitFor(1L, TimeUnit.SECONDS) // wait for 1 second
if (!isKilled) {
process.destroyForcibly() // Force to kill the process if it's still alive
} }
} }
semaphore.acquire()
if (ioException != null) throw ioException!! process.waitFor() // ensure the process is destroyed
}
pushException(null)
}
}
}
/**
* This is an indication of which thread pool is being active.
* Reading/writing this collection still needs an additional lock to prevent concurrent modification.
*/
private val guardThreads = AtomicReference<HashSet<Thread>>(HashSet())
fun start(cmd: List<String>, onRestartCallback: (() -> Unit)? = null): GuardedProcessPool {
val guard = Guard(cmd, onRestartCallback)
val guardThreads = guardThreads.get()
synchronized(guardThreads) {
guardThreads.add(thread("GuardThread-${guard.cmdName}") {
guard.looper(guardThreads)
})
}
val ioException = guard.excQueue.take()
if (ioException !== Dummy) throw ioException
return this return this
} }
fun destroy() { fun killAll() {
isDestroyed = true val guardThreads = guardThreads.getAndSet(HashSet())
guardThread.interrupt() synchronized(guardThreads) {
destroyProcess() guardThreads.forEach { it.interrupt() }
try { try {
guardThread.join() guardThreads.forEach { it.join() }
} catch (_: InterruptedException) { } } catch (_: InterruptedException) { }
} }
private fun destroyProcess() {
if (Build.VERSION.SDK_INT < 24) @Suppress("DEPRECATION") {
JniHelper.sigtermCompat(process)
JniHelper.waitForCompat(process, 500)
}
process.destroy()
} }
} }
...@@ -20,94 +20,74 @@ ...@@ -20,94 +20,74 @@
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import com.github.shadowsocks.App.Companion.app import com.github.shadowsocks.Core
import com.github.shadowsocks.Core.app
import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.parseNumericAddress import com.github.shadowsocks.utils.parseNumericAddress
import org.json.JSONArray
import org.json.JSONObject
import java.io.File import java.io.File
import java.net.Inet6Address import java.net.Inet6Address
import java.util.* import org.json.JSONArray
import org.json.JSONObject
/**
* This object also uses WeakMap to simulate the effects of multi-inheritance, but more lightweight.
*/
object LocalDnsService { object LocalDnsService {
interface Interface : BaseService.Interface { interface Interface : BaseService.Interface {
var overtureProcess: GuardedProcess?
get() = overtureProcesses[this]
set(value) {
if (value == null) overtureProcesses.remove(this) else overtureProcesses[this] = value
}
override fun startNativeProcesses() { override fun startNativeProcesses() {
super.startNativeProcesses() super.startNativeProcesses()
val data = data val data = data
val profile = data.profile!! val profile = data.profile!!
fun makeDns(name: String, address: String, timeout: Int, edns: Boolean = true): JSONObject { fun makeDns(name: String, address: String, timeout: Int, edns: Boolean = true) = JSONObject().apply {
val dns = JSONObject() put("Name", name)
.put("Name", name) put("Address", when (address.parseNumericAddress()) {
.put("Address", when (address.parseNumericAddress()) {
is Inet6Address -> "[$address]" is Inet6Address -> "[$address]"
else -> address else -> address
}) })
.put("Timeout", timeout) put("Timeout", timeout)
.put("EDNSClientSubnet", JSONObject().put("Policy", "disable")) put("EDNSClientSubnet", JSONObject().put("Policy", "disable"))
if (edns) dns put("Protocol", if (edns) {
.put("Protocol", "tcp") put("Socks5Address", "127.0.0.1:" + DataStore.portProxy)
.put("Socks5Address", "127.0.0.1:" + DataStore.portProxy) "tcp"
else dns.put("Protocol", "udp") } else "udp")
return dns
} }
fun buildOvertureConfig(file: String): String { fun buildOvertureConfig(file: String) = file.also {
val config = JSONObject() File(Core.deviceStorage.noBackupFilesDir, it).writeText(JSONObject().run {
.put("BindAddress", "127.0.0.1:" + DataStore.portLocalDns) put("BindAddress", "${DataStore.listenAddress}:${DataStore.portLocalDns}")
.put("RedirectIPv6Record", true) put("RedirectIPv6Record", true)
.put("DomainBase64Decode", false) put("DomainBase64Decode", false)
.put("HostsFile", "hosts") put("HostsFile", "hosts")
.put("MinimumTTL", 120) put("MinimumTTL", 120)
.put("CacheSize", 4096) put("CacheSize", 4096)
val remoteDns = JSONArray(profile.remoteDns.split(",") val remoteDns = JSONArray(profile.remoteDns.split(",")
.mapIndexed { i, dns -> makeDns("UserDef-$i", dns.trim() + ":53", 9) }) .mapIndexed { i, dns -> makeDns("UserDef-$i", dns.trim() + ":53", 12) })
val localDns = JSONArray(arrayOf( val localDns = JSONArray(arrayOf(
makeDns("Primary-1", "208.67.222.222:443", 3, false), makeDns("Primary-1", "208.67.222.222:443", 9, false),
makeDns("Primary-2", "119.29.29.29:53", 3, false), makeDns("Primary-2", "119.29.29.29:53", 9, false),
makeDns("Primary-3", "114.114.114.114:53", 3, false) makeDns("Primary-3", "114.114.114.114:53", 9, false)))
))
when (profile.route) { when (profile.route) {
Acl.BYPASS_CHN, Acl.BYPASS_LAN_CHN, Acl.GFWLIST, Acl.CUSTOM_RULES -> config Acl.BYPASS_CHN, Acl.BYPASS_LAN_CHN, Acl.GFWLIST, Acl.CUSTOM_RULES -> {
.put("PrimaryDNS", localDns) put("PrimaryDNS", localDns)
.put("AlternativeDNS", remoteDns) put("AlternativeDNS", remoteDns)
.put("IPNetworkFile", "china_ip_list.txt") put("IPNetworkFile", "china_ip_list.txt")
Acl.CHINALIST -> config }
.put("PrimaryDNS", localDns) Acl.CHINALIST -> {
.put("AlternativeDNS", remoteDns) put("PrimaryDNS", localDns)
else -> config put("AlternativeDNS", remoteDns)
.put("PrimaryDNS", remoteDns) }
else -> {
put("PrimaryDNS", remoteDns)
// no need to setup AlternativeDNS in Acl.ALL/BYPASS_LAN mode // no need to setup AlternativeDNS in Acl.ALL/BYPASS_LAN mode
.put("OnlyPrimaryDNS", true) put("OnlyPrimaryDNS", true)
} }
File(app.deviceContext.filesDir, file).writeText(config.toString())
return file
} }
toString()
if (!profile.udpdns) overtureProcess = GuardedProcess(buildAdditionalArguments(arrayListOf( })
File(app.applicationInfo.nativeLibraryDir, Executable.OVERTURE).absolutePath,
"-c", buildOvertureConfig("overture.conf")
))).start()
} }
override fun killProcesses() { if (!profile.udpdns) data.processes.start(buildAdditionalArguments(arrayListOf(
super.killProcesses() File(app.applicationInfo.nativeLibraryDir, Executable.OVERTURE).absolutePath,
overtureProcess?.destroy() "-c", buildOvertureConfig("overture.conf"))))
overtureProcess = null
} }
} }
private val overtureProcesses = WeakHashMap<Interface, GuardedProcess>()
} }
...@@ -23,14 +23,13 @@ package com.github.shadowsocks.bg ...@@ -23,14 +23,13 @@ package com.github.shadowsocks.bg
import android.net.LocalServerSocket import android.net.LocalServerSocket
import android.net.LocalSocket import android.net.LocalSocket
import android.net.LocalSocketAddress import android.net.LocalSocketAddress
import android.util.Log import com.github.shadowsocks.utils.printLog
import com.github.shadowsocks.App.Companion.app
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
abstract class LocalSocketListener(protected val tag: String) : Thread(tag) { abstract class LocalSocketListener(protected val tag: String) : Thread(tag) {
init { init {
setUncaughtExceptionHandler(app::track) setUncaughtExceptionHandler { _, t -> printLog(t) }
} }
protected abstract val socketFile: File protected abstract val socketFile: File
...@@ -48,15 +47,14 @@ abstract class LocalSocketListener(protected val tag: String) : Thread(tag) { ...@@ -48,15 +47,14 @@ abstract class LocalSocketListener(protected val tag: String) : Thread(tag) {
localSocket.bind(LocalSocketAddress(socketFile.absolutePath, LocalSocketAddress.Namespace.FILESYSTEM)) localSocket.bind(LocalSocketAddress(socketFile.absolutePath, LocalSocketAddress.Namespace.FILESYSTEM))
LocalServerSocket(localSocket.fileDescriptor) LocalServerSocket(localSocket.fileDescriptor)
} catch (e: IOException) { } catch (e: IOException) {
Log.e(tag, "unable to bind", e) printLog(e)
return return
} }
while (running) { while (running) {
try { try {
serverSocket.accept() serverSocket.accept()
} catch (e: IOException) { } catch (e: IOException) {
Log.e(tag, "Error when accept socket", e) printLog(e)
app.track(e)
null null
}?.use(this::accept) }?.use(this::accept)
} }
......
...@@ -22,7 +22,6 @@ package com.github.shadowsocks.bg ...@@ -22,7 +22,6 @@ package com.github.shadowsocks.bg
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import android.os.IBinder
/** /**
* Shadowsocks service at its minimum. * Shadowsocks service at its minimum.
...@@ -36,7 +35,7 @@ class ProxyService : Service(), BaseService.Interface { ...@@ -36,7 +35,7 @@ class ProxyService : Service(), BaseService.Interface {
override fun createNotification(profileName: String): ServiceNotification = override fun createNotification(profileName: String): ServiceNotification =
ServiceNotification(this, profileName, "service-proxy", true) ServiceNotification(this, profileName, "service-proxy", true)
override fun onBind(intent: Intent): IBinder? = super.onBind(intent) override fun onBind(intent: Intent) = super.onBind(intent)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int = override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int =
super<BaseService.Interface>.onStartCommand(intent, flags, startId) super<BaseService.Interface>.onStartCommand(intent, flags, startId)
} }
...@@ -29,11 +29,13 @@ import android.content.Intent ...@@ -29,11 +29,13 @@ import android.content.Intent
import android.content.IntentFilter import android.content.IntentFilter
import android.os.Build import android.os.Build
import android.os.PowerManager import android.os.PowerManager
import android.support.v4.app.NotificationCompat import android.text.format.Formatter
import android.support.v4.content.ContextCompat import androidx.core.app.NotificationCompat
import com.github.shadowsocks.MainActivity import androidx.core.content.ContextCompat
import com.github.shadowsocks.R import androidx.core.content.getSystemService
import com.github.shadowsocks.Core
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.core.R
import com.github.shadowsocks.utils.Action import com.github.shadowsocks.utils.Action
import com.github.shadowsocks.utils.broadcastReceiver import com.github.shadowsocks.utils.broadcastReceiver
import java.util.* import java.util.*
...@@ -46,23 +48,21 @@ import java.util.* ...@@ -46,23 +48,21 @@ import java.util.*
*/ */
class ServiceNotification(private val service: BaseService.Interface, profileName: String, class ServiceNotification(private val service: BaseService.Interface, profileName: String,
channel: String, private val visible: Boolean = false) { channel: String, private val visible: Boolean = false) {
private val keyGuard = (service as Context).getSystemService(Context.KEYGUARD_SERVICE) as KeyguardManager private val keyGuard = (service as Context).getSystemService<KeyguardManager>()!!
private val nm by lazy { private val nm by lazy { (service as Context).getSystemService<NotificationManager>()!! }
(service as Context).getSystemService(Context.NOTIFICATION_SERVICE) as NotificationManager private val callback: IShadowsocksServiceCallback by lazy {
}
private val callback by lazy {
object : IShadowsocksServiceCallback.Stub() { object : IShadowsocksServiceCallback.Stub() {
override fun stateChanged(state: Int, profileName: String?, msg: String?) { } // ignore override fun stateChanged(state: Int, profileName: String?, msg: String?) { } // ignore
override fun trafficUpdated(profileId: Int, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) { override fun trafficUpdated(profileId: Long, txRate: Long, rxRate: Long, txTotal: Long, rxTotal: Long) {
service as Context service as Context
val txr = service.getString(R.string.speed, TrafficMonitor.formatTraffic(txRate)) val txr = service.getString(R.string.speed, Formatter.formatFileSize(service, txRate))
val rxr = service.getString(R.string.speed, TrafficMonitor.formatTraffic(rxRate)) val rxr = service.getString(R.string.speed, Formatter.formatFileSize(service, rxRate))
builder.setContentText("$txr↑\t$rxr↓") builder.setContentText("$txr↑\t$rxr↓")
style.bigText(service.getString(R.string.stat_summary).format(Locale.ENGLISH, txr, rxr, style.bigText(service.getString(R.string.stat_summary, txr, rxr,
TrafficMonitor.formatTraffic(txTotal), TrafficMonitor.formatTraffic(rxTotal))) Formatter.formatFileSize(service, txTotal), Formatter.formatFileSize(service, rxTotal)))
show() show()
} }
override fun trafficPersisted(profileId: Int) { } override fun trafficPersisted(profileId: Long) { }
} }
} }
private val lockReceiver = broadcastReceiver { _, intent -> update(intent.action) } private val lockReceiver = broadcastReceiver { _, intent -> update(intent.action) }
...@@ -73,17 +73,17 @@ class ServiceNotification(private val service: BaseService.Interface, profileNam ...@@ -73,17 +73,17 @@ class ServiceNotification(private val service: BaseService.Interface, profileNam
.setColor(ContextCompat.getColor(service, R.color.material_primary_500)) .setColor(ContextCompat.getColor(service, R.color.material_primary_500))
.setTicker(service.getString(R.string.forward_success)) .setTicker(service.getString(R.string.forward_success))
.setContentTitle(profileName) .setContentTitle(profileName)
.setContentIntent(MainActivity.pendingIntent(service)) .setContentIntent(Core.configureIntent(service))
.setSmallIcon(R.drawable.ic_service_active) .setSmallIcon(R.drawable.ic_service_active)
private val style = NotificationCompat.BigTextStyle(builder) private val style = NotificationCompat.BigTextStyle(builder).bigText("")
private var isVisible = true private var isVisible = true
init { init {
service as Context service as Context
if (Build.VERSION.SDK_INT < 24) builder.addAction(R.drawable.ic_navigation_close, if (Build.VERSION.SDK_INT < 24) builder.addAction(R.drawable.ic_navigation_close,
service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, Intent(Action.CLOSE), 0)) service.getString(R.string.stop), PendingIntent.getBroadcast(service, 0, Intent(Action.CLOSE), 0))
val power = service.getSystemService(Context.POWER_SERVICE) as PowerManager update(if (service.getSystemService<PowerManager>()?.isInteractive != false)
update(if (power.isInteractive) Intent.ACTION_SCREEN_ON else Intent.ACTION_SCREEN_OFF, true) Intent.ACTION_SCREEN_ON else Intent.ACTION_SCREEN_OFF, true)
val screenFilter = IntentFilter() val screenFilter = IntentFilter()
screenFilter.addAction(Intent.ACTION_SCREEN_ON) screenFilter.addAction(Intent.ACTION_SCREEN_ON)
screenFilter.addAction(Intent.ACTION_SCREEN_OFF) screenFilter.addAction(Intent.ACTION_SCREEN_OFF)
...@@ -91,14 +91,14 @@ class ServiceNotification(private val service: BaseService.Interface, profileNam ...@@ -91,14 +91,14 @@ class ServiceNotification(private val service: BaseService.Interface, profileNam
service.registerReceiver(lockReceiver, screenFilter) service.registerReceiver(lockReceiver, screenFilter)
} }
private fun update(action: String, forceShow: Boolean = false) { private fun update(action: String?, forceShow: Boolean = false) {
if (forceShow || service.data.state == BaseService.CONNECTED) when (action) { if (forceShow || service.data.state == BaseService.CONNECTED) when (action) {
Intent.ACTION_SCREEN_OFF -> { Intent.ACTION_SCREEN_OFF -> {
setVisible(false, forceShow) setVisible(false, forceShow)
unregisterCallback() // unregister callback to save battery unregisterCallback() // unregister callback to save battery
} }
Intent.ACTION_SCREEN_ON -> { Intent.ACTION_SCREEN_ON -> {
setVisible(visible && !keyGuard.inKeyguardRestrictedInputMode(), forceShow) setVisible(visible && !keyGuard.isKeyguardLocked, forceShow)
service.data.binder.registerCallback(callback) service.data.binder.registerCallback(callback)
service.data.binder.startListeningForBandwidth(callback) service.data.binder.startListeningForBandwidth(callback)
callbackRegistered = true callbackRegistered = true
......
...@@ -21,9 +21,6 @@ ...@@ -21,9 +21,6 @@
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import android.os.SystemClock import android.os.SystemClock
import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.R
import java.text.DecimalFormat
object TrafficMonitor { object TrafficMonitor {
// Bytes per second // Bytes per second
...@@ -41,19 +38,6 @@ object TrafficMonitor { ...@@ -41,19 +38,6 @@ object TrafficMonitor {
@Volatile @Volatile
private var dirty = true private var dirty = true
private val units = arrayOf("KB", "MB", "GB", "TB", "PB", "EB", "ZB", "YB", "BB", "NB", "DB", "CB")
private val numberFormat = DecimalFormat("@@@")
fun formatTraffic(size: Long): String {
var n = size.toDouble()
var i = -1
while (n >= 999.5) {
n /= 1024
++i
}
return if (i < 0) "$size ${app.resources.getQuantityString(R.plurals.bytes, size.toInt())}"
else "${numberFormat.format(n)} ${units[i]}"
}
fun updateRate(): Boolean { fun updateRate(): Boolean {
val now = SystemClock.elapsedRealtime() val now = SystemClock.elapsedRealtime()
val delta = now - timestampLast val delta = now - timestampLast
......
...@@ -21,15 +21,15 @@ ...@@ -21,15 +21,15 @@
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import android.net.LocalSocket import android.net.LocalSocket
import android.util.Log import com.github.shadowsocks.Core
import com.github.shadowsocks.App.Companion.app import com.github.shadowsocks.utils.printLog
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.nio.ByteBuffer import java.nio.ByteBuffer
import java.nio.ByteOrder import java.nio.ByteOrder
class TrafficMonitorThread : LocalSocketListener("TrafficMonitorThread") { class TrafficMonitorThread : LocalSocketListener("TrafficMonitorThread") {
override val socketFile = File(app.deviceContext.filesDir, "stat_path") override val socketFile = File(Core.deviceStorage.noBackupFilesDir, "stat_path")
override fun accept(socket: LocalSocket) { override fun accept(socket: LocalSocket) {
try { try {
...@@ -38,8 +38,7 @@ class TrafficMonitorThread : LocalSocketListener("TrafficMonitorThread") { ...@@ -38,8 +38,7 @@ class TrafficMonitorThread : LocalSocketListener("TrafficMonitorThread") {
val stat = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN) val stat = ByteBuffer.wrap(buffer).order(ByteOrder.LITTLE_ENDIAN)
TrafficMonitor.update(stat.getLong(0), stat.getLong(8)) TrafficMonitor.update(stat.getLong(0), stat.getLong(8))
} catch (e: IOException) { } catch (e: IOException) {
Log.e(tag, "Error when recv traffic stat", e) printLog(e)
app.track(e)
} }
} }
} }
...@@ -22,8 +22,7 @@ package com.github.shadowsocks.bg ...@@ -22,8 +22,7 @@ package com.github.shadowsocks.bg
import android.app.Service import android.app.Service
import android.content.Intent import android.content.Intent
import android.os.IBinder import com.github.shadowsocks.Core
import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import java.io.File import java.io.File
...@@ -36,26 +35,24 @@ class TransproxyService : Service(), LocalDnsService.Interface { ...@@ -36,26 +35,24 @@ class TransproxyService : Service(), LocalDnsService.Interface {
override fun createNotification(profileName: String): ServiceNotification = override fun createNotification(profileName: String): ServiceNotification =
ServiceNotification(this, profileName, "service-transproxy", true) ServiceNotification(this, profileName, "service-transproxy", true)
override fun onBind(intent: Intent): IBinder? = super.onBind(intent) override fun onBind(intent: Intent) = super.onBind(intent)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int = override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int =
super<LocalDnsService.Interface>.onStartCommand(intent, flags, startId) super<LocalDnsService.Interface>.onStartCommand(intent, flags, startId)
private var sstunnelProcess: GuardedProcess? = null
private var redsocksProcess: GuardedProcess? = null
private fun startDNSTunnel() { private fun startDNSTunnel() {
val cmd = arrayListOf(File(applicationInfo.nativeLibraryDir, Executable.SS_TUNNEL).absolutePath, val cmd = arrayListOf(File(applicationInfo.nativeLibraryDir, Executable.SS_TUNNEL).absolutePath,
"-t", "10", "-t", "10",
"-b", "127.0.0.1", "-b", DataStore.listenAddress,
"-u", "-u",
"-l", DataStore.portLocalDns.toString(), // ss-tunnel listens on the same port as overture "-l", DataStore.portLocalDns.toString(), // ss-tunnel listens on the same port as overture
"-L", data.profile!!.remoteDns.split(",").first().trim() + ":53", "-L", data.profile!!.remoteDns.split(",").first().trim() + ":53",
"-c", data.shadowsocksConfigFile!!.absolutePath) // config is already built by BaseService.Interface "-c", data.shadowsocksConfigFile!!.absolutePath) // config is already built by BaseService.Interface
sstunnelProcess = GuardedProcess(cmd).start() if (DataStore.tcpFastOpen) cmd += "--fast-open"
data.processes.start(cmd)
} }
private fun startRedsocksDaemon() { private fun startRedsocksDaemon() {
File(app.deviceContext.filesDir, "redsocks.conf").writeText("""base { File(Core.deviceStorage.noBackupFilesDir, "redsocks.conf").writeText("""base {
log_debug = off; log_debug = off;
log_info = off; log_info = off;
log = stderr; log = stderr;
...@@ -63,17 +60,15 @@ class TransproxyService : Service(), LocalDnsService.Interface { ...@@ -63,17 +60,15 @@ class TransproxyService : Service(), LocalDnsService.Interface {
redirector = iptables; redirector = iptables;
} }
redsocks { redsocks {
local_ip = 127.0.0.1; local_ip = ${DataStore.listenAddress};
local_port = ${DataStore.portTransproxy}; local_port = ${DataStore.portTransproxy};
ip = 127.0.0.1; ip = 127.0.0.1;
port = ${DataStore.portProxy}; port = ${DataStore.portProxy};
type = socks5; type = socks5;
} }
""") """)
redsocksProcess = GuardedProcess(arrayListOf( data.processes.start(listOf(
File(applicationInfo.nativeLibraryDir, Executable.REDSOCKS).absolutePath, File(applicationInfo.nativeLibraryDir, Executable.REDSOCKS).absolutePath, "-c", "redsocks.conf"))
"-c", "redsocks.conf")
).start()
} }
override fun startNativeProcesses() { override fun startNativeProcesses() {
...@@ -81,12 +76,4 @@ redsocks { ...@@ -81,12 +76,4 @@ redsocks {
super.startNativeProcesses() super.startNativeProcesses()
if (data.profile!!.udpdns) startDNSTunnel() if (data.profile!!.udpdns) startDNSTunnel()
} }
override fun killProcesses() {
super.killProcesses()
sstunnelProcess?.destroy()
sstunnelProcess = null
redsocksProcess?.destroy()
redsocksProcess = null
}
} }
...@@ -22,24 +22,23 @@ package com.github.shadowsocks.bg ...@@ -22,24 +22,23 @@ package com.github.shadowsocks.bg
import android.annotation.TargetApi import android.annotation.TargetApi
import android.app.Service import android.app.Service
import android.content.Context
import android.content.Intent import android.content.Intent
import android.content.pm.PackageManager import android.content.pm.PackageManager
import android.net.* import android.net.*
import android.os.Build import android.os.Build
import android.os.IBinder
import android.os.ParcelFileDescriptor import android.os.ParcelFileDescriptor
import android.support.v4.os.BuildCompat import android.system.ErrnoException
import android.util.Log import android.system.Os
import com.github.shadowsocks.App.Companion.app import androidx.core.content.getSystemService
import com.github.shadowsocks.Core
import com.github.shadowsocks.JniHelper import com.github.shadowsocks.JniHelper
import com.github.shadowsocks.MainActivity
import com.github.shadowsocks.R
import com.github.shadowsocks.VpnRequestActivity import com.github.shadowsocks.VpnRequestActivity
import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.core.R
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Subnet import com.github.shadowsocks.utils.Subnet
import com.github.shadowsocks.utils.parseNumericAddress import com.github.shadowsocks.utils.parseNumericAddress
import com.github.shadowsocks.utils.printLog
import java.io.File import java.io.File
import java.io.FileDescriptor import java.io.FileDescriptor
import java.io.IOException import java.io.IOException
...@@ -50,9 +49,12 @@ import android.net.VpnService as BaseVpnService ...@@ -50,9 +49,12 @@ import android.net.VpnService as BaseVpnService
class VpnService : BaseVpnService(), LocalDnsService.Interface { class VpnService : BaseVpnService(), LocalDnsService.Interface {
companion object { companion object {
private const val VPN_MTU = 1500 private const val VPN_MTU = 1500
private const val PRIVATE_VLAN = "26.26.26.%s" private const val PRIVATE_VLAN = "172.19.0.%s"
private const val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s" private const val PRIVATE_VLAN6 = "fdfe:dcba:9876::%s"
/**
* https://android.googlesource.com/platform/prebuilts/runtime/+/94fec32/appcompat/hiddenapi-light-greylist.txt#9466
*/
private val getInt: Method = FileDescriptor::class.java.getDeclaredMethod("getInt$") private val getInt: Method = FileDescriptor::class.java.getDeclaredMethod("getInt$")
/** /**
...@@ -72,35 +74,27 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -72,35 +74,27 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
} }
private inner class ProtectWorker : LocalSocketListener("ShadowsocksVpnThread") { private inner class ProtectWorker : LocalSocketListener("ShadowsocksVpnThread") {
override val socketFile: File = File(app.deviceContext.filesDir, "protect_path") override val socketFile: File = File(Core.deviceStorage.noBackupFilesDir, "protect_path")
override fun accept(socket: LocalSocket) { override fun accept(socket: LocalSocket) {
var success = false
try { try {
socket.inputStream.read() socket.inputStream.read()
val fd = socket.ancillaryFileDescriptors!!.single()!! val fd = socket.ancillaryFileDescriptors!!.single()!!
val fdInt = getInt.invoke(fd) as Int socket.outputStream.write(if (try {
try {
val network = underlyingNetwork val network = underlyingNetwork
success = if (network != null && Build.VERSION.SDK_INT >= 23) { if (network != null && Build.VERSION.SDK_INT >= 23) {
network.bindSocket(fd) network.bindSocket(fd)
true true
} else protect(fdInt) } else protect(getInt.invoke(fd) as Int)
} catch (e: Exception) {
Log.e(tag, "Error when protect socket", e)
app.track(e)
} finally { } finally {
JniHelper.close(fdInt) // Trick to close file decriptor
}
} catch (e: Exception) {
Log.e(tag, "Error when receiving ancillary fd", e)
app.track(e)
}
try { try {
socket.outputStream.write(if (success) 0 else 1) Os.close(fd)
} catch (e: ErrnoException) {
printLog(e)
}
}) 0 else 1)
} catch (e: IOException) { } catch (e: IOException) {
Log.e(tag, "Error when returning result in protect", e) printLog(e)
app.track(e)
} }
} }
} }
...@@ -116,7 +110,6 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -116,7 +110,6 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
private var conn: ParcelFileDescriptor? = null private var conn: ParcelFileDescriptor? = null
private var worker: ProtectWorker? = null private var worker: ProtectWorker? = null
private var tun2socksProcess: GuardedProcess? = null
private var underlyingNetwork: Network? = null private var underlyingNetwork: Network? = null
@TargetApi(28) @TargetApi(28)
set(value) { set(value) {
...@@ -124,7 +117,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -124,7 +117,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
field = value field = value
} }
private val connectivity by lazy { getSystemService(Context.CONNECTIVITY_SERVICE) as ConnectivityManager } private val connectivity by lazy { getSystemService<ConnectivityManager>()!! }
@TargetApi(28) @TargetApi(28)
private val defaultNetworkCallback = object : ConnectivityManager.NetworkCallback() { private val defaultNetworkCallback = object : ConnectivityManager.NetworkCallback() {
override fun onAvailable(network: Network) { override fun onAvailable(network: Network) {
...@@ -140,7 +133,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -140,7 +133,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
} }
private var listeningForDefaultNetwork = false private var listeningForDefaultNetwork = false
override fun onBind(intent: Intent): IBinder? = when (intent.action) { override fun onBind(intent: Intent) = when (intent.action) {
SERVICE_INTERFACE -> super<BaseVpnService>.onBind(intent) SERVICE_INTERFACE -> super<BaseVpnService>.onBind(intent)
else -> super<LocalDnsService.Interface>.onBind(intent) else -> super<LocalDnsService.Interface>.onBind(intent)
} }
...@@ -155,8 +148,6 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -155,8 +148,6 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
worker?.stopThread() worker?.stopThread()
worker = null worker = null
super.killProcesses() super.killProcesses()
tun2socksProcess?.destroy()
tun2socksProcess = null
conn?.close() conn?.close()
conn = null conn = null
} }
...@@ -178,8 +169,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -178,8 +169,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
super.startNativeProcesses() super.startNativeProcesses()
val fd = startVpn() sendFd(startVpn())
if (!sendFd(fd)) throw IOException("sendFd failed")
} }
override fun buildAdditionalArguments(cmd: ArrayList<String>): ArrayList<String> { override fun buildAdditionalArguments(cmd: ArrayList<String>): ArrayList<String> {
...@@ -190,7 +180,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -190,7 +180,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
private fun startVpn(): Int { private fun startVpn(): Int {
val profile = data.profile!! val profile = data.profile!!
val builder = Builder() val builder = Builder()
.setConfigureIntent(MainActivity.pendingIntent(this)) .setConfigureIntent(Core.configureIntent(this))
.setSession(profile.formattedName) .setSession(profile.formattedName)
.setMtu(VPN_MTU) .setMtu(VPN_MTU)
.addAddress(PRIVATE_VLAN.format(Locale.ENGLISH, "1"), 24) .addAddress(PRIVATE_VLAN.format(Locale.ENGLISH, "1"), 24)
...@@ -211,7 +201,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -211,7 +201,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
if (profile.bypass) builder.addDisallowedApplication(it) if (profile.bypass) builder.addDisallowedApplication(it)
else builder.addAllowedApplication(it) else builder.addAllowedApplication(it)
} catch (ex: PackageManager.NameNotFoundException) { } catch (ex: PackageManager.NameNotFoundException) {
Log.e(tag, "Invalid package name", ex) printLog(ex)
} }
} }
if (!profile.bypass) builder.addAllowedApplication(me) if (!profile.bypass) builder.addAllowedApplication(me)
...@@ -233,7 +223,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -233,7 +223,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
this.conn = conn this.conn = conn
val fd = conn.fd val fd = conn.fd
if (BuildCompat.isAtLeastP()) { if (Build.VERSION.SDK_INT >= 28) {
// we want REQUEST here instead of LISTEN // we want REQUEST here instead of LISTEN
connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback) connectivity.requestNetwork(defaultNetworkRequest, defaultNetworkCallback)
listeningForDefaultNetwork = true listeningForDefaultNetwork = true
...@@ -242,7 +232,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -242,7 +232,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
val cmd = arrayListOf(File(applicationInfo.nativeLibraryDir, Executable.TUN2SOCKS).absolutePath, val cmd = arrayListOf(File(applicationInfo.nativeLibraryDir, Executable.TUN2SOCKS).absolutePath,
"--netif-ipaddr", PRIVATE_VLAN.format(Locale.ENGLISH, "2"), "--netif-ipaddr", PRIVATE_VLAN.format(Locale.ENGLISH, "2"),
"--netif-netmask", "255.255.255.0", "--netif-netmask", "255.255.255.0",
"--socks-server-addr", "127.0.0.1:${DataStore.portProxy}", "--socks-server-addr", "${DataStore.listenAddress}:${DataStore.portProxy}",
"--tunfd", fd.toString(), "--tunfd", fd.toString(),
"--tunmtu", VPN_MTU.toString(), "--tunmtu", VPN_MTU.toString(),
"--sock-path", "sock_path", "--sock-path", "sock_path",
...@@ -256,19 +246,27 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface { ...@@ -256,19 +246,27 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
cmd += "--dnsgw" cmd += "--dnsgw"
cmd += "127.0.0.1:${DataStore.portLocalDns}" cmd += "127.0.0.1:${DataStore.portLocalDns}"
} }
tun2socksProcess = GuardedProcess(cmd).start { sendFd(fd) } data.processes.start(cmd, onRestartCallback = {
try {
sendFd(fd)
} catch (e: ErrnoException) {
stopRunner(true, e.message)
}
})
return fd return fd
} }
private fun sendFd(fd: Int): Boolean { private fun sendFd(fd: Int) {
if (fd != -1) { if (fd == -1) throw IOException("Invalid fd (-1)")
var tries = 0 var tries = 0
while (tries < 10) { val path = File(Core.deviceStorage.noBackupFilesDir, "sock_path").absolutePath
Thread.sleep(30L shl tries) while (true) try {
if (JniHelper.sendFd(fd, File(app.deviceContext.filesDir, "sock_path").absolutePath) != -1) return true Thread.sleep(50L shl tries)
JniHelper.sendFd(fd, path)
return
} catch (e: ErrnoException) {
if (tries > 5) throw e
tries += 1 tries += 1
} }
} }
return false
}
} }
...@@ -20,37 +20,54 @@ ...@@ -20,37 +20,54 @@
package com.github.shadowsocks.database package com.github.shadowsocks.database
import com.j256.ormlite.field.DataType import androidx.room.*
import com.j256.ormlite.field.DatabaseField
import java.io.ByteArrayOutputStream import java.io.ByteArrayOutputStream
import java.nio.ByteBuffer import java.nio.ByteBuffer
@Entity
class KeyValuePair() { class KeyValuePair() {
companion object { companion object {
const val TYPE_UNINITIALIZED = 0 const val TYPE_UNINITIALIZED = 0
const val TYPE_BOOLEAN = 1 const val TYPE_BOOLEAN = 1
const val TYPE_FLOAT = 2 const val TYPE_FLOAT = 2
@Deprecated("Use TYPE_LONG.")
const val TYPE_INT = 3 const val TYPE_INT = 3
const val TYPE_LONG = 4 const val TYPE_LONG = 4
const val TYPE_STRING = 5 const val TYPE_STRING = 5
const val TYPE_STRING_SET = 6 const val TYPE_STRING_SET = 6
} }
@DatabaseField(id = true) @androidx.room.Dao
var key: String? = "" interface Dao {
@DatabaseField @Query("SELECT * FROM `KeyValuePair` WHERE `key` = :key")
operator fun get(key: String): KeyValuePair?
@Insert(onConflict = OnConflictStrategy.REPLACE)
fun put(value: KeyValuePair): Long
@Query("DELETE FROM `KeyValuePair` WHERE `key` = :key")
fun delete(key: String): Int
}
@PrimaryKey
var key: String = ""
var valueType: Int = TYPE_UNINITIALIZED var valueType: Int = TYPE_UNINITIALIZED
@DatabaseField(dataType = DataType.BYTE_ARRAY)
var value: ByteArray = ByteArray(0) var value: ByteArray = ByteArray(0)
val boolean: Boolean? val boolean: Boolean?
get() = if (valueType == TYPE_BOOLEAN) ByteBuffer.wrap(value).get() != 0.toByte() else null get() = if (valueType == TYPE_BOOLEAN) ByteBuffer.wrap(value).get() != 0.toByte() else null
val float: Float? val float: Float?
get() = if (valueType == TYPE_FLOAT) ByteBuffer.wrap(value).float else null get() = if (valueType == TYPE_FLOAT) ByteBuffer.wrap(value).float else null
@Suppress("DEPRECATION")
@Deprecated("Use long.", ReplaceWith("long"))
val int: Int? val int: Int?
get() = if (valueType == TYPE_INT) ByteBuffer.wrap(value).int else null get() = if (valueType == TYPE_INT) ByteBuffer.wrap(value).int else null
val long: Long? val long: Long? get() = when (valueType) {
get() = if (valueType == TYPE_LONG) ByteBuffer.wrap(value).long else null @Suppress("DEPRECATION")
TYPE_INT -> ByteBuffer.wrap(value).int.toLong()
TYPE_LONG -> ByteBuffer.wrap(value).long
else -> null
}
val string: String? val string: String?
get() = if (valueType == TYPE_STRING) String(value) else null get() = if (valueType == TYPE_STRING) String(value) else null
val stringSet: Set<String>? val stringSet: Set<String>?
...@@ -65,7 +82,8 @@ class KeyValuePair() { ...@@ -65,7 +82,8 @@ class KeyValuePair() {
result result
} else null } else null
constructor(key: String?) : this() { @Ignore
constructor(key: String) : this() {
this.key = key this.key = key
} }
...@@ -80,6 +98,8 @@ class KeyValuePair() { ...@@ -80,6 +98,8 @@ class KeyValuePair() {
this.value = ByteBuffer.allocate(4).putFloat(value).array() this.value = ByteBuffer.allocate(4).putFloat(value).array()
return this return this
} }
@Suppress("DEPRECATION")
@Deprecated("Use long.")
fun put(value: Int): KeyValuePair { fun put(value: Int): KeyValuePair {
valueType = TYPE_INT valueType = TYPE_INT
this.value = ByteBuffer.allocate(4).putInt(value).array() this.value = ByteBuffer.allocate(4).putInt(value).array()
......
...@@ -20,42 +20,39 @@ ...@@ -20,42 +20,39 @@
package com.github.shadowsocks.database package com.github.shadowsocks.database
import android.database.sqlite.SQLiteDatabase import androidx.room.Database
import com.github.shadowsocks.App.Companion.app import androidx.room.Room
import com.github.shadowsocks.acl.Acl import androidx.room.RoomDatabase
import androidx.sqlite.db.SupportSQLiteDatabase
import com.github.shadowsocks.Core.app
import com.github.shadowsocks.database.migration.RecreateSchemaMigration
import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.Key
import com.j256.ormlite.android.AndroidDatabaseConnection
import com.j256.ormlite.android.apptools.OrmLiteSqliteOpenHelper
import com.j256.ormlite.dao.Dao
import com.j256.ormlite.support.ConnectionSource
import com.j256.ormlite.table.TableUtils
object PublicDatabase : OrmLiteSqliteOpenHelper(app.deviceContext, Key.DB_PUBLIC, null, 2) { @Database(entities = [Profile::class, KeyValuePair::class], version = 26)
@Suppress("UNCHECKED_CAST") abstract class PrivateDatabase : RoomDatabase() {
val kvPairDao: Dao<KeyValuePair, String?> by lazy { getDao(KeyValuePair::class.java) as Dao<KeyValuePair, String?> } companion object {
private val instance by lazy {
override fun onCreate(database: SQLiteDatabase?, connectionSource: ConnectionSource?) { Room.databaseBuilder(app, PrivateDatabase::class.java, Key.DB_PROFILE)
TableUtils.createTable(connectionSource, KeyValuePair::class.java) .addMigrations(
Migration26
)
.fallbackToDestructiveMigration()
.allowMainThreadQueries()
.build()
} }
override fun onUpgrade(database: SQLiteDatabase?, connectionSource: ConnectionSource?, val profileDao get() = instance.profileDao()
oldVersion: Int, newVersion: Int) { val kvPairDao get() = instance.keyValuePairDao()
if (oldVersion < 1) {
PrivateDatabase.kvPairDao.queryBuilder().where().`in`("key",
Key.id, Key.tfo, Key.serviceMode, Key.portProxy, Key.portLocalDns, Key.portTransproxy).query()
.forEach { kvPairDao.createOrUpdate(it) }
} }
abstract fun profileDao(): Profile.Dao
abstract fun keyValuePairDao(): KeyValuePair.Dao
if (oldVersion < 2) { private object Migration26 : RecreateSchemaMigration(25, 26, "Profile",
kvPairDao.createOrUpdate(KeyValuePair(Acl.CUSTOM_RULES).put(Acl().fromId(Acl.CUSTOM_RULES).toString())) "(`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `method` TEXT NOT NULL, `route` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `proxyApps` INTEGER NOT NULL, `bypass` INTEGER NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `individual` TEXT NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL, `plugin` TEXT)",
} "`id`, `name`, `host`, `remotePort`, `password`, `method`, `route`, `remoteDns`, `proxyApps`, `bypass`, `udpdns`, `ipv6`, `individual`, `tx`, `rx`, `userOrder`, `plugin`") {
override fun migrate(database: SupportSQLiteDatabase) {
super.migrate(database)
PublicDatabase.Migration3.migrate(database)
} }
override fun onDowngrade(db: SQLiteDatabase?, oldVersion: Int, newVersion: Int) {
val connection = AndroidDatabaseConnection(db, true)
connectionSource.saveSpecialConnection(connection)
TableUtils.dropTable<KeyValuePair, String?>(connectionSource, KeyValuePair::class.java, true)
onCreate(db, connectionSource)
connectionSource.clearSpecialConnection(connection)
} }
} }
...@@ -21,11 +21,9 @@ ...@@ -21,11 +21,9 @@
package com.github.shadowsocks.database package com.github.shadowsocks.database
import android.database.sqlite.SQLiteCantOpenDatabaseException import android.database.sqlite.SQLiteCantOpenDatabaseException
import android.util.Log
import com.github.shadowsocks.App.Companion.app
import com.github.shadowsocks.ProfilesFragment
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.DirectBoot import com.github.shadowsocks.utils.DirectBoot
import com.github.shadowsocks.utils.printLog
import java.io.IOException import java.io.IOException
import java.sql.SQLException import java.sql.SQLException
...@@ -34,27 +32,18 @@ import java.sql.SQLException ...@@ -34,27 +32,18 @@ import java.sql.SQLException
* to ensure we are in a consistent state. * to ensure we are in a consistent state.
*/ */
object ProfileManager { object ProfileManager {
private const val TAG = "ProfileManager" interface Listener {
fun onAdd(profile: Profile)
fun onRemove(profileId: Long)
}
var listener: Listener? = null
@Throws(SQLException::class) @Throws(SQLException::class)
fun createProfile(p: Profile? = null): Profile { fun createProfile(profile: Profile = Profile()): Profile {
val profile = p ?: Profile()
profile.id = 0 profile.id = 0
val oldProfile = app.currentProfile profile.userOrder = PrivateDatabase.profileDao.nextOrder() ?: 0
if (oldProfile != null) { profile.id = PrivateDatabase.profileDao.create(profile)
// Copy Feature Settings from old profile listener?.onAdd(profile)
profile.route = oldProfile.route
profile.ipv6 = oldProfile.ipv6
profile.proxyApps = oldProfile.proxyApps
profile.bypass = oldProfile.bypass
profile.individual = oldProfile.individual
profile.udpdns = oldProfile.udpdns
}
val last = PrivateDatabase.profileDao.queryRaw(PrivateDatabase.profileDao.queryBuilder()
.selectRaw("MAX(userOrder)").prepareStatementString()).firstResult
if (last != null && last.size == 1 && last[0] != null) profile.userOrder = last[0].toLong() + 1
PrivateDatabase.profileDao.createOrUpdate(profile)
ProfilesFragment.instance?.profilesAdapter?.add(profile)
return profile return profile
} }
...@@ -62,42 +51,51 @@ object ProfileManager { ...@@ -62,42 +51,51 @@ object ProfileManager {
* Note: It's caller's responsibility to update DirectBoot profile if necessary. * Note: It's caller's responsibility to update DirectBoot profile if necessary.
*/ */
@Throws(SQLException::class) @Throws(SQLException::class)
fun updateProfile(profile: Profile) = PrivateDatabase.profileDao.update(profile) fun updateProfile(profile: Profile) = check(PrivateDatabase.profileDao.update(profile) == 1)
@Throws(IOException::class) @Throws(IOException::class)
fun getProfile(id: Int): Profile? = try { fun getProfile(id: Long): Profile? = try {
PrivateDatabase.profileDao.queryForId(id) PrivateDatabase.profileDao[id]
} catch (ex: SQLiteCantOpenDatabaseException) {
throw IOException(ex)
} catch (ex: SQLException) { } catch (ex: SQLException) {
if (ex.cause is SQLiteCantOpenDatabaseException) throw IOException(ex) printLog(ex)
Log.e(TAG, "getProfile", ex)
app.track(ex)
null null
} }
@Throws(SQLException::class) @Throws(SQLException::class)
fun delProfile(id: Int) { fun delProfile(id: Long) {
PrivateDatabase.profileDao.deleteById(id) check(PrivateDatabase.profileDao.delete(id) == 1)
ProfilesFragment.instance?.profilesAdapter?.removeId(id) listener?.onRemove(id)
if (id == DataStore.profileId && DataStore.directBootAware) DirectBoot.clean() if (id == DataStore.profileId && DataStore.directBootAware) DirectBoot.clean()
} }
@Throws(SQLException::class)
fun clear() = PrivateDatabase.profileDao.deleteAll().also {
// listener is not called since this won't be used in mobile submodule
DirectBoot.clean()
}
@Throws(IOException::class) @Throws(IOException::class)
fun getFirstProfile(): Profile? = try { fun ensureNotEmpty() {
PrivateDatabase.profileDao.query(PrivateDatabase.profileDao.queryBuilder().limit(1L).prepare()).singleOrNull() val nonEmpty = try {
PrivateDatabase.profileDao.isNotEmpty()
} catch (ex: SQLiteCantOpenDatabaseException) {
throw IOException(ex)
} catch (ex: SQLException) { } catch (ex: SQLException) {
if (ex.cause is SQLiteCantOpenDatabaseException) throw IOException(ex) printLog(ex)
Log.e(TAG, "getFirstProfile", ex) false
app.track(ex) }
null if (!nonEmpty) DataStore.profileId = createProfile().id
} }
@Throws(IOException::class) @Throws(IOException::class)
fun getAllProfiles(): List<Profile>? = try { fun getAllProfiles(): List<Profile>? = try {
PrivateDatabase.profileDao.query(PrivateDatabase.profileDao.queryBuilder().orderBy("userOrder", true).prepare()) PrivateDatabase.profileDao.list()
} catch (ex: SQLiteCantOpenDatabaseException) {
throw IOException(ex)
} catch (ex: SQLException) { } catch (ex: SQLException) {
if (ex.cause is SQLiteCantOpenDatabaseException) throw IOException(ex) printLog(ex)
Log.e(TAG, "getAllProfiles", ex)
app.track(ex)
null null
} }
} }
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* 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/>. *
* *
*******************************************************************************/
package com.github.shadowsocks.database
import androidx.room.Database
import androidx.room.Room
import androidx.room.RoomDatabase
import com.github.shadowsocks.Core
import com.github.shadowsocks.database.migration.RecreateSchemaMigration
import com.github.shadowsocks.utils.Key
@Database(entities = [KeyValuePair::class], version = 3)
abstract class PublicDatabase : RoomDatabase() {
companion object {
private val instance by lazy {
Room.databaseBuilder(Core.deviceStorage, PublicDatabase::class.java, Key.DB_PUBLIC)
.allowMainThreadQueries()
.addMigrations(
Migration3
)
.fallbackToDestructiveMigration()
.build()
}
val kvPairDao get() = instance.keyValuePairDao()
}
abstract fun keyValuePairDao(): KeyValuePair.Dao
internal object Migration3 : RecreateSchemaMigration(2, 3, "KeyValuePair",
"(`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))",
"`key`, `valueType`, `value`")
}
package com.github.shadowsocks.plugin package com.github.shadowsocks.plugin
import com.github.shadowsocks.App.Companion.app import com.github.shadowsocks.Core.app
object NoPlugin : Plugin() { object NoPlugin : Plugin() {
override val id: String get() = "" override val id: String get() = ""
override val label: CharSequence get() = app.getText(com.github.shadowsocks.R.string.plugin_disabled) override val label: CharSequence get() = app.getText(com.github.shadowsocks.core.R.string.plugin_disabled)
} }
This diff is collapsed.
This diff is collapsed.
Subproject commit 58f8a8883e51e5cb97391c4b6733ce255bf11f95 Subproject commit 9965ce070a5330602a71e97fdd41213e1fdd8d6d
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
This diff is collapsed.
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment