Unverified Commit 66f01b69 authored by Max Lv's avatar Max Lv Committed by GitHub

Merge pull request #2454 from shadowsocks/rust

Integrate shadowsocks-rust
parents 2f6319de d7b8ef6a
......@@ -3,7 +3,7 @@ jobs:
build:
working_directory: ~/code
docker:
- image: circleci/android:api-29-ndk
- image: shadowsocks/android-ndk-go:v1.0
environment:
GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy="in-process"
steps:
......@@ -13,7 +13,7 @@ jobs:
key: jars-{{ checksum "build.gradle" }}
- run:
name: Run Build and Tests
command: ./gradlew assembleDebug check
command: ./gradlew assembleDebug check -PCARGO_PROFILE=debug
- save_cache:
paths:
- ~/.gradle
......
[submodule "core/src/main/jni/shadowsocks-libev"]
path = core/src/main/jni/shadowsocks-libev
url = https://github.com/shadowsocks/shadowsocks-libev
[submodule "core/src/main/jni/badvpn"]
path = core/src/main/jni/badvpn
url = https://github.com/shadowsocks/badvpn.git
......@@ -17,19 +14,6 @@
path = core/src/main/jni/redsocks
url = https://github.com/shadowsocks/redsocks.git
branch = shadowsocks-android
[submodule "core/src/main/jni/mbedtls"]
path = core/src/main/jni/mbedtls
url = https://github.com/ARMmbed/mbedtls
[submodule "core/src/main/jni/pcre"]
path = core/src/main/jni/pcre
url = https://android.googlesource.com/platform/external/pcre
[submodule "core/src/main/jni/libsodium"]
path = core/src/main/jni/libsodium
url = https://github.com/jedisct1/libsodium.git
branch = stable
[submodule "core/src/main/jni/libev"]
path = core/src/main/jni/libev
url = https://git.lighttpd.net/libev.git
[submodule "core/src/main/jni/re2"]
path = core/src/main/jni/re2
url = https://github.com/google/re2.git
[submodule "core/src/main/rust/shadowsocks-rust"]
path = core/src/main/rust/shadowsocks-rust
url = https://github.com/madeye/shadowsocks-rust
......@@ -45,14 +45,11 @@ If you are interested in contributing or getting involved with this project, ple
<ul>
<li>redsocks: <a href="https://github.com/shadowsocks/redsocks/blob/shadowsocks-android/README">APL 2.0</a></li>
<li>mbed TLS: <a href="https://github.com/ARMmbed/mbedtls/blob/development/LICENSE">APL 2.0</a></li>
<li>libevent: <a href="https://github.com/shadowsocks/libevent/blob/master/LICENSE">BSD</a></li>
<li>tun2socks: <a href="https://github.com/shadowsocks/badvpn/blob/shadowsocks-android/COPYING">BSD</a></li>
<li>pcre: <a href="https://android.googlesource.com/platform/external/pcre/+/master/dist2/LICENCE">BSD</a></li>
<li>libancillary: <a href="https://github.com/shadowsocks/libancillary/blob/shadowsocks-android/COPYING">BSD</a></li>
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a></li>
<li>libev: <a href="https://github.com/shadowsocks/libev/blob/master/LICENSE">GPLv2</a></li>
<li>shadowsocks-rust: <a href="https://github.com/shadowsocks/shadowsocks-rust/blob/master/LICENSE">MIT</a></li>
<li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</a></li>
<li>OpenSSL: <a href="https://www.openssl.org/source/license-openssl-ssleay.txt">OpenSSL License</a></li>
</ul>
......
......@@ -5,12 +5,12 @@ apply plugin: 'com.github.ben-manes.versions'
buildscript {
ext {
javaVersion = JavaVersion.VERSION_1_8
kotlinVersion = '1.3.61'
kotlinVersion = '1.3.72'
minSdkVersion = 21
sdkVersion = 29
compileSdkVersion = 29
lifecycleVersion = '2.2.0'
desugarLibsVersion = '1.0.4'
desugarLibsVersion = '1.0.5'
junitVersion = '4.13'
androidTestVersion = '1.2.0'
androidEspressoVersion = '3.2.0'
......@@ -25,14 +25,18 @@ buildscript {
maven {
url 'https://maven.fabric.io/public'
}
maven {
url "https://plugins.gradle.org/m2/"
}
}
dependencies {
classpath 'com.android.tools.build:gradle:4.0.0-alpha09'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
classpath 'com.google.android.gms:oss-licenses-plugin:0.9.5'
classpath 'com.android.tools.build:gradle:4.0.0-beta04'
classpath 'com.github.ben-manes:gradle-versions-plugin:0.28.0'
classpath 'com.google.android.gms:oss-licenses-plugin:0.10.2'
classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.9.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.11.1'
classpath 'gradle.plugin.org.mozilla.rust-android-gradle:plugin:0.8.3'
classpath 'io.fabric.tools:gradle:1.31.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
......
......@@ -2,6 +2,7 @@ apply plugin: 'com.android.library'
apply plugin: 'kotlin-android'
apply plugin: 'kotlin-android-extensions'
apply plugin: 'kotlin-kapt'
apply plugin: 'org.mozilla.rust-android-gradle.rust-android'
android {
compileSdkVersion rootProject.compileSdkVersion
......@@ -50,27 +51,43 @@ androidExtensions {
experimental = true
}
def coroutinesVersion = '1.3.3'
def roomVersion = '2.2.3'
def workVersion = '2.3.1'
cargo {
module = 'src/main/rust/shadowsocks-rust'
libname = 'sslocal'
targets = ['arm', 'arm64', 'x86', 'x86_64']
profile = findProperty('CARGO_PROFILE') ?: 'release'
targetIncludes = ["lib${libname}.so"]
extraCargoBuildArguments = ['--bin', 'sslocal']
features {
noDefaultBut "sodium", "rc4", "aes-cfb", "aes-ctr", "camellia-cfb", "openssl-vendored", "local-flow-stat", "local-dns-relay"
}
exec { spec, toolchain ->
spec.environment("RUSTFLAGS", "-C lto=no -C link-arg=-o -C link-arg=target/${toolchain.target}/$profile/lib${libname}.so")
}
}
preBuild.dependsOn "cargoBuild"
def coroutinesVersion = '1.3.5'
def roomVersion = '2.2.5'
def workVersion = '2.3.4'
dependencies {
api project(':plugin')
api 'androidx.fragment:fragment-ktx:1.2.1'
api 'androidx.fragment:fragment-ktx:1.2.4'
api "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
api "androidx.lifecycle:lifecycle-livedata-core-ktx:$lifecycleVersion"
api 'androidx.preference:preference:1.1.0'
api 'androidx.preference:preference:1.1.1'
api "androidx.room:room-runtime:$roomVersion"
api "androidx.work:work-runtime-ktx:$workVersion"
api "androidx.work:work-gcm:$workVersion"
api 'com.crashlytics.sdk.android:crashlytics:2.10.1'
api 'com.google.android.gms:play-services-oss-licenses:17.0.0'
api 'com.google.code.gson:gson:2.8.6'
api 'com.google.firebase:firebase-analytics:17.2.2'
api 'com.google.firebase:firebase-config:19.1.1'
api 'dnsjava:dnsjava:2.1.9'
api 'com.google.firebase:firebase-analytics:17.3.0'
api 'com.google.firebase:firebase-config:19.1.3'
api 'dnsjava:dnsjava:3.0.2'
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
api "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutinesVersion"
api 'org.connectbot.jsocks:jsocks:1.0.0'
coreLibraryDesugaring "com.android.tools:desugar_jdk_libs:$desugarLibsVersion"
kapt "androidx.room:room-compiler:$roomVersion"
testImplementation "junit:junit:$junitVersion"
......
......@@ -20,8 +20,3 @@
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
# https://issuetracker.google.com/issues/147972078#comment4
-keepclasseswithmembers class * extends androidx.lifecycle.ViewModel {
<init>();
}
/*******************************************************************************
* *
* Copyright (C) 2020 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2020 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.acl
import androidx.test.core.app.ApplicationProvider
import com.github.shadowsocks.Core
import com.github.shadowsocks.utils.parseNumericAddress
import kotlinx.coroutines.runBlocking
import org.junit.Assert
import org.junit.BeforeClass
import org.junit.Test
class AclMatcherTest {
companion object {
@BeforeClass
@JvmStatic
fun setup() {
Core.app = ApplicationProvider.getApplicationContext()
}
}
@Test
fun emptyFile() {
runBlocking {
AclMatcher().apply {
init(AclTest.BYPASS_BASE.reader())
Assert.assertTrue(shouldBypassIpv4(ByteArray(4)))
Assert.assertTrue(shouldBypassIpv6(ByteArray(16)))
Assert.assertNull(shouldBypass("www.google.com"))
}
}
}
@Test
fun basic() {
runBlocking {
AclMatcher().apply {
init(AclTest.INPUT1.reader())
Assert.assertTrue(shouldBypassIpv4("0.1.2.3".parseNumericAddress()!!.address))
Assert.assertFalse(shouldBypassIpv4("1.0.1.2".parseNumericAddress()!!.address))
Assert.assertTrue(shouldBypassIpv4("1.0.3.2".parseNumericAddress()!!.address))
Assert.assertTrue(shouldBypassIpv6("::".parseNumericAddress()!!.address))
Assert.assertFalse(shouldBypassIpv6("2020::2020".parseNumericAddress()!!.address))
Assert.assertTrue(shouldBypassIpv6("fe80::2020".parseNumericAddress()!!.address))
Assert.assertTrue(shouldBypass("4tern.com") == false)
Assert.assertTrue(shouldBypass("www.4tern.com") == false)
Assert.assertNull(shouldBypass("www.google.com"))
}
}
}
@Test
fun bypassList() {
runBlocking {
AclMatcher().apply {
init(AclTest.INPUT2.reader())
Assert.assertFalse(shouldBypassIpv4("0.1.2.3".parseNumericAddress()!!.address))
Assert.assertTrue(shouldBypassIpv4("10.0.1.2".parseNumericAddress()!!.address))
Assert.assertTrue(shouldBypassIpv4("10.10.1.2".parseNumericAddress()!!.address))
Assert.assertFalse(shouldBypassIpv4("11.0.1.2".parseNumericAddress()!!.address))
Assert.assertTrue(shouldBypass("chrome.com") == true)
Assert.assertTrue(shouldBypass("about.google") == false)
Assert.assertNull(shouldBypass("www.google.com"))
}
}
}
}
......@@ -103,7 +103,6 @@
<provider android:name="com.google.firebase.provider.FirebaseInitProvider"
tools:node="remove"/>
<service android:name="androidx.room.MultiInstanceInvalidationService"
android:directBootAware="true"
android:process=":bg"/>
<!-- https://android.googlesource.com/platform/frameworks/support/+/androidx-master-dev/work/workmanager/src/main/AndroidManifest.xml -->
<provider android:name="androidx.work.impl.WorkManagerInitializer"
......
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2019 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.acl
import com.github.shadowsocks.Core
import com.github.shadowsocks.net.Subnet
import java.io.Reader
import java.net.Inet4Address
import java.net.Inet6Address
class AclMatcher : AutoCloseable {
companion object {
init {
System.loadLibrary("jni-helper")
}
@JvmStatic private external fun init(): Long
@JvmStatic private external fun close(handle: Long)
@JvmStatic private external fun addBypassDomain(handle: Long, regex: String): Boolean
@JvmStatic private external fun addProxyDomain(handle: Long, regex: String): Boolean
@JvmStatic private external fun build(handle: Long, memoryLimit: Long): String?
@JvmStatic private external fun matchHost(handle: Long, host: String): Int
}
class Re2Exception(message: String) : IllegalArgumentException(message)
private var handle = 0L
override fun close() {
if (handle != 0L) {
close(handle)
handle = 0L
}
}
private var subnetsIpv4 = emptyList<Subnet.Immutable>()
private var subnetsIpv6 = emptyList<Subnet.Immutable>()
private var bypass = false
suspend fun init(id: String) = init(Acl.getFile(id).bufferedReader())
suspend fun init(reader: Reader) {
fun Sequence<Subnet>.dedup() = sequence {
val iterator = map { it.toImmutable() }.sortedWith(Subnet.Immutable).iterator()
var current: Subnet.Immutable? = null
while (iterator.hasNext()) {
val next = iterator.next()
if (current?.matches(next) == true) continue
yield(next)
current = next
}
}.toList()
check(handle == 0L)
handle = init()
try {
val (bypass, subnets) = Acl.parse(reader, {
check(addBypassDomain(handle, it))
}, {
check(addProxyDomain(handle, it))
})
build(handle, if (Core.activity.isLowRamDevice) 8 shl 20 else 64 shl 20)?.also { throw Re2Exception(it) }
subnetsIpv4 = subnets.asSequence().filter { it.address is Inet4Address }.dedup()
subnetsIpv6 = subnets.asSequence().filter { it.address is Inet6Address }.dedup()
this.bypass = bypass
} catch (e: Exception) {
close()
throw e
}
}
private fun quickMatches(subnets: List<Subnet.Immutable>, ip: ByteArray): Boolean {
val i = subnets.binarySearch(Subnet.Immutable(ip), Subnet.Immutable)
return i >= 0 || i < -1 && subnets[-i - 2].matches(ip)
}
fun shouldBypassIpv4(ip: ByteArray) = bypass xor quickMatches(subnetsIpv4, ip)
fun shouldBypassIpv6(ip: ByteArray) = bypass xor quickMatches(subnetsIpv6, ip)
fun shouldBypass(host: String) = when (val e = matchHost(handle, host)) {
0 -> null
1 -> true
2 -> false
else -> error("matchHost -> $e")
}
}
......@@ -37,6 +37,7 @@ import com.github.shadowsocks.aidl.IShadowsocksService
import com.github.shadowsocks.aidl.IShadowsocksServiceCallback
import com.github.shadowsocks.aidl.TrafficStats
import com.github.shadowsocks.core.R
import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.net.HostsFile
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.*
......@@ -46,7 +47,6 @@ import java.io.File
import java.io.IOException
import java.net.URL
import java.net.UnknownHostException
import java.util.*
/**
* This object uses WeakMap to simulate the effects of multi-inheritance.
......@@ -74,6 +74,7 @@ object BaseService {
var processes: GuardedProcessPool? = null
var proxy: ProxyInstance? = null
var udpFallback: ProxyInstance? = null
var localDns: LocalDnsWorker? = null
var notification: ServiceNotification? = null
val closeReceiver = broadcastReceiver { _, intent ->
......@@ -232,7 +233,7 @@ object BaseService {
}
}
fun buildAdditionalArguments(cmd: ArrayList<String>): ArrayList<String> = cmd
val isVpnService get() = false
suspend fun startProcesses(hosts: HostsFile) {
val configRoot = (if (Build.VERSION.SDK_INT < 24 || app.getSystemService<UserManager>()
......@@ -241,12 +242,13 @@ object BaseService {
data.proxy!!.start(this,
File(Core.deviceStorage.noBackupFilesDir, "stat_main"),
File(configRoot, CONFIG_FILE),
if (udpFallback == null) "-u" else null)
if (udpFallback == null) "-U" else null)
check(udpFallback?.plugin == null) { "UDP fallback cannot have plugins" }
udpFallback?.start(this,
File(Core.deviceStorage.noBackupFilesDir, "stat_udp"),
File(configRoot, CONFIG_FILE_UDP),
"-U")
"-u", false)
data.localDns = LocalDnsWorker(this::rawResolver).apply { start() }
}
fun startRunner() {
......@@ -260,6 +262,8 @@ object BaseService {
close(scope)
data.processes = null
}
data.localDns?.shutdown(scope)
data.localDns = null
}
fun stopRunner(restart: Boolean = false, msg: String? = null) {
......@@ -309,6 +313,7 @@ object BaseService {
suspend fun preInit() { }
suspend fun getActiveNetwork() = if (Build.VERSION.SDK_INT >= 23) Core.connectivity.activeNetwork else null
suspend fun resolver(host: String) = DnsResolverCompat.resolveOnActiveNetwork(host)
suspend fun rawResolver(query: ByteArray) = DnsResolverCompat.resolveRawOnActiveNetwork(query)
suspend fun openConnection(url: URL) = url.openConnection()
fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int {
......
......@@ -31,7 +31,7 @@ import java.io.IOException
object Executable {
const val REDSOCKS = "libredsocks.so"
const val SS_LOCAL = "libss-local.so"
const val SS_LOCAL = "libsslocal.so"
const val TUN2SOCKS = "libtun2socks.so"
private val EXECUTABLES = setOf(SS_LOCAL, REDSOCKS, TUN2SOCKS)
......
package com.github.shadowsocks.bg
import android.net.LocalSocket
import android.util.Log
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core
import com.github.shadowsocks.net.ConcurrentLocalSocketListener
import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.CancellationException
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.TimeoutCancellationException
import kotlinx.coroutines.launch
import org.xbill.DNS.Message
import org.xbill.DNS.Rcode
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.File
import java.io.IOException
class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray) : ConcurrentLocalSocketListener(
"LocalDnsThread", File(Core.deviceStorage.noBackupFilesDir, "local_dns_path")), CoroutineScope {
override fun acceptInternal(socket: LocalSocket) = error("big no no")
override fun accept(socket: LocalSocket) {
launch {
socket.use {
val input = DataInputStream(socket.inputStream)
val query = ByteArray(input.readUnsignedShort())
input.read(query)
try {
resolver(query)
} catch (e: Exception) {
when (e) {
is TimeoutCancellationException -> Crashlytics.log(Log.WARN, name, "Resolving timed out")
is CancellationException -> { } // ignore
is IOException -> Crashlytics.log(Log.WARN, name, e.message)
else -> printLog(e)
}
try {
DnsResolverCompat.prepareDnsResponse(Message(query)).apply {
header.rcode = Rcode.SERVFAIL
}.toWire()
} catch (_: IOException) {
byteArrayOf() // return empty if cannot parse packet
}
}?.let { response ->
try {
val output = DataOutputStream(socket.outputStream)
output.writeShort(response.size)
output.write(response)
} catch (_: IOException) { } // connection early close possibly due to resolving timeout
}
}
}
}
}
......@@ -26,6 +26,7 @@ import com.github.shadowsocks.Core
import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.acl.AclSyncer
import com.github.shadowsocks.database.Profile
import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.net.HostsFile
import com.github.shadowsocks.plugin.PluginConfiguration
import com.github.shadowsocks.plugin.PluginManager
......@@ -109,32 +110,40 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
* Sensitive shadowsocks configuration file requires extra protection. It may be stored in encrypted storage or
* device storage, depending on which is currently available.
*/
fun start(service: BaseService.Interface, stat: File, configFile: File, extraFlag: String? = null) {
fun start(service: BaseService.Interface, stat: File, configFile: File, extraFlag: String? = null,
dnsRelay: Boolean = true) {
trafficMonitor = TrafficMonitor(stat)
this.configFile = configFile
val config = profile.toJson()
plugin?.let { (path, opts) -> config.put("plugin", path).put("plugin_opts", opts.toString()) }
val vpnFlags = if (service.isVpnService) ";V" else ""
plugin?.let { (path, opts) -> config.put("plugin", path).put("plugin_opts", opts.toString() + vpnFlags) }
config.put("local_address", DataStore.listenAddress)
config.put("local_port", DataStore.portProxy)
configFile.writeText(config.toString())
val cmd = service.buildAdditionalArguments(arrayListOf(
val dns = try {
URI("dns://${profile.remoteDns}")
} catch (e: URISyntaxException) {
throw BaseService.ExpectedExceptionWrapper(e)
}
val cmd = arrayListOf(
File((service as Context).applicationInfo.nativeLibraryDir, Executable.SS_LOCAL).absolutePath,
"-b", DataStore.listenAddress,
"-l", DataStore.portProxy.toString(),
"-t", "600",
"-S", stat.absolutePath,
"-c", configFile.absolutePath))
"--stat-path", stat.absolutePath,
"-c", configFile.absolutePath)
if (service.isVpnService) cmd += arrayListOf("--vpn")
if (extraFlag != null) cmd.add(extraFlag)
if (dnsRelay) cmd += arrayListOf(
"--dns-relay", "${DataStore.listenAddress}:${DataStore.portLocalDns}",
"--remote-dns", "${dns.host}:${if (dns.port < 0) 53 else dns.port}")
if (route != Acl.ALL) {
cmd += "--acl"
cmd += Acl.getFile(route).absolutePath
}
// for UDP profile, it's only going to operate in UDP relay mode-only so this flag has no effect
if (profile.route == Acl.ALL) cmd += "-D"
if (DataStore.tcpFastOpen) cmd += "--fast-open"
// if (DataStore.tcpFastOpen) cmd += "--fast-open"
service.data.processes!!.start(cmd)
}
......
......@@ -27,7 +27,7 @@ import com.github.shadowsocks.net.HostsFile
import com.github.shadowsocks.preference.DataStore
import java.io.File
class TransproxyService : Service(), LocalDnsService.Interface {
class TransproxyService : Service(), BaseService.Interface {
override val data = BaseService.Data(this)
override val tag: String get() = "ShadowsocksTransproxyService"
override fun createNotification(profileName: String): ServiceNotification =
......@@ -35,7 +35,7 @@ class TransproxyService : Service(), LocalDnsService.Interface {
override fun onBind(intent: Intent) = super.onBind(intent)
override fun onStartCommand(intent: Intent?, flags: Int, startId: Int): Int =
super<LocalDnsService.Interface>.onStartCommand(intent, flags, startId)
super<BaseService.Interface>.onStartCommand(intent, flags, startId)
private fun startRedsocksDaemon() {
File(Core.deviceStorage.noBackupFilesDir, "redsocks.conf").writeText("""base {
......
......@@ -34,26 +34,18 @@ import com.github.shadowsocks.Core
import com.github.shadowsocks.VpnRequestActivity
import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.core.R
import com.github.shadowsocks.net.ConcurrentLocalSocketListener
import com.github.shadowsocks.net.DefaultNetworkListener
import com.github.shadowsocks.net.HostsFile
import com.github.shadowsocks.net.Subnet
import com.github.shadowsocks.net.*
import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.Key
import com.github.shadowsocks.utils.closeQuietly
import com.github.shadowsocks.utils.int
import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.delay
import kotlinx.coroutines.launch
import java.io.File
import java.io.FileDescriptor
import java.io.IOException
import kotlinx.coroutines.*
import java.io.*
import java.net.URL
import java.util.*
import android.net.VpnService as BaseVpnService
class VpnService : BaseVpnService(), LocalDnsService.Interface {
class VpnService : BaseVpnService(), BaseService.Interface {
companion object {
private const val VPN_MTU = 1500
private const val PRIVATE_VLAN4_CLIENT = "172.19.0.1"
......@@ -115,7 +107,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
override fun onBind(intent: Intent) = when (intent.action) {
SERVICE_INTERFACE -> super<BaseVpnService>.onBind(intent)
else -> super<LocalDnsService.Interface>.onBind(intent)
else -> super<BaseService.Interface>.onBind(intent)
}
override fun onRevoke() = stopRunner()
......@@ -134,7 +126,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
if (DataStore.serviceMode == Key.modeVpn) {
if (prepare(this) != null) {
startActivity(Intent(this, VpnRequestActivity::class.java).addFlags(Intent.FLAG_ACTIVITY_NEW_TASK))
} else return super<LocalDnsService.Interface>.onStartCommand(intent, flags, startId)
} else return super<BaseService.Interface>.onStartCommand(intent, flags, startId)
}
stopRunner()
return Service.START_NOT_STICKY
......@@ -143,6 +135,10 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
override suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it }
override suspend fun getActiveNetwork() = DefaultNetworkListener.get()
override suspend fun resolver(host: String) = DnsResolverCompat.resolve(DefaultNetworkListener.get(), host)
override suspend fun rawResolver(query: ByteArray) =
// no need to listen for network here as this is only used for forwarding local DNS queries.
// retries should be attempted by client.
DnsResolverCompat.resolveRaw(underlyingNetwork ?: throw IOException("no network"), query)
override suspend fun openConnection(url: URL) = DefaultNetworkListener.get().openConnection(url)
override suspend fun startProcesses(hosts: HostsFile) {
......@@ -151,10 +147,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
sendFd(startVpn())
}
override fun buildAdditionalArguments(cmd: ArrayList<String>): ArrayList<String> {
cmd += "-V"
return cmd
}
override val isVpnService get() = true
private suspend fun startVpn(): FileDescriptor {
val profile = data.proxy!!.profile
......
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2019 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.net
import android.os.Build
import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.*
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.sendBlocking
import java.io.IOException
import java.nio.ByteBuffer
import java.nio.channels.*
class ChannelMonitor : Thread("ChannelMonitor") {
private data class Registration(val channel: SelectableChannel,
val ops: Int,
val listener: (SelectionKey) -> Unit) {
val result = CompletableDeferred<SelectionKey>()
}
private val selector = Selector.open()
private val registrationPipe = Pipe.open()
private val pendingRegistrations = Channel<Registration>(Channel.UNLIMITED)
private val closeChannel = Channel<Unit>(1)
@Volatile
private var running = true
private fun registerInternal(channel: SelectableChannel, ops: Int, block: (SelectionKey) -> Unit) =
channel.register(selector, ops, block)
init {
registrationPipe.source().apply {
configureBlocking(false)
registerInternal(this, SelectionKey.OP_READ) {
val junk = ByteBuffer.allocateDirect(1)
while (read(junk) > 0) {
pendingRegistrations.poll()!!.apply {
try {
result.complete(registerInternal(channel, ops, listener))
} catch (e: Exception) {
result.completeExceptionally(e)
}
}
junk.clear()
}
}
}
start()
}
/**
* Prevent NetworkOnMainThreadException because people enable strict mode for no reasons.
*/
private suspend fun WritableByteChannel.writeCompat(src: ByteBuffer) =
if (Build.VERSION.SDK_INT <= 23) withContext(Dispatchers.Default) { write(src) } else write(src)
suspend fun register(channel: SelectableChannel, ops: Int, block: (SelectionKey) -> Unit): SelectionKey {
val registration = Registration(channel, ops, block)
pendingRegistrations.send(registration)
ByteBuffer.allocateDirect(1).also { junk ->
loop@ while (running) when (registrationPipe.sink().writeCompat(junk)) {
0 -> kotlinx.coroutines.yield()
1 -> break@loop
else -> throw IOException("Failed to register in the channel")
}
}
if (!running) throw CancellationException()
return registration.result.await()
}
suspend fun wait(channel: SelectableChannel, ops: Int) = CompletableDeferred<SelectionKey>().run {
register(channel, ops) {
if (it.isValid) try {
it.interestOps(0) // stop listening
} catch (_: CancelledKeyException) { }
complete(it)
}
await()
}
override fun run() {
while (running) {
val num = try {
selector.select()
} catch (e: Exception) {
printLog(e)
continue
}
if (num <= 0) continue
val iterator = selector.selectedKeys().iterator()
while (iterator.hasNext()) {
val key = iterator.next()
iterator.remove()
(key.attachment() as (SelectionKey) -> Unit)(key)
}
}
closeChannel.sendBlocking(Unit)
}
fun close(scope: CoroutineScope) {
running = false
selector.wakeup()
scope.launch {
closeChannel.receive()
selector.keys().forEach { it.channel().close() }
selector.close()
}
}
}
......@@ -18,7 +18,7 @@
* *
*******************************************************************************/
package com.github.shadowsocks.bg
package com.github.shadowsocks.net
import android.annotation.SuppressLint
import android.annotation.TargetApi
......@@ -36,8 +36,11 @@ import com.github.shadowsocks.utils.int
import com.github.shadowsocks.utils.parseNumericAddress
import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.*
import org.xbill.DNS.*
import java.io.FileDescriptor
import java.io.IOException
import java.net.Inet4Address
import java.net.Inet6Address
import java.net.InetAddress
import java.util.concurrent.Executor
import java.util.concurrent.Executors
......@@ -94,6 +97,22 @@ sealed class DnsResolverCompat {
override fun bindSocket(network: Network, socket: FileDescriptor) = instance.bindSocket(network, socket)
override suspend fun resolve(network: Network, host: String) = instance.resolve(network, host)
override suspend fun resolveOnActiveNetwork(host: String) = instance.resolveOnActiveNetwork(host)
override suspend fun resolveRaw(network: Network, query: ByteArray) = instance.resolveRaw(network, query)
override suspend fun resolveRawOnActiveNetwork(query: ByteArray) = instance.resolveRawOnActiveNetwork(query)
// additional platform-independent DNS helpers
/**
* TTL returned from localResolver is set to 120. Android API does not provide TTL,
* so we suppose Android apps should not care about TTL either.
*/
private const val TTL = 120L
fun prepareDnsResponse(request: Message) = Message(request.header.id).apply {
header.setFlag(Flags.QR.toInt()) // this is a response
if (request.header.getFlag(Flags.RD.toInt())) header.setFlag(Flags.RD.toInt())
request.question?.also { addRecord(it, Section.QUESTION) }
}
}
@Throws(IOException::class)
......@@ -102,6 +121,8 @@ sealed class DnsResolverCompat {
Os.connect(fd, address, port)
abstract suspend fun resolve(network: Network, host: String): Array<InetAddress>
abstract suspend fun resolveOnActiveNetwork(host: String): Array<InetAddress>
abstract suspend fun resolveRaw(network: Network, query: ByteArray): ByteArray
abstract suspend fun resolveRawOnActiveNetwork(query: ByteArray): ByteArray
@SuppressLint("PrivateApi")
private open class DnsResolverCompat21 : DnsResolverCompat() {
......@@ -138,6 +159,40 @@ sealed class DnsResolverCompat {
GlobalScope.async(unboundedIO) { network.getAllByName(host) }.await()
override suspend fun resolveOnActiveNetwork(host: String) =
GlobalScope.async(unboundedIO) { InetAddress.getAllByName(host) }.await()
private suspend fun resolveRaw(query: ByteArray,
hostResolver: suspend (String) -> Array<InetAddress>): ByteArray {
val request = try {
Message(query)
} catch (e: IOException) {
throw UnsupportedOperationException(e) // unrecognized packet
}
when (val opcode = request.header.opcode) {
Opcode.QUERY -> { }
else -> throw UnsupportedOperationException("Unsupported opcode $opcode")
}
val question = request.question
val isIpv6 = when (val type = question?.type) {
Type.A -> false
Type.AAAA -> true
else -> throw UnsupportedOperationException("Unsupported query type $type")
}
val host = question.name.canonicalize().toString(true)
return prepareDnsResponse(request).apply {
header.setFlag(Flags.RA.toInt()) // recursion available
for (address in hostResolver(host).asIterable().run {
if (isIpv6) filterIsInstance<Inet6Address>() else filterIsInstance<Inet4Address>()
}) addRecord(when (address) {
is Inet4Address -> ARecord(question.name, DClass.IN, TTL, address)
is Inet6Address -> AAAARecord(question.name, DClass.IN, TTL, address)
else -> error("Unsupported address $address")
}, Section.ANSWER)
}.toWire()
}
override suspend fun resolveRaw(network: Network, query: ByteArray) =
resolveRaw(query) { resolve(network, it) }
override suspend fun resolveRawOnActiveNetwork(query: ByteArray) =
resolveRaw(query, this::resolveOnActiveNetwork)
}
@TargetApi(23)
......@@ -154,6 +209,8 @@ sealed class DnsResolverCompat {
override fun bindSocket(network: Network, socket: FileDescriptor) = network.bindSocket(socket)
private val activeNetwork get() = Core.connectivity.activeNetwork ?: throw IOException("no network")
override suspend fun resolve(network: Network, host: String): Array<InetAddress> {
return suspendCancellableCoroutine { cont ->
val signal = CancellationSignal()
......@@ -167,9 +224,19 @@ sealed class DnsResolverCompat {
})
}
}
override suspend fun resolveOnActiveNetwork(host: String) = resolve(activeNetwork, host)
override suspend fun resolveOnActiveNetwork(host: String): Array<InetAddress> {
return resolve(Core.connectivity.activeNetwork ?: return emptyArray(), host)
override suspend fun resolveRaw(network: Network, query: ByteArray): ByteArray {
return suspendCancellableCoroutine { cont ->
val signal = CancellationSignal()
cont.invokeOnCancellation { signal.cancel() }
DnsResolver.getInstance().rawQuery(network, query, DnsResolver.FLAG_NO_RETRY, this,
signal, object : DnsResolver.Callback<ByteArray> {
override fun onAnswer(answer: ByteArray, rcode: Int) = cont.resume(answer)
override fun onError(error: DnsResolver.DnsException) = cont.resumeWithException(IOException(error))
})
}
}
override suspend fun resolveRawOnActiveNetwork(query: ByteArray) = resolveRaw(activeNetwork, query)
}
}
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2019 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.net
import com.github.shadowsocks.utils.parseNumericAddress
import net.sourceforge.jsocks.Socks4Message
import net.sourceforge.jsocks.Socks5Message
import java.io.EOFException
import java.io.IOException
import java.net.Inet4Address
import java.net.Inet6Address
import java.nio.ByteBuffer
import kotlin.math.max
class Socks5Endpoint(host: String, port: Int) {
private val dest = host.parseNumericAddress().let { numeric ->
val bytes = numeric?.address ?: host.toByteArray().apply { check(size < 256) { "Hostname too long" } }
val type = when (numeric) {
null -> Socks5Message.SOCKS_ATYP_DOMAINNAME
is Inet4Address -> Socks5Message.SOCKS_ATYP_IPV4
is Inet6Address -> Socks5Message.SOCKS_ATYP_IPV6
else -> error("Unsupported address type $numeric")
}
ByteBuffer.allocate(bytes.size + (if (numeric == null) 1 else 0) + 3).apply {
put(type.toByte())
if (numeric == null) put(bytes.size.toByte())
put(bytes)
putShort(port.toShort())
}
}.array()
private val headerReserved = max(3 + 3 + 16, 3 + dest.size)
fun tcpWrap(message: ByteBuffer): ByteBuffer {
check(message.remaining() < 65536) { "TCP message too large" }
return ByteBuffer.allocateDirect(8 + dest.size + message.remaining()).apply {
put(Socks5Message.SOCKS_VERSION.toByte())
put(1) // nmethods
put(0) // no authentication required
// header
put(Socks5Message.SOCKS_VERSION.toByte())
put(Socks4Message.REQUEST_CONNECT.toByte())
put(0) // reserved
put(dest)
// data
putShort(message.remaining().toShort())
put(message)
flip()
}
}
fun tcpReceiveBuffer(size: Int) = ByteBuffer.allocateDirect(headerReserved + 4 + size)
suspend fun tcpUnwrap(buffer: ByteBuffer, reader: (ByteBuffer) -> Int, wait: suspend () -> Unit) {
suspend fun readBytes(till: Int) {
if (buffer.position() >= till) return
while (reader(buffer) >= 0 && buffer.position() < till) wait()
if (buffer.position() < till) throw EOFException("${buffer.position()} < $till")
}
suspend fun read(index: Int): Byte {
readBytes(index + 1)
return buffer[index]
}
if (read(0) != Socks5Message.SOCKS_VERSION.toByte()) throw IOException("Unsupported SOCKS version ${buffer[0]}")
if (read(1) != 0.toByte()) throw IOException("Unsupported authentication ${buffer[1]}")
if (read(2) != Socks5Message.SOCKS_VERSION.toByte()) throw IOException("Unsupported SOCKS version ${buffer[2]}")
if (read(3) != 0.toByte()) throw IOException("SOCKS5 server returned error ${buffer[3]}")
val dataOffset = when (val type = read(5)) {
Socks5Message.SOCKS_ATYP_IPV4.toByte() -> 4
Socks5Message.SOCKS_ATYP_DOMAINNAME.toByte() -> 1 + read(6)
Socks5Message.SOCKS_ATYP_IPV6.toByte() -> 16
else -> throw IOException("Unsupported address type $type")
} + 8
readBytes(dataOffset + 2)
buffer.limit(buffer.position()) // store old position to update mark
buffer.position(dataOffset)
val dataLength = buffer.short.toUShort().toInt()
val end = buffer.position() + dataLength
if (end > buffer.capacity()) throw IOException(
"Buffer too small to contain the message: $dataLength > ${buffer.capacity() - buffer.position()}")
buffer.mark()
buffer.position(buffer.limit()) // restore old position
buffer.limit(end)
readBytes(buffer.limit())
buffer.reset()
}
private fun ByteBuffer.tryPosition(newPosition: Int) {
if (limit() < newPosition) throw EOFException("${limit()} < $newPosition")
position(newPosition)
}
fun udpWrap(packet: ByteBuffer) = ByteBuffer.allocateDirect(3 + dest.size + packet.remaining()).apply {
// header
putShort(0) // reserved
put(0) // fragment number
put(dest)
// data
put(packet)
flip()
}
fun udpReceiveBuffer(size: Int) = ByteBuffer.allocateDirect(headerReserved + size)
fun udpUnwrap(packet: ByteBuffer) {
packet.tryPosition(3)
packet.tryPosition(6 + when (val type = packet.get()) {
Socks5Message.SOCKS_ATYP_IPV4.toByte() -> 4
Socks5Message.SOCKS_ATYP_DOMAINNAME.toByte() -> 1 + packet.get()
Socks5Message.SOCKS_ATYP_IPV6.toByte() -> 16
else -> throw IOException("Unsupported address type $type")
})
packet.mark()
}
}
......@@ -24,8 +24,6 @@ import android.app.NotificationChannel
import android.app.NotificationManager
import android.app.PendingIntent
import android.app.Service
import android.content.BroadcastReceiver
import android.content.Context
import android.content.Intent
import android.content.IntentFilter
import android.os.IBinder
......@@ -35,7 +33,6 @@ import androidx.annotation.RequiresApi
import androidx.core.app.NotificationCompat
import androidx.core.content.ContextCompat
import androidx.lifecycle.MutableLiveData
import com.crashlytics.android.Crashlytics
import com.github.shadowsocks.Core
import com.github.shadowsocks.Core.app
import com.github.shadowsocks.core.R
......
......@@ -70,7 +70,7 @@ fun FileDescriptor.closeQuietly() = try {
Os.close(this)
} catch (_: ErrnoException) { }
private val parseNumericAddress by lazy @SuppressLint("DiscouragedPrivateApi") {
private val parseNumericAddress by lazy @SuppressLint("SoonBlockedPrivateApi") {
InetAddress::class.java.getDeclaredMethod("parseNumericAddress", String::class.java).apply {
isAccessible = true
}
......
This diff is collapsed.
/* config.h. Generated from config.h.in by configure. */
/* config.h.in. Generated from configure.ac by autoheader. */
/* Define to 1 if you have the `clock_gettime' function. */
/* #undef HAVE_CLOCK_GETTIME */
/* Define to 1 to use the syscall interface for clock_gettime */
#define HAVE_CLOCK_SYSCALL 1
/* Define to 1 if you have the <dlfcn.h> header file. */
#define HAVE_DLFCN_H 1
/* Define to 1 if you have the `epoll_ctl' function. */
#define HAVE_EPOLL_CTL 1
/* Define to 1 if you have the `eventfd' function. */
#define HAVE_EVENTFD 1
/* Define to 1 if the floor function is available */
#define HAVE_FLOOR 1
/* Define to 1 if you have the `inotify_init' function. */
#define HAVE_INOTIFY_INIT 1
/* Define to 1 if you have the <inttypes.h> header file. */
#define HAVE_INTTYPES_H 1
/* Define to 1 if you have the `kqueue' function. */
/* #undef HAVE_KQUEUE */
/* Define to 1 if you have the `rt' library (-lrt). */
/* #undef HAVE_LIBRT */
/* Define to 1 if you have the <memory.h> header file. */
#define HAVE_MEMORY_H 1
/* Define to 1 if you have the `nanosleep' function. */
#define HAVE_NANOSLEEP 1
/* Define to 1 if you have the `poll' function. */
#define HAVE_POLL 1
/* Define to 1 if you have the <poll.h> header file. */
#define HAVE_POLL_H 1
/* Define to 1 if you have the `port_create' function. */
/* #undef HAVE_PORT_CREATE */
/* Define to 1 if you have the <port.h> header file. */
/* #undef HAVE_PORT_H */
/* Define to 1 if you have the `select' function. */
#define HAVE_SELECT 1
/* Define to 1 if you have the `signalfd' function. */
#define HAVE_SIGNALFD 0
/* Define to 1 if you have the <stdint.h> header file. */
#define HAVE_STDINT_H 1
/* Define to 1 if you have the <stdlib.h> header file. */
#define HAVE_STDLIB_H 1
/* Define to 1 if you have the <strings.h> header file. */
#define HAVE_STRINGS_H 1
/* Define to 1 if you have the <string.h> header file. */
#define HAVE_STRING_H 1
/* Define to 1 if you have the <sys/epoll.h> header file. */
#define HAVE_SYS_EPOLL_H 1
/* Define to 1 if you have the <sys/eventfd.h> header file. */
#define HAVE_SYS_EVENTFD_H 1
/* Define to 1 if you have the <sys/event.h> header file. */
/* #undef HAVE_SYS_EVENT_H */
/* Define to 1 if you have the <sys/inotify.h> header file. */
#define HAVE_SYS_INOTIFY_H 1
/* Define to 1 if you have the <sys/select.h> header file. */
#define HAVE_SYS_SELECT_H 1
/* Define to 1 if you have the <sys/signalfd.h> header file. */
#define HAVE_SYS_SIGNALFD_H 1
/* Define to 1 if you have the <sys/stat.h> header file. */
#define HAVE_SYS_STAT_H 1
/* Define to 1 if you have the <sys/types.h> header file. */
#define HAVE_SYS_TYPES_H 1
/* Define to 1 if you have the <unistd.h> header file. */
#define HAVE_UNISTD_H 1
/* Define to the sub-directory in which libtool stores uninstalled libraries.
*/
#define LT_OBJDIR ".libs/"
/* Name of package */
#define PACKAGE "libev"
/* Define to the address where bug reports for this package should be sent. */
#define PACKAGE_BUGREPORT ""
/* Define to the full name of this package. */
#define PACKAGE_NAME ""
/* Define to the full name and version of this package. */
#define PACKAGE_STRING ""
/* Define to the one symbol short name of this package. */
#define PACKAGE_TARNAME ""
/* Define to the home page for this package. */
#define PACKAGE_URL ""
/* Define to the version of this package. */
#define PACKAGE_VERSION ""
/* Define to 1 if you have the ANSI C header files. */
#define STDC_HEADERS 1
/* Version number of package */
#define VERSION "4.11"
This diff is collapsed.
This diff is collapsed.
#ifndef sodium_version_H
#define sodium_version_H
#include "export.h"
#define SODIUM_VERSION_STRING "1.0.7"
#define SODIUM_LIBRARY_VERSION_MAJOR 9
#define SODIUM_LIBRARY_VERSION_MINOR 0
#ifdef __cplusplus
extern "C" {
#endif
SODIUM_EXPORT
const char *sodium_version_string(void);
SODIUM_EXPORT
int sodium_library_version_major(void);
SODIUM_EXPORT
int sodium_library_version_minor(void);
#ifdef __cplusplus
}
#endif
#endif
/*******************************************************************************
* *
* Copyright (C) 2019 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2019 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/>. *
* *
*******************************************************************************/
#include <sstream>
#include "jni.h"
#include "re2/re2.h"
using namespace std;
struct AclMatcher {
stringstream bypassDomainsBuilder, proxyDomainsBuilder;
RE2 *bypassDomains, *proxyDomains;
~AclMatcher() {
if (bypassDomains) delete bypassDomains;
if (proxyDomains) delete proxyDomains;
}
};
bool addDomain(JNIEnv *env, stringstream &domains, jstring regex) {
const char *regexChars = env->GetStringUTFChars(regex, nullptr);
if (regexChars == nullptr) return false;
if (domains.rdbuf()->in_avail()) domains << '|';
domains << regexChars;
env->ReleaseStringUTFChars(regex, regexChars);
return true;
}
const char *buildRE2(stringstream &domains, RE2 *&out, const RE2::Options &options) {
if (domains.rdbuf()->in_avail()) {
out = new RE2(domains.str(), options);
domains.clear();
if (!out->ok()) return out->error().c_str();
} else {
delete out;
out = nullptr;
}
return nullptr;
}
#pragma clang diagnostic push
#pragma clang diagnostic ignored "-Wunused-parameter"
extern "C" {
JNIEXPORT jlong JNICALL Java_com_github_shadowsocks_acl_AclMatcher_init(JNIEnv *env, jclass clazz) {
return reinterpret_cast<jlong>(new AclMatcher());
}
JNIEXPORT void JNICALL
Java_com_github_shadowsocks_acl_AclMatcher_close(JNIEnv *env, jclass clazz, jlong handle) {
delete reinterpret_cast<AclMatcher *>(handle);
}
JNIEXPORT jboolean JNICALL
Java_com_github_shadowsocks_acl_AclMatcher_addBypassDomain(JNIEnv *env, jclass clazz, jlong handle,
jstring regex) {
return static_cast<jboolean>(handle &&
::addDomain(env, reinterpret_cast<AclMatcher *>(handle)->bypassDomainsBuilder, regex));
}
JNIEXPORT jboolean JNICALL
Java_com_github_shadowsocks_acl_AclMatcher_addProxyDomain(JNIEnv *env, jclass clazz, jlong handle,
jstring regex) {
return static_cast<jboolean>(handle &&
::addDomain(env, reinterpret_cast<AclMatcher *>(handle)->proxyDomainsBuilder, regex));
}
JNIEXPORT jstring JNICALL
Java_com_github_shadowsocks_acl_AclMatcher_build(JNIEnv *env, jclass clazz, jlong handle,
jlong memory_limit) {
if (!handle) return env->NewStringUTF("AclMatcher closed");
auto matcher = reinterpret_cast<AclMatcher *>(handle);
RE2::Options options;
options.set_max_mem(memory_limit);
options.set_never_capture(true);
const char *e = ::buildRE2(matcher->bypassDomainsBuilder, matcher->bypassDomains, options);
if (e) return env->NewStringUTF(e);
e = ::buildRE2(matcher->proxyDomainsBuilder, matcher->proxyDomains, options);
if (e) return env->NewStringUTF(e);
return nullptr;
}
JNIEXPORT jint JNICALL
Java_com_github_shadowsocks_acl_AclMatcher_matchHost(JNIEnv *env, jclass clazz, jlong handle,
jstring host) {
if (!handle) return -1;
auto matcher = reinterpret_cast<const AclMatcher *>(handle);
const char *hostChars = env->GetStringUTFChars(host, nullptr);
jint result = 0;
if (matcher->bypassDomains && RE2::PartialMatch(hostChars, *matcher->bypassDomains)) result = 1;
else if (matcher->proxyDomains && RE2::PartialMatch(hostChars, *matcher->proxyDomains)) result = 2;
env->ReleaseStringUTFChars(host, hostChars);
return result;
}
}
libev @ 31ca40b7
Subproject commit 31ca40b7a18ed424213a4ecba0d57706ed3e56a0
Subproject commit b732443c442239c2e0184820e9b23cca0de0828c
Subproject commit 9f4f8eec93dd1f32d78e0bcceddbef0ca570e66f
/*************************************************
* Perl-Compatible Regular Expressions *
*************************************************/
/* This file contains character tables that are used when no external tables
are passed to PCRE by the application that calls it. The tables are used only
for characters whose code values are less than 256.
This is a default version of the tables that assumes ASCII encoding. A program
called dftables (which is distributed with PCRE) can be used to build
alternative versions of this file. This is necessary if you are running in an
EBCDIC environment, or if you want to default to a different encoding, for
example ISO-8859-1. When dftables is run, it creates these tables in the
current locale. If PCRE is configured with --enable-rebuild-chartables, this
happens automatically.
The following #includes are present because without them gcc 4.x may remove the
array definition from the final binary if PCRE is built into a static library
and dead code stripping is activated. This leads to link errors. Pulling in the
header ensures that the array gets flagged as "someone outside this compilation
unit might reference this" and so it will always be supplied to the linker. */
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include "pcre_internal.h"
const pcre_uint8 PRIV(default_tables)[] = {
/* This table is a lower casing table. */
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 97, 98, 99,100,101,102,103,
104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,
120,121,122, 91, 92, 93, 94, 95,
96, 97, 98, 99,100,101,102,103,
104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,
120,121,122,123,124,125,126,127,
128,129,130,131,132,133,134,135,
136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,
152,153,154,155,156,157,158,159,
160,161,162,163,164,165,166,167,
168,169,170,171,172,173,174,175,
176,177,178,179,180,181,182,183,
184,185,186,187,188,189,190,191,
192,193,194,195,196,197,198,199,
200,201,202,203,204,205,206,207,
208,209,210,211,212,213,214,215,
216,217,218,219,220,221,222,223,
224,225,226,227,228,229,230,231,
232,233,234,235,236,237,238,239,
240,241,242,243,244,245,246,247,
248,249,250,251,252,253,254,255,
/* This table is a case flipping table. */
0, 1, 2, 3, 4, 5, 6, 7,
8, 9, 10, 11, 12, 13, 14, 15,
16, 17, 18, 19, 20, 21, 22, 23,
24, 25, 26, 27, 28, 29, 30, 31,
32, 33, 34, 35, 36, 37, 38, 39,
40, 41, 42, 43, 44, 45, 46, 47,
48, 49, 50, 51, 52, 53, 54, 55,
56, 57, 58, 59, 60, 61, 62, 63,
64, 97, 98, 99,100,101,102,103,
104,105,106,107,108,109,110,111,
112,113,114,115,116,117,118,119,
120,121,122, 91, 92, 93, 94, 95,
96, 65, 66, 67, 68, 69, 70, 71,
72, 73, 74, 75, 76, 77, 78, 79,
80, 81, 82, 83, 84, 85, 86, 87,
88, 89, 90,123,124,125,126,127,
128,129,130,131,132,133,134,135,
136,137,138,139,140,141,142,143,
144,145,146,147,148,149,150,151,
152,153,154,155,156,157,158,159,
160,161,162,163,164,165,166,167,
168,169,170,171,172,173,174,175,
176,177,178,179,180,181,182,183,
184,185,186,187,188,189,190,191,
192,193,194,195,196,197,198,199,
200,201,202,203,204,205,206,207,
208,209,210,211,212,213,214,215,
216,217,218,219,220,221,222,223,
224,225,226,227,228,229,230,231,
232,233,234,235,236,237,238,239,
240,241,242,243,244,245,246,247,
248,249,250,251,252,253,254,255,
/* This table contains bit maps for various character classes. Each map is 32
bytes long and the bits run from the least significant end of each byte. The
classes that have their own maps are: space, xdigit, digit, upper, lower, word,
graph, print, punct, and cntrl. Other classes are built from combinations. */
0x00,0x3e,0x00,0x00,0x01,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
0x7e,0x00,0x00,0x00,0x7e,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xfe,0xff,0xff,0x07,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0x07,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0xff,0x03,
0xfe,0xff,0xff,0x87,0xfe,0xff,0xff,0x07,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xfe,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xff,0xff,0xff,0xff,
0xff,0xff,0xff,0xff,0xff,0xff,0xff,0x7f,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0xfe,0xff,0x00,0xfc,
0x01,0x00,0x00,0xf8,0x01,0x00,0x00,0x78,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0xff,0xff,0xff,0xff,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x80,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,
/* This table identifies various classes of character by individual bits:
0x01 white space character
0x02 letter
0x04 decimal digit
0x08 hexadecimal digit
0x10 alphanumeric or '_'
0x80 regular expression metacharacter or binary zero
*/
0x80,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 0- 7 */
0x00,0x01,0x01,0x01,0x01,0x01,0x00,0x00, /* 8- 15 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 16- 23 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 24- 31 */
0x01,0x00,0x00,0x00,0x80,0x00,0x00,0x00, /* - ' */
0x80,0x80,0x80,0x80,0x00,0x00,0x80,0x00, /* ( - / */
0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c,0x1c, /* 0 - 7 */
0x1c,0x1c,0x00,0x00,0x00,0x00,0x00,0x80, /* 8 - ? */
0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* @ - G */
0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* H - O */
0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* P - W */
0x12,0x12,0x12,0x80,0x80,0x00,0x80,0x10, /* X - _ */
0x00,0x1a,0x1a,0x1a,0x1a,0x1a,0x1a,0x12, /* ` - g */
0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* h - o */
0x12,0x12,0x12,0x12,0x12,0x12,0x12,0x12, /* p - w */
0x12,0x12,0x12,0x80,0x80,0x00,0x00,0x00, /* x -127 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 128-135 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 136-143 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 144-151 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 152-159 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 160-167 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 168-175 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 176-183 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 184-191 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 192-199 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 200-207 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 208-215 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 216-223 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 224-231 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 232-239 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00, /* 240-247 */
0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00};/* 248-255 */
/* End of pcre_chartables.c */
pcre @ 222bbf4b
Subproject commit 222bbf4b3fb8e13c21686803e47e31aa3e4ad130
Subproject commit bb8e777557ddbdeabdedea4f23613c5021ffd7b1
Subproject commit 6b5025bf741cff93212ba048353eb3d2c02b5c46
<?xml version="1.0" encoding="utf-8"?>
<resources>
<string-array name="enc_method_entry" translatable="false">
<item>PLAIN</item>
<item>RC4-MD5</item>
<item>AES-128-CFB</item>
<item>AES-192-CFB</item>
......@@ -23,6 +24,7 @@
</string-array>
<string-array name="enc_method_value" translatable="false">
<item>plain</item>
<item>rc4-md5</item>
<item>aes-128-cfb</item>
<item>aes-192-cfb</item>
......@@ -212,4 +214,4 @@
<item>vpn</item>
<item>transproxy</item>
</string-array>
</resources>
\ No newline at end of file
</resources>
Subproject commit 246b6c48bce6d4a83af046bd3feb4843f772b9d2
# https://github.com/arturbosch/detekt/blob/1.5.0/detekt-cli/src/main/resources/default-detekt-config.yml
# https://github.com/arturbosch/detekt/blob/1.7.1/detekt-cli/src/main/resources/default-detekt-config.yml
comments:
active: false
......@@ -12,6 +12,7 @@ complexity:
active: true
threshold: 10
includeStaticDeclarations: false
includePrivateDeclarations: false
ComplexMethod:
active: true
threshold: 15
......@@ -21,7 +22,7 @@ complexity:
nestingFunctions: run,let,apply,with,also,use,forEach,isNotNull,ifNull
LabeledExpression:
active: false
ignoredLabels: ""
ignoredLabels: ''
LargeClass:
active: true
threshold: 600
......@@ -30,8 +31,10 @@ complexity:
threshold: 60
LongParameterList:
active: true
threshold: 6
functionThreshold: 6
constructorThreshold: 7
ignoreDefaultParameters: true
ignoreDataClasses: true
MethodOverloading:
active: false
NestedBlockDepth:
......@@ -39,14 +42,14 @@ complexity:
threshold: 4
StringLiteralDuplication:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
threshold: 3
ignoreAnnotation: true
excludeStringsWithLessThan5Characters: true
ignoreStringsRegex: '$^'
TooManyFunctions:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
thresholdInFiles: 11
thresholdInClasses: 11
thresholdInInterfaces: 11
......@@ -67,7 +70,7 @@ empty-blocks:
active: true
EmptyCatchBlock:
active: true
allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
allowedExceptionNameRegex: '^(_|(ignore|expected).*)'
EmptyClassBlock:
active: true
EmptyDefaultConstructor:
......@@ -91,6 +94,8 @@ empty-blocks:
active: true
EmptySecondaryConstructor:
active: true
EmptyTryBlock:
active: true
EmptyWhenBlock:
active: true
EmptyWhileBlock:
......@@ -115,7 +120,7 @@ exceptions:
SwallowedException:
active: true
ignoredExceptionTypes: 'InterruptedException,NumberFormatException,ParseException,MalformedURLException'
allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
allowedExceptionNameRegex: '^(_|(ignore|expected).*)'
ThrowingExceptionFromFinally:
active: false
ThrowingExceptionInMain:
......@@ -127,7 +132,7 @@ exceptions:
active: true
TooGenericExceptionCaught:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
exceptionNames:
- ArrayIndexOutOfBoundsException
- Error
......@@ -137,7 +142,7 @@ exceptions:
- IndexOutOfBoundsException
- RuntimeException
- Throwable
allowedExceptionNameRegex: "^(_|(ignore|expected).*)"
allowedExceptionNameRegex: '^(_|(ignore|expected).*)'
TooGenericExceptionThrown:
active: true
exceptionNames:
......@@ -166,6 +171,7 @@ formatting:
FinalNewline:
active: true
autoCorrect: true
insertFinalNewLine: true
ImportOrdering:
active: false
Indentation:
......@@ -253,37 +259,37 @@ naming:
ClassNaming:
active: true
classPattern: '[A-Z$][a-zA-Z0-9$]*'
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
ConstructorParameterNaming:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
parameterPattern: '[a-z][A-Za-z0-9]*'
privateParameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
ignoreOverridden: true
EnumNaming:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
enumEntryPattern: '^[A-Z][_a-zA-Z0-9]*'
ForbiddenClassName:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
forbiddenName: ''
FunctionMaxLength:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
maximumFunctionNameLength: 30
FunctionMinLength:
active: false
FunctionNaming:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
functionPattern: '^([a-z$][a-zA-Z$0-9]*)|(`.*`)$'
excludeClassPattern: '$^'
ignoreOverridden: true
FunctionParameterNaming:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
parameterPattern: '[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
ignoreOverridden: true
......@@ -292,33 +298,34 @@ naming:
rootPackage: ''
MatchingDeclarationName:
active: true
mustBeFirst: true
MemberNameEqualsClassName:
active: false
ObjectPropertyNaming:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
constantPattern: '[A-Za-z][_A-Za-z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '(_)?[A-Za-z][_A-Za-z0-9]*'
PackageNaming:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
packagePattern: '^[a-z]+(\.[a-z][A-Za-z0-9]*)*$'
TopLevelPropertyNaming:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
constantPattern: '[A-Z][_A-Z0-9]*'
propertyPattern: '[A-Za-z][_A-Za-z0-9]*'
privatePropertyPattern: '_?[A-Za-z][_A-Za-z0-9]*'
VariableMaxLength:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
maximumVariableNameLength: 64
VariableMinLength:
active: false
VariableNaming:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
variablePattern: '[a-z][A-Za-z0-9]*'
privateVariablePattern: '(_)?[a-z][A-Za-z0-9]*'
excludeClassPattern: '$^'
......@@ -330,10 +337,10 @@ performance:
active: true
ForEachOnRange:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
SpreadOperator:
active: true
excludes: "**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt"
excludes: '**/test/**,**/androidTest/**,**/*.Test.kt,**/*.Spec.kt,**/*.Spek.kt'
UnnecessaryTemporaryInstantiation:
active: true
......@@ -402,11 +409,11 @@ style:
ForbiddenComment:
active: true
values: 'TODO:,FIXME:,STOPSHIP:'
allowedPatterns: ""
allowedPatterns: ''
ForbiddenImport:
active: true
imports: ''
forbiddenPatterns: ""
imports: []
forbiddenPatterns: ''
ForbiddenMethodCall:
active: true
methods: ''
......@@ -421,7 +428,7 @@ style:
active: true
ignoreOverridableFunction: true
excludedFunctions: 'describeContents'
excludeAnnotatedFunction: "dagger.Provides"
excludeAnnotatedFunction: 'dagger.Provides'
LibraryCodeMustSpecifyReturnType:
active: true
LoopWithTooManyJumpStatements:
......@@ -477,8 +484,7 @@ style:
UnderscoresInNumericLiterals:
active: false
UnnecessaryAbstractClass:
active: true
excludeAnnotatedClasses: "dagger.Module"
active: false
UnnecessaryAnnotationUseSiteTarget:
active: true
UnnecessaryApply:
......@@ -497,7 +503,7 @@ style:
active: true
UnusedPrivateMember:
active: true
allowedNames: "(_|ignored|expected|serialVersionUID)"
allowedNames: '(_|ignored|expected|serialVersionUID)'
UseArrayLiteralsInAnnotations:
active: true
UseCheckOrError:
......
distributionBase=GRADLE_USER_HOME
distributionPath=wrapper/dists
distributionUrl=https\://services.gradle.org/distributions/gradle-6.1.1-all.zip
distributionUrl=https\://services.gradle.org/distributions/gradle-6.3-all.zip
zipStoreBase=GRADLE_USER_HOME
zipStorePath=wrapper/dists
......@@ -29,6 +29,9 @@ if "%DIRNAME%" == "" set DIRNAME=.
set APP_BASE_NAME=%~n0
set APP_HOME=%DIRNAME%
@rem Resolve any "." and ".." in APP_HOME to make it shorter.
for %%i in ("%APP_HOME%") do set APP_HOME=%%~fi
@rem Add default JVM options here. You can also use JAVA_OPTS and GRADLE_OPTS to pass JVM options to this script.
set DEFAULT_JVM_OPTS="-Xmx64m" "-Xms64m"
......
......@@ -66,12 +66,12 @@ dependencies {
implementation 'androidx.browser:browser:1.2.0'
implementation 'androidx.constraintlayout:constraintlayout:2.0.0-beta4'
implementation "androidx.lifecycle:lifecycle-runtime-ktx:$lifecycleVersion"
implementation 'com.google.android.gms:play-services-vision:19.0.0'
implementation 'com.google.firebase:firebase-ads:18.3.0'
implementation 'com.google.android.gms:play-services-vision:20.0.0'
implementation 'com.google.firebase:firebase-ads:19.1.0'
implementation 'com.google.zxing:core:3.4.0'
implementation 'com.takisoft.preferencex:preferencex-simplemenu:1.1.0'
implementation 'com.twofortyfouram:android-plugin-api-for-locale:1.0.4'
implementation 'me.zhanghai.android.fastscroll:library:1.1.0'
implementation 'me.zhanghai.android.fastscroll:library:1.1.2'
implementation 'xyz.belvi.mobilevision:barcodescanner:2.0.3'
testImplementation "junit:junit:$junitVersion"
androidTestImplementation "androidx.test:runner:$androidTestVersion"
......
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* Copyright (C) 2020 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2020 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 *
......@@ -18,47 +18,24 @@
* *
*******************************************************************************/
package com.github.shadowsocks.bg
package com.github.shadowsocks
import com.github.shadowsocks.acl.Acl
import com.github.shadowsocks.acl.AclMatcher
import com.github.shadowsocks.net.HostsFile
import com.github.shadowsocks.net.LocalDnsServer
import com.github.shadowsocks.net.Socks5Endpoint
import com.github.shadowsocks.preference.DataStore
import kotlinx.coroutines.CoroutineScope
import java.net.InetSocketAddress
import java.net.URI
import java.net.URISyntaxException
import java.util.*
import android.content.Context
import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.MobileAds
import com.google.android.gms.ads.RequestConfiguration
object LocalDnsService {
private val servers = WeakHashMap<Interface, LocalDnsServer>()
interface Interface : BaseService.Interface {
override suspend fun startProcesses(hosts: HostsFile) {
super.startProcesses(hosts)
val profile = data.proxy!!.profile
val dns = try {
URI("dns://${profile.remoteDns}")
} catch (e: URISyntaxException) {
throw BaseService.ExpectedExceptionWrapper(e)
}
LocalDnsServer(this::resolver,
Socks5Endpoint(dns.host, if (dns.port < 0) 53 else dns.port),
DataStore.proxyAddress,
hosts,
!profile.udpdns,
if (profile.route == Acl.ALL) null else object {
suspend fun createAcl() = AclMatcher().apply { init(profile.route) }
}::createAcl).also {
servers[this] = it
}.start(InetSocketAddress(DataStore.listenAddress, DataStore.portLocalDns))
}
override fun killProcesses(scope: CoroutineScope) {
servers.remove(this)?.shutdown(scope)
super.killProcesses(scope)
}
internal object AdsManager {
init {
MobileAds.setRequestConfiguration(RequestConfiguration.Builder().apply {
setTestDeviceIds(listOf(
"B08FC1764A7B250E91EA9D0D5EBEB208", "7509D18EB8AF82F915874FEF53877A64",
"F58907F28184A828DD0DB6F8E38189C6", "FE983F496D7C5C1878AA163D9420CA97"))
}.build())
}
fun load(context: Context?, setup: AdLoader.Builder.() -> Unit) =
AdLoader.Builder(context, "ca-app-pub-3283768469187309/8632513739").apply(setup).build()
.loadAd(AdRequest.Builder().build())
}
......@@ -91,25 +91,17 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() {
val portTransproxy = findPreference<EditTextPreference>(Key.portTransproxy)!!
portTransproxy.setOnBindEditTextListener(EditTextPreferenceModifiers.Port)
val onServiceModeChange = Preference.OnPreferenceChangeListener { _, newValue ->
val (enabledLocalDns, enabledTransproxy) = when (newValue as String?) {
Key.modeProxy -> Pair(false, false)
Key.modeVpn -> Pair(true, false)
Key.modeTransproxy -> Pair(true, true)
else -> throw IllegalArgumentException("newValue: $newValue")
}
hosts.isEnabled = enabledLocalDns
portLocalDns.isEnabled = enabledLocalDns
portTransproxy.isEnabled = enabledTransproxy
portTransproxy.isEnabled = newValue as String? == Key.modeTransproxy
true
}
val listener: (BaseService.State) -> Unit = {
val stopped = it == BaseService.State.Stopped
tfo.isEnabled = stopped
hosts.isEnabled = stopped
serviceMode.isEnabled = stopped
portProxy.isEnabled = stopped
portLocalDns.isEnabled = stopped
if (stopped) onServiceModeChange.onPreferenceChange(null, DataStore.serviceMode) else {
hosts.isEnabled = false
portLocalDns.isEnabled = false
portTransproxy.isEnabled = false
}
}
......
......@@ -177,7 +177,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
DataStore.dirty = true
true
} catch (exc: RuntimeException) {
Snackbar.make(view!!, exc.readableMessage, Snackbar.LENGTH_LONG).show()
Snackbar.make(requireView(), exc.readableMessage, Snackbar.LENGTH_LONG).show()
false
}
......@@ -217,7 +217,9 @@ class ProfileConfigFragment : PreferenceFragmentCompat(),
plugin.value = pluginConfiguration.selected
pluginConfigure.isEnabled = selected !is NoPlugin
pluginConfigure.text = pluginConfiguration.getOptions().toString()
if (!selected.trusted) Snackbar.make(view!!, R.string.plugin_untrusted, Snackbar.LENGTH_LONG).show()
if (!selected.trusted) {
Snackbar.make(requireView(), R.string.plugin_untrusted, Snackbar.LENGTH_LONG).show()
}
}
REQUEST_CODE_PLUGIN_CONFIGURE -> when (resultCode) {
Activity.RESULT_OK -> {
......
......@@ -59,8 +59,6 @@ import com.github.shadowsocks.utils.readableMessage
import com.github.shadowsocks.widget.ListHolderListener
import com.github.shadowsocks.widget.MainListListener
import com.github.shadowsocks.widget.UndoSnackbarManager
import com.google.android.gms.ads.AdLoader
import com.google.android.gms.ads.AdRequest
import com.google.android.gms.ads.VideoOptions
import com.google.android.gms.ads.formats.NativeAdOptions
import com.google.android.gms.ads.formats.UnifiedNativeAd
......@@ -249,7 +247,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
if (adHost != null || !item.isSponsored) return
if (nativeAdView == null) {
nativeAdView = layoutInflater.inflate(R.layout.ad_unified, adContainer, false) as UnifiedNativeAdView
AdLoader.Builder(context, "ca-app-pub-3283768469187309/8632513739").apply {
AdsManager.load(context) {
forUnifiedNativeAd { unifiedNativeAd ->
// You must call destroy on old ads when you are done with them,
// otherwise you will have a memory leak.
......@@ -262,12 +260,7 @@ class ProfilesFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener {
setStartMuted(true)
}.build())
}.build())
}.build().loadAd(AdRequest.Builder().apply {
addTestDevice("B08FC1764A7B250E91EA9D0D5EBEB208")
addTestDevice("7509D18EB8AF82F915874FEF53877A64")
addTestDevice("F58907F28184A828DD0DB6F8E38189C6")
addTestDevice("FE983F496D7C5C1878AA163D9420CA97")
}.build())
}
} else if (nativeAd != null) populateUnifiedNativeAdView(nativeAd!!, nativeAdView!!)
}
......
......@@ -220,7 +220,7 @@ class SubscriptionFragment : ToolbarFragment(), Toolbar.OnMenuItemClickListener
toolbar.setTitle(R.string.subscriptions)
toolbar.inflateMenu(R.menu.subscription_menu)
toolbar.setOnMenuItemClickListener(this)
SubscriptionService.idle.observe(this) {
SubscriptionService.idle.observe(viewLifecycleOwner) {
toolbar.menu.findItem(R.id.action_update_subscription).isEnabled = it
}
val activity = activity as MainActivity
......
......@@ -20,14 +20,11 @@
<h3>Open Source Licenses</h3>
<ul>
<li>redsocks: <a href="https://github.com/shadowsocks/redsocks/blob/shadowsocks-android/README">APL 2.0</a>&nbsp;</li>
<li>mbed TLS: <a href="https://github.com/ARMmbed/mbedtls/blob/development/LICENSE">APL 2.0</a>&nbsp;</li>
<li>libevent: <a href="https://github.com/shadowsocks/libevent/blob/master/LICENSE">BSD</a>&nbsp;</li>
<li>tun2socks: <a href="https://github.com/shadowsocks/badvpn/blob/shadowsocks-android/COPYING">BSD</a>&nbsp;</li>
<li>pcre: <a href="https://android.googlesource.com/platform/external/pcre/+/master/dist2/LICENCE">BSD</a>&nbsp;</li>
<li>libancillary: <a href="https://github.com/shadowsocks/libancillary/blob/shadowsocks-android/COPYING">BSD</a>&nbsp;</li>
<li>shadowsocks-libev: <a href="https://github.com/shadowsocks/shadowsocks-libev/blob/master/LICENSE">GPLv3</a>&nbsp;</li>
<li>libev: <a href="https://github.com/shadowsocks/libev/blob/master/LICENSE">GPLv2</a>&nbsp;</li>
<li>shadowsocks-rust: <a href="https://github.com/shadowsocks/shadowsocks-rust/blob/master/LICENSE">MIT</a>&nbsp;</li>
<li>libsodium: <a href="https://github.com/jedisct1/libsodium/blob/master/LICENSE">ISC</a>&nbsp;</li>
<li>OpenSSL: <a href="https://www.openssl.org/source/license-openssl-ssleay.txt">OpenSSL License</a>&nbsp;</li>
<li><a href="#other-licenses">Other licenses</a></li>
</ul>
</body>
......
......@@ -47,7 +47,7 @@ mavenPublish {
dependencies {
api 'androidx.core:core-ktx:1.2.0'
api 'androidx.drawerlayout:drawerlayout:1.1.0-alpha03' // https://android-developers.googleblog.com/2019/07/android-q-beta-5-update.html
api 'androidx.drawerlayout:drawerlayout:1.1.0-beta01' // https://android-developers.googleblog.com/2019/07/android-q-beta-5-update.html
api 'com.google.android.material:material:1.1.0'
api "org.jetbrains.kotlin:kotlin-stdlib-jdk8:$kotlinVersion"
testImplementation "junit:junit:$junitVersion"
......
......@@ -41,7 +41,7 @@ abstract class AlertDialogFragment<Arg : Parcelable, Ret : Parcelable> :
}
protected abstract fun AlertDialog.Builder.prepare(listener: DialogInterface.OnClickListener)
protected val arg by lazy { arguments!!.getParcelable<Arg>(KEY_ARG)!! }
protected val arg by lazy { requireArguments().getParcelable<Arg>(KEY_ARG)!! }
protected open fun ret(which: Int): Ret? = null
fun withArg(arg: Arg) = apply { arguments = Bundle().apply { putParcelable(KEY_ARG, arg) } }
......
......@@ -76,15 +76,7 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
private lateinit var portLocalDns: EditTextPreference
private lateinit var portTransproxy: EditTextPreference
private val onServiceModeChange = Preference.OnPreferenceChangeListener { _, newValue ->
val (enabledLocalDns, enabledTransproxy) = when (newValue as String?) {
Key.modeProxy -> Pair(false, false)
Key.modeVpn -> Pair(true, false)
Key.modeTransproxy -> Pair(true, true)
else -> throw IllegalArgumentException("newValue: $newValue")
}
hosts.isEnabled = enabledLocalDns
portLocalDns.isEnabled = enabledLocalDns
portTransproxy.isEnabled = enabledTransproxy
portTransproxy.isEnabled = newValue as String? == Key.modeTransproxy
true
}
private val tester by viewModels<HttpsTest>()
......@@ -125,11 +117,12 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo
val stopped = state == BaseService.State.Stopped
controlImport.isEnabled = stopped
tfo.isEnabled = stopped
hosts.isEnabled = stopped
serviceMode.isEnabled = stopped
shareOverLan.isEnabled = stopped
portProxy.isEnabled = stopped
portLocalDns.isEnabled = stopped
if (stopped) onServiceModeChange.onPreferenceChange(null, DataStore.serviceMode) else {
portLocalDns.isEnabled = false
portTransproxy.isEnabled = false
}
}
......
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