Unverified Commit 700f4d26 authored by Max Lv's avatar Max Lv Committed by GitHub

Switch to local UDP DNS resolver (#2635)

* Switch to the local UDP DNS resolver

* Update shadowsocks-rust

* Revert the rustup commands

* Fix #2642
parent f44de5be
...@@ -7,11 +7,10 @@ jobs: ...@@ -7,11 +7,10 @@ jobs:
environment: environment:
GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy="in-process" GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy="in-process"
steps: steps:
- run: rustup toolchain install nightly-2020-12-20
- run: rustup override set nightly-2020-12-20
- run: rustup target install armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
- checkout - checkout
- run: git submodule update --init --recursive - run: git submodule update --init --recursive
- run: rustup update
- run: cd core/src/main/rust/shadowsocks-rust && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
- restore_cache: - restore_cache:
key: jars-{{ checksum "build.gradle.kts" }} key: jars-{{ checksum "build.gradle.kts" }}
- run: - run:
......
...@@ -21,9 +21,8 @@ for Android TV ([beta](https://play.google.com/apps/testing/com.github.shadowsoc ...@@ -21,9 +21,8 @@ for Android TV ([beta](https://play.google.com/apps/testing/com.github.shadowsoc
* Rust with Android targets installed * Rust with Android targets installed
```bash ```bash
$ rustup toolchain install nightly-2020-12-20 $ cd core/src/main/rust/shadowsocks-rust
$ rustup override set nightly-2020-12-20 $ rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
$ rustup target install armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
``` ```
### BUILD ### BUILD
......
...@@ -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).apply { start() } data.localDns = LocalDnsWorker(this::rawResolver, DataStore.portLocalDns + 1).apply { start() }
} }
fun startRunner() { fun startRunner() {
......
package com.github.shadowsocks.bg package com.github.shadowsocks.bg
import android.net.LocalSocket import com.github.shadowsocks.net.ConcurrentUdpSocketListener
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
...@@ -12,47 +10,45 @@ import kotlinx.coroutines.launch ...@@ -12,47 +10,45 @@ 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) : ConcurrentLocalSocketListener( class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray, port: Int) : ConcurrentUdpSocketListener(
"LocalDnsThread", File(Core.deviceStorage.noBackupFilesDir, "local_dns_path")), CoroutineScope { "LocalDnsThread", port), CoroutineScope {
override fun acceptInternal(socket: LocalSocket) = error("big no no")
override fun accept(socket: LocalSocket) { override fun handle(channel: DatagramChannel, sender: SocketAddress, query: ByteBuffer) {
launch { launch {
socket.use { query.flip()
val input = DataInputStream(socket.inputStream) val data = ByteArray(query.remaining())
val query = try { query.get(data)
ByteArray(input.readUnsignedShort()).also { input.read(it) } try {
} catch (e: IOException) { // connection early close possibly due to resolving timeout resolver(data)
return@use Timber.d(e) } catch (e: Exception) {
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 {
resolver(query) val response = ByteBuffer.allocate(1024)
} catch (e: Exception) { response.clear()
when (e) { response.put(r)
is TimeoutCancellationException -> Timber.w("Resolving timed out") response.flip()
is CancellationException -> { } // ignore channel.send(response, sender)
is IOException -> Timber.d(e) } catch (e: IOException) {
else -> Timber.w(e) Timber.d(e.readableMessage)
}
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", "local_dns_path", "--local-dns-addr", "127.0.0.1:${DataStore.portLocalDns + 1}",
"--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(1024)
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() }
}
}
...@@ -16,7 +16,6 @@ ...@@ -16,7 +16,6 @@
<item>AES-128-GCM</item> <item>AES-128-GCM</item>
<item>AES-256-GCM</item> <item>AES-256-GCM</item>
<item>CHACHA20-IETF-POLY1305</item> <item>CHACHA20-IETF-POLY1305</item>
<item>XCHACHA20-IETF-POLY1305</item>
</string-array> </string-array>
<string-array name="enc_method_value" translatable="false"> <string-array name="enc_method_value" translatable="false">
...@@ -35,7 +34,6 @@ ...@@ -35,7 +34,6 @@
<item>aes-128-gcm</item> <item>aes-128-gcm</item>
<item>aes-256-gcm</item> <item>aes-256-gcm</item>
<item>chacha20-ietf-poly1305</item> <item>chacha20-ietf-poly1305</item>
<item>xchacha20-ietf-poly1305</item>
</string-array> </string-array>
<string-array name="bypass_private_route" translatable="false"> <string-array name="bypass_private_route" translatable="false">
......
Subproject commit 3a1054acc54cfc23a1cd232aa0a45b66af4bd612 Subproject commit e048bc2f343bbed36ed6dea0151d34ceecfce8ca
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