Skip to content

Commit

Permalink
update
Browse files Browse the repository at this point in the history
  • Loading branch information
bannedbook committed Mar 2, 2020
1 parent c5ed3c3 commit 67dc983
Show file tree
Hide file tree
Showing 55 changed files with 492 additions and 289 deletions.
6 changes: 3 additions & 3 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -14,8 +14,8 @@ buildscript {
junitVersion = '4.13'
androidTestVersion = '1.2.0'
androidEspressoVersion = '3.2.0'
versionCode = 5000351
versionName = '5.0.3.1-nightly'
versionCode = 5000550
versionName = '5.0.5-nightly'
resConfigs = ['ar', 'es', 'fa', 'fr', 'ja', 'ko', 'ru', 'tr', 'zh-rCN', 'zh-rTW']
}

Expand All @@ -32,7 +32,7 @@ buildscript {
classpath 'com.github.ben-manes:gradle-versions-plugin:0.27.0'
classpath 'com.google.android.gms:oss-licenses-plugin:0.9.5'
classpath 'com.google.gms:google-services:4.3.3'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.8.0'
classpath 'com.vanniktech:gradle-maven-publish-plugin:0.9.0'
classpath 'io.fabric.tools:gradle:1.31.2'
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:$kotlinVersion"
}
Expand Down
6 changes: 3 additions & 3 deletions core/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -52,10 +52,10 @@ androidExtensions {

def coroutinesVersion = '1.3.3'
def roomVersion = '2.2.3'
def workVersion = '2.3.0'
def workVersion = '2.3.1'
dependencies {
api project(':plugin')
api 'androidx.fragment:fragment-ktx:1.2.0'
api 'androidx.fragment:fragment-ktx:1.2.1'
api "androidx.lifecycle:lifecycle-common-java8:$lifecycleVersion"
api "androidx.lifecycle:lifecycle-livedata-core-ktx:$lifecycleVersion"
api 'androidx.preference:preference:1.1.0'
Expand All @@ -66,7 +66,7 @@ dependencies {
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.0'
api 'com.google.firebase:firebase-config:19.1.1'
api 'dnsjava:dnsjava:2.1.9'
api "org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion"
api "org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutinesVersion"
Expand Down
1 change: 1 addition & 0 deletions core/src/main/AndroidManifest.xml
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
<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-permission-sdk-23 android:name="android.permission.WRITE_EXTERNAL_STORAGE"/>

<uses-feature android:name="android.software.leanback"
android:required="false"/>
Expand Down
8 changes: 4 additions & 4 deletions core/src/main/java/com/github/shadowsocks/acl/AclMatcher.kt
Original file line number Diff line number Diff line change
Expand Up @@ -20,13 +20,11 @@

package com.github.shadowsocks.acl

import android.util.Log
import com.github.shadowsocks.Core
import com.github.shadowsocks.net.Subnet
import java.io.Reader
import java.net.Inet4Address
import java.net.Inet6Address
import kotlin.system.measureNanoTime

class AclMatcher : AutoCloseable {
companion object {
Expand Down Expand Up @@ -71,7 +69,7 @@ class AclMatcher : AutoCloseable {
}.toList()
check(handle == 0L)
handle = init()
val time = measureNanoTime {
try {
val (bypass, subnets) = Acl.parse(reader, {
check(addBypassDomain(handle, it))
}, {
Expand All @@ -81,8 +79,10 @@ class AclMatcher : AutoCloseable {
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
}
Log.d("AclMatcher", "ACL initialized in $time ns")
}

private fun quickMatches(subnets: List<Subnet.Immutable>, ip: ByteArray): Boolean {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -242,7 +242,7 @@ object BaseService {
File(Core.deviceStorage.noBackupFilesDir, "stat_main"),
File(configRoot, CONFIG_FILE),
if (udpFallback == null) "-u" else null)
check(udpFallback?.pluginPath == null) { "UDP fallback cannot have plugins" }
check(udpFallback?.plugin == null) { "UDP fallback cannot have plugins" }
udpFallback?.start(this,
File(Core.deviceStorage.noBackupFilesDir, "stat_udp"),
File(configRoot, CONFIG_FILE_UDP),
Expand Down
23 changes: 12 additions & 11 deletions core/src/main/java/com/github/shadowsocks/bg/DnsResolverCompat.kt
Original file line number Diff line number Diff line change
Expand Up @@ -38,8 +38,6 @@ import com.github.shadowsocks.utils.printLog
import kotlinx.coroutines.*
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
Expand Down Expand Up @@ -73,21 +71,24 @@ sealed class DnsResolverCompat {
socket.closeQuietly()
}
true
} catch (_: IOException) {
false
} catch (e: IOException) {
if ((e.cause as? ErrnoException)?.errno == OsConstants.EPERM) checkConnectivity(network, addr) else false
} catch (_: ErrnoException) {
false
} catch (e: ReflectiveOperationException) {
check(Build.VERSION.SDK_INT < 23)
printLog(e)
val addresses = Core.connectivity.getLinkProperties(network)?.linkAddresses
true == when (addr) {
is Inet4Address -> addresses?.any { it.address is Inet4Address && !it.address.isLinkLocalAddress }
is Inet6Address -> addresses?.any {
it.address.run { this is Inet6Address && !isLinkLocalAddress && !isIPv4CompatibleAddress }
checkConnectivity(network, addr)
}
private fun checkConnectivity(network: Network, addr: InetAddress): Boolean {
return Core.connectivity.getLinkProperties(network)?.routes?.any {
try {
it.matches(addr)
} catch (e: RuntimeException) {
printLog(e)
false
}
else -> error("Unknown address type")
}
} == true
}

override fun bindSocket(network: Network, socket: FileDescriptor) = instance.bindSocket(network, socket)
Expand Down
7 changes: 3 additions & 4 deletions core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,8 +47,7 @@ import java.security.MessageDigest
class ProxyInstance(val profile: Profile, private val route: String = profile.route) {
private var configFile: File? = null
var trafficMonitor: TrafficMonitor? = null
private val plugin = PluginConfiguration(profile.plugin ?: "").selectedOptions
val pluginPath by lazy { PluginManager.init(plugin) }
val plugin by lazy { PluginManager.init(PluginConfiguration(profile.plugin ?: "")) }
private var scheduleConfigUpdate = false

suspend fun init(service: BaseService.Interface, hosts: HostsFile) {
Expand Down Expand Up @@ -85,7 +84,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro

this.configFile = configFile
val config = profile.toJson()
if (pluginPath != null) config.put("plugin", pluginPath).put("plugin_opts", plugin.toString())
plugin?.let { (path, opts) -> config.put("plugin", path).put("plugin_opts", opts.toString()) }
configFile.writeText(config.toString())

val cmd = service.buildAdditionalArguments(arrayListOf(
Expand All @@ -103,7 +102,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
}

// 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 || profile.route == Acl.BYPASS_LAN) cmd += "-D"
if (profile.route == Acl.ALL) cmd += "-D"

if (DataStore.tcpFastOpen) cmd += "--fast-open"

Expand Down
20 changes: 13 additions & 7 deletions core/src/main/java/com/github/shadowsocks/bg/VpnService.kt
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,7 @@ import android.net.Network
import android.os.Build
import android.os.ParcelFileDescriptor
import android.system.ErrnoException
import android.system.OsConstants
import com.github.shadowsocks.Core
import com.github.shadowsocks.VpnRequestActivity
import com.github.shadowsocks.acl.Acl
Expand Down Expand Up @@ -72,8 +73,11 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
DnsResolverCompat.bindSocket(network, fd)
return@let true
} catch (e: IOException) {
// suppress ENONET (Machine is not on the network)
if ((e.cause as? ErrnoException)?.errno != 64) printLog(e)
when ((e.cause as? ErrnoException)?.errno) {
// also suppress ENONET (Machine is not on the network)
OsConstants.EPERM, 64 -> e.printStackTrace()
else -> printLog(e)
}
return@let false
} catch (e: ReflectiveOperationException) {
check(Build.VERSION.SDK_INT < 23)
Expand Down Expand Up @@ -161,10 +165,7 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
.addAddress(PRIVATE_VLAN4_CLIENT, 30)
.addDnsServer(PRIVATE_VLAN4_ROUTER)

if (profile.ipv6) {
builder.addAddress(PRIVATE_VLAN6_CLIENT, 126)
builder.addRoute("::", 0)
}
if (profile.ipv6) builder.addAddress(PRIVATE_VLAN6_CLIENT, 126)

if (profile.proxyApps) {
val me = packageName
Expand All @@ -182,13 +183,18 @@ class VpnService : BaseVpnService(), LocalDnsService.Interface {
}

when (profile.route) {
Acl.ALL, Acl.BYPASS_CHN, Acl.CUSTOM_RULES -> builder.addRoute("0.0.0.0", 0)
Acl.ALL, Acl.BYPASS_CHN, Acl.CUSTOM_RULES -> {
builder.addRoute("0.0.0.0", 0)
if (profile.ipv6) builder.addRoute("::", 0)
}
else -> {
resources.getStringArray(R.array.bypass_private_route).forEach {
val subnet = Subnet.fromString(it)!!
builder.addRoute(subnet.address.hostAddress, subnet.prefixSize)
}
builder.addRoute(PRIVATE_VLAN4_ROUTER, 32)
// https://issuetracker.google.com/issues/149636790
if (profile.ipv6) builder.addRoute("2000::", 3)
}
}

Expand Down
21 changes: 11 additions & 10 deletions core/src/main/java/com/github/shadowsocks/database/Profile.kt
Original file line number Diff line number Diff line change
Expand Up @@ -260,7 +260,7 @@ data class Profile(
}.filterNotNull()

private class JsonParser(private val feature: Profile? = null) : ArrayList<Profile>() {
private val fallbackMap = mutableMapOf<Profile, Profile>()
val fallbackMap = mutableMapOf<Profile, Profile>()

private val JsonElement?.optString get() = (this as? JsonPrimitive)?.asString
private val JsonElement?.optBoolean
Expand Down Expand Up @@ -321,27 +321,28 @@ data class Profile(
}
}

fun finalize(create: (Profile) -> Unit) {
fun finalize(create: (Profile) -> Profile) {
val profiles = ProfileManager.getAllProfiles() ?: emptyList()
for ((profile, fallback) in fallbackMap) {
val match = profiles.firstOrNull {
fallback.host == it.host && fallback.remotePort == it.remotePort &&
fallback.password == it.password && fallback.method == it.method &&
it.plugin.isNullOrEmpty()
}
profile.udpFallback = if (match == null) {
create(fallback)
fallback.id
} else match.id
profile.udpFallback = (match ?: create(fallback)).id
ProfileManager.updateProfile(profile)
}
}
}

fun parseJson(json: JsonElement, feature: Profile? = null, create: (Profile) -> Unit) {
fun parseJson(json: JsonElement, feature: Profile? = null, create: (Profile) -> Profile) {
JsonParser(feature).run {
process(json)
for (profile in this) create(profile)
for (i in indices) {
val fallback = fallbackMap.remove(this[i])
this[i] = create(this[i])
fallback?.also { fallbackMap[this[i]] = it }
}
finalize(create)
}
}
Expand Down Expand Up @@ -404,7 +405,7 @@ data class Profile(
.encodedAuthority("$auth@$wrappedHost:$remotePort")
val configuration = PluginConfiguration(plugin ?: "")
if (configuration.selected.isNotEmpty()) {
builder.appendQueryParameter(Key.plugin, configuration.selectedOptions.toString(false))
builder.appendQueryParameter(Key.plugin, configuration.getOptions().toString(false))
}
if (!name.isNullOrEmpty()) builder.fragment(name)
return builder.build()
Expand All @@ -421,7 +422,7 @@ data class Profile(
put("password", password)
put("method", method)
if (profiles == null) return@apply
PluginConfiguration(plugin ?: "").selectedOptions.also {
PluginConfiguration(plugin ?: "").getOptions().also {
if (it.id.isNotEmpty()) {
put("plugin", it.id)
put("plugin_opts", it.toString())
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -205,7 +205,9 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd
fun shutdown(scope: CoroutineScope) {
cancel()
monitor.close(scope)
coroutineContext[Job]!!.also { job -> scope.launch { job.join() } }
acl?.also { scope.launch { it.await().close() } }
scope.launch {
this@LocalDnsServer.coroutineContext[Job]!!.join()
acl?.also { it.await().close() }
}
}
}
7 changes: 2 additions & 5 deletions core/src/main/java/com/github/shadowsocks/net/Subnet.kt
Original file line number Diff line number Diff line change
Expand Up @@ -57,8 +57,6 @@ class Subnet(val address: InetAddress, val prefixSize: Int) : Comparable<Subnet>
}
}

fun Byte.toPositiveInt() = toInt() and 0xFF

fun matches(b: Immutable) = matches(b.a)
fun matches(b: ByteArray): Boolean {
if (a.size != b.size) return false
Expand All @@ -67,14 +65,13 @@ class Subnet(val address: InetAddress, val prefixSize: Int) : Comparable<Subnet>
if (a[i] != b[i]) return false
++i
}
val mask = 256 - (1 shl i * 8 + 8 - prefixSize)
return i * 8 == prefixSize || a[i].toPositiveInt() == b[i].toPositiveInt() and mask
return i * 8 == prefixSize || a[i] == (b[i].toInt() and -(1 shl i * 8 + 8 - prefixSize)).toByte()
}
}
fun toImmutable() = Immutable(address.address.also {
var i = prefixSize / 8
if (prefixSize % 8 > 0) {
it[i] = (it[i].toInt() and 256 - (1 shl i * 8 + 8 - prefixSize)).toByte()
it[i] = (it[i].toInt() and -(1 shl i * 8 + 8 - prefixSize)).toByte()
++i
}
while (i < it.size) it[i++] = 0
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,11 @@
package com.github.shadowsocks.plugin

import android.content.pm.ResolveInfo
import android.os.Bundle

class NativePlugin(resolveInfo: ResolveInfo) : ResolvedPlugin(resolveInfo) {
init {
check(resolveInfo.providerInfo != null)
}

override val metaData: Bundle get() = resolveInfo.providerInfo.metaData
override val packageName: String get() = resolveInfo.providerInfo.packageName
override val componentInfo get() = resolveInfo.providerInfo!!
}
9 changes: 9 additions & 0 deletions core/src/main/java/com/github/shadowsocks/plugin/Plugin.kt
Original file line number Diff line number Diff line change
Expand Up @@ -24,9 +24,18 @@ import android.graphics.drawable.Drawable

abstract class Plugin {
abstract val id: String
open val idAliases get() = emptyArray<String>()
abstract val label: CharSequence
open val icon: Drawable? get() = null
open val defaultConfig: String? get() = null
open val packageName: String get() = ""
open val trusted: Boolean get() = true
open val directBootAware: Boolean get() = true

override fun equals(other: Any?): Boolean {
if (this === other) return true
if (javaClass != other?.javaClass) return false
return id == (other as Plugin).id
}
override fun hashCode() = id.hashCode()
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,15 @@ class PluginConfiguration(val pluginsOptions: Map<String, PluginOptions>, val se
} else PluginOptions(line)
})

fun getOptions(id: String): PluginOptions = if (id.isEmpty()) PluginOptions() else
pluginsOptions[id] ?: PluginOptions(id, PluginManager.fetchPlugins()[id]?.defaultConfig)
val selectedOptions: PluginOptions get() = getOptions(selected)
fun getOptions(
id: String = selected,
defaultConfig: () -> String? = { PluginManager.fetchPlugins().lookup[id]?.defaultConfig }
) = if (id.isEmpty()) PluginOptions() else pluginsOptions[id] ?: PluginOptions(id, defaultConfig())

override fun toString(): String {
val result = LinkedList<PluginOptions>()
for ((id, opt) in pluginsOptions) if (id == this.selected) result.addFirst(opt) else result.addLast(opt)
if (!pluginsOptions.contains(selected)) result.addFirst(selectedOptions)
if (!pluginsOptions.contains(selected)) result.addFirst(getOptions())
return result.joinToString("\n") { it.toString(false) }
}
}
Loading

0 comments on commit 67dc983

Please sign in to comment.