Commit 1421bb70 authored by Mygod's avatar Mygod

Make acl init async

parent 8b4b2409
...@@ -28,11 +28,15 @@ import com.github.shadowsocks.Core ...@@ -28,11 +28,15 @@ import com.github.shadowsocks.Core
import com.github.shadowsocks.net.Subnet import com.github.shadowsocks.net.Subnet
import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.DataStore
import com.github.shadowsocks.utils.asIterable import com.github.shadowsocks.utils.asIterable
import kotlinx.coroutines.Job
import kotlinx.coroutines.ensureActive
import kotlinx.coroutines.runBlocking
import java.io.File import java.io.File
import java.io.IOException import java.io.IOException
import java.io.Reader import java.io.Reader
import java.net.URL import java.net.URL
import java.net.URLConnection import java.net.URLConnection
import kotlin.coroutines.coroutineContext
class Acl { class Acl {
companion object { companion object {
...@@ -65,7 +69,7 @@ class Acl { ...@@ -65,7 +69,7 @@ class Acl {
value.proxyHostnames.size() == 0 && value.urls.size() == 0) null else value.toString()) value.proxyHostnames.size() == 0 && value.urls.size() == 0) null else value.toString())
fun save(id: String, acl: Acl) = getFile(id).writeText(acl.toString()) fun save(id: String, acl: Acl) = getFile(id).writeText(acl.toString())
fun <T> parse(reader: Reader, bypassHostnames: (String) -> T, proxyHostnames: (String) -> T, suspend fun <T> parse(reader: Reader, bypassHostnames: (String) -> T, proxyHostnames: (String) -> T,
urls: ((URL) -> T)? = null, defaultBypass: Boolean = false): Pair<Boolean, List<Subnet>> { urls: ((URL) -> T)? = null, defaultBypass: Boolean = false): Pair<Boolean, List<Subnet>> {
var bypass = defaultBypass var bypass = defaultBypass
val bypassSubnets = mutableListOf<Subnet>() val bypassSubnets = mutableListOf<Subnet>()
...@@ -74,6 +78,7 @@ class Acl { ...@@ -74,6 +78,7 @@ class Acl {
var subnets: MutableList<Subnet>? = if (defaultBypass) proxySubnets else bypassSubnets var subnets: MutableList<Subnet>? = if (defaultBypass) proxySubnets else bypassSubnets
reader.useLines { reader.useLines {
for (line in it) { for (line in it) {
coroutineContext[Job]!!.ensureActive()
val input = (if (urls == null) line else { val input = (if (urls == null) line else {
val blocks = line.split('#', limit = 2) val blocks = line.split('#', limit = 2)
val url = networkAclParser.matchEntire(blocks.getOrElse(1) { "" })?.groupValues?.getOrNull(1) val url = networkAclParser.matchEntire(blocks.getOrElse(1) { "" })?.groupValues?.getOrNull(1)
...@@ -150,7 +155,9 @@ class Acl { ...@@ -150,7 +155,9 @@ class Acl {
proxyHostnames.clear() proxyHostnames.clear()
subnets.clear() subnets.clear()
urls.clear() urls.clear()
val (bypass, subnets) = parse(reader, bypassHostnames::add, proxyHostnames::add, urls::add, defaultBypass) val (bypass, subnets) = runBlocking {
parse(reader, bypassHostnames::add, proxyHostnames::add, urls::add, defaultBypass)
}
this.bypass = bypass this.bypass = bypass
for (item in subnets) this.subnets.add(item) for (item in subnets) this.subnets.add(item)
return this return this
......
...@@ -20,20 +20,23 @@ ...@@ -20,20 +20,23 @@
package com.github.shadowsocks.acl package com.github.shadowsocks.acl
import android.util.Log
import com.github.shadowsocks.net.Subnet import com.github.shadowsocks.net.Subnet
import java.net.Inet4Address import java.net.Inet4Address
import java.net.Inet6Address import java.net.Inet6Address
import kotlin.system.measureNanoTime
class AclMatcher(id: String) { class AclMatcher {
private val subnetsIpv4: List<Subnet.Immutable> private var subnetsIpv4 = emptyList<Subnet.Immutable>()
private val subnetsIpv6: List<Subnet.Immutable> private var subnetsIpv6 = emptyList<Subnet.Immutable>()
private val bypassDomains = mutableListOf<Regex>() private val bypassDomains = mutableListOf<Regex>()
private val proxyDomains = mutableListOf<Regex>() private val proxyDomains = mutableListOf<Regex>()
private val bypass: Boolean private var bypass = false
init { suspend fun init(id: String) {
val time = measureNanoTime {
val (bypass, subnets) = Acl.parse(Acl.getFile(id).bufferedReader(), { val (bypass, subnets) = Acl.parse(Acl.getFile(id).bufferedReader(), {
// bypassDomains.add(it.toRegex()) // bypassDomains.add(it.toRegex())
}, { }, {
if (it.startsWith("(?:^|\\.)googleapis")) proxyDomains.add(it.toRegex()) if (it.startsWith("(?:^|\\.)googleapis")) proxyDomains.add(it.toRegex())
}) })
...@@ -41,6 +44,8 @@ class AclMatcher(id: String) { ...@@ -41,6 +44,8 @@ class AclMatcher(id: String) {
subnetsIpv6 = subnets.filter { it.address is Inet6Address }.map { it.toImmutable() } subnetsIpv6 = subnets.filter { it.address is Inet6Address }.map { it.toImmutable() }
this.bypass = bypass this.bypass = bypass
} }
Log.d("AclMatcher", "ACL initialized in $time ns")
}
fun shouldBypassIpv4(ip: ByteArray) = bypass xor subnetsIpv4.any { it.matches(ip) } fun shouldBypassIpv4(ip: ByteArray) = bypass xor subnetsIpv4.any { it.matches(ip) }
fun shouldBypassIpv6(ip: ByteArray) = bypass xor subnetsIpv6.any { it.matches(ip) } fun shouldBypassIpv6(ip: ByteArray) = bypass xor subnetsIpv6.any { it.matches(ip) }
......
...@@ -48,8 +48,9 @@ object LocalDnsService { ...@@ -48,8 +48,9 @@ object LocalDnsService {
Socks5Endpoint(dns.host, if (dns.port < 0) 53 else dns.port), Socks5Endpoint(dns.host, if (dns.port < 0) 53 else dns.port),
DataStore.proxyAddress, DataStore.proxyAddress,
hosts, hosts,
!profile.udpdns, !profile.udpdns) {
if (profile.route == Acl.ALL) null else AclMatcher(profile.route)).also { if (profile.route == Acl.ALL) null else AclMatcher().apply { init(profile.route) }
}.also {
servers[this] = it servers[this] = it
}.start(InetSocketAddress(DataStore.listenAddress, DataStore.portLocalDns)) }.start(InetSocketAddress(DataStore.listenAddress, DataStore.portLocalDns))
} }
......
...@@ -51,7 +51,7 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd ...@@ -51,7 +51,7 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd
* Forward UDP queries to TCP. * Forward UDP queries to TCP.
*/ */
private val tcp: Boolean = false, private val tcp: Boolean = false,
private val acl: AclMatcher? = null) : CoroutineScope { private val aclSpawn: suspend () -> AclMatcher? = { null }) : CoroutineScope {
companion object { companion object {
private const val TAG = "LocalDnsServer" private const val TAG = "LocalDnsServer"
private const val TIMEOUT = 10_000L private const val TIMEOUT = 10_000L
...@@ -84,6 +84,7 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd ...@@ -84,6 +84,7 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd
override val coroutineContext = SupervisorJob() + CoroutineExceptionHandler { _, t -> override val coroutineContext = SupervisorJob() + CoroutineExceptionHandler { _, t ->
if (t is IOException) Crashlytics.log(Log.WARN, TAG, t.message) else printLog(t) if (t is IOException) Crashlytics.log(Log.WARN, TAG, t.message) else printLog(t)
} }
private val acl = async { aclSpawn() }
suspend fun start(listen: SocketAddress) = DatagramChannel.open().run { suspend fun start(listen: SocketAddress) = DatagramChannel.open().run {
configureBlocking(false) configureBlocking(false)
...@@ -128,7 +129,7 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd ...@@ -128,7 +129,7 @@ class LocalDnsServer(private val localResolver: suspend (String) -> Array<InetAd
remote.cancel() remote.cancel()
return@supervisorScope cookDnsResponse(request, hostsResults) return@supervisorScope cookDnsResponse(request, hostsResults)
} }
if (acl == null) return@supervisorScope remote.await() val acl = acl.await() ?: return@supervisorScope remote.await()
val useLocal = when (acl.shouldBypass(host)) { val useLocal = when (acl.shouldBypass(host)) {
true -> true.also { remote.cancel() } true -> true.also { remote.cancel() }
false -> return@supervisorScope remote.await() false -> return@supervisorScope remote.await()
......
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