Commit 1421bb70 authored by Mygod's avatar Mygod

Make acl init async

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