Commit e51f1779 authored by Max Lv's avatar Max Lv

Revert to local UDS resolver

Fix #2650
parent a1b52e0c
...@@ -253,7 +253,7 @@ object BaseService { ...@@ -253,7 +253,7 @@ object BaseService {
File(Core.deviceStorage.noBackupFilesDir, "stat_udp"), File(Core.deviceStorage.noBackupFilesDir, "stat_udp"),
File(configRoot, CONFIG_FILE_UDP), File(configRoot, CONFIG_FILE_UDP),
"-u", false) "-u", false)
data.localDns = LocalDnsWorker(this::rawResolver, DataStore.portLocalDns + 1).apply { start() } data.localDns = LocalDnsWorker(this::rawResolver).apply { start() }
} }
fun startRunner() { fun startRunner() {
......
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import com.github.shadowsocks.net.ConcurrentUdpSocketListener import android.net.LocalSocket
import com.github.shadowsocks.Core
import com.github.shadowsocks.net.ConcurrentLocalSocketListener
import com.github.shadowsocks.net.DnsResolverCompat import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.utils.readableMessage import com.github.shadowsocks.utils.readableMessage
import kotlinx.coroutines.CancellationException import kotlinx.coroutines.CancellationException
...@@ -10,45 +12,47 @@ import kotlinx.coroutines.launch ...@@ -10,45 +12,47 @@ import kotlinx.coroutines.launch
import org.xbill.DNS.Message import org.xbill.DNS.Message
import org.xbill.DNS.Rcode import org.xbill.DNS.Rcode
import timber.log.Timber import timber.log.Timber
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.File
import java.io.IOException import java.io.IOException
import java.net.SocketAddress
import java.nio.ByteBuffer
import java.nio.channels.DatagramChannel
class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray, port: Int) : ConcurrentUdpSocketListener( class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray) : ConcurrentLocalSocketListener(
"LocalDnsThread", port), CoroutineScope { "LocalDnsThread", File(Core.deviceStorage.noBackupFilesDir, "local_dns_path")), CoroutineScope {
override fun acceptInternal(socket: LocalSocket) = error("big no no")
override fun handle(channel: DatagramChannel, sender: SocketAddress, query: ByteBuffer) { override fun accept(socket: LocalSocket) {
launch { launch {
query.flip() socket.use {
val data = ByteArray(query.remaining()) val input = DataInputStream(socket.inputStream)
query.get(data) val query = try {
try { ByteArray(input.readUnsignedShort()).also { input.read(it) }
resolver(data) } catch (e: IOException) { // connection early close possibly due to resolving timeout
} catch (e: Exception) { return@use Timber.d(e)
when (e) {
is TimeoutCancellationException -> Timber.w("Resolving timed out")
is CancellationException -> {
} // ignore
is IOException -> Timber.d(e)
else -> Timber.w(e)
}
try {
DnsResolverCompat.prepareDnsResponse(Message(data)).apply {
header.rcode = Rcode.SERVFAIL
}.toWire()
} catch (_: IOException) {
byteArrayOf() // return empty if cannot parse packet
} }
}?.let { r ->
try { try {
val response = ByteBuffer.allocate(r.size) resolver(query)
response.clear() } catch (e: Exception) {
response.put(r) when (e) {
response.flip() is TimeoutCancellationException -> Timber.w("Resolving timed out")
channel.send(response, sender) is CancellationException -> { } // ignore
} catch (e: IOException) { is IOException -> Timber.d(e)
Timber.d(e.readableMessage) else -> Timber.w(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 (e: IOException) {
Timber.d(e.readableMessage)
}
} }
} }
} }
......
...@@ -91,7 +91,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro ...@@ -91,7 +91,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
}.let { dns -> }.let { dns ->
cmd += arrayListOf( cmd += arrayListOf(
"--dns-addr", "${DataStore.listenAddress}:${DataStore.portLocalDns}", "--dns-addr", "${DataStore.listenAddress}:${DataStore.portLocalDns}",
"--local-dns-addr", "127.0.0.1:${DataStore.portLocalDns + 1}", "--local-dns-addr", "local_dns_path",
"--remote-dns-addr", "${dns.host ?: "0.0.0.0"}:${if (dns.port < 0) 53 else dns.port}") "--remote-dns-addr", "${dns.host ?: "0.0.0.0"}:${if (dns.port < 0) 53 else dns.port}")
} }
......
/*******************************************************************************
* *
* 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 kotlinx.coroutines.*
import timber.log.Timber
abstract class ConcurrentUdpSocketListener(name: String, port: Int) : UdpSocketListener(name, port),
CoroutineScope {
override val coroutineContext = Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler { _, t -> Timber.w(t) }
override fun shutdown(scope: CoroutineScope) {
running = false
cancel()
super.shutdown(scope)
coroutineContext[Job]!!.also { job -> scope.launch { job.join() } }
}
}
/*******************************************************************************
* *
* Copyright (C) 2017 by Max Lv <max.c.lv@gmail.com> *
* Copyright (C) 2017 by Mygod Studio <contact-shadowsocks-android@mygod.be> *
* *
* This program is free software: you can redistribute it and/or modify *
* it under the terms of the GNU General Public License as published by *
* the Free Software Foundation, either version 3 of the License, or *
* (at your option) any later version. *
* *
* This program is distributed in the hope that it will be useful, *
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
* GNU General Public License for more details. *
* *
* You should have received a copy of the GNU General Public License *
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
* *
*******************************************************************************/
package com.github.shadowsocks.net
import android.annotation.SuppressLint
import kotlinx.coroutines.CoroutineScope
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.channels.sendBlocking
import kotlinx.coroutines.launch
import timber.log.Timber
import java.io.IOException
import java.net.InetSocketAddress
import java.net.SocketAddress
import java.nio.ByteBuffer
import java.nio.channels.DatagramChannel
abstract class UdpSocketListener(name: String, val port: Int) : Thread(name) {
private val udpChannel = DatagramChannel.open()
private val closeChannel = Channel<Unit>(1)
@Volatile
protected var running = true
/**
* Inherited class do not need to close input/output streams as they will be closed automatically.
*/
protected abstract fun handle(channel: DatagramChannel, sender: SocketAddress, query: ByteBuffer)
final override fun run() {
udpChannel.socket().bind(InetSocketAddress(port))
udpChannel.configureBlocking(true)
udpChannel.use {
while (running) {
try {
val query = ByteBuffer.allocate(65536)
query.clear()
udpChannel.receive(query)?.let { handle(udpChannel, it, query) }
} catch (e: IOException) {
if (running) Timber.w(e)
continue
}
}
}
closeChannel.sendBlocking(Unit)
}
@SuppressLint("NewApi")
open fun shutdown(scope: CoroutineScope) {
running = false
udpChannel.close()
scope.launch { closeChannel.receive() }
}
}
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