Skip to content

Commit

Permalink
Switch to local UDP DNS resolver (#2635)
Browse files Browse the repository at this point in the history
* Switch to the local UDP DNS resolver

* Update shadowsocks-rust

* Revert the rustup commands

* Fix #2642
  • Loading branch information
madeye authored Dec 31, 2020
1 parent f44de5b commit 700f4d2
Show file tree
Hide file tree
Showing 9 changed files with 150 additions and 50 deletions.
5 changes: 2 additions & 3 deletions .circleci/config.yml
Original file line number Diff line number Diff line change
Expand Up @@ -7,11 +7,10 @@ jobs:
environment:
GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy="in-process"
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
- 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

This comment has been minimized.

- restore_cache:
key: jars-{{ checksum "build.gradle.kts" }}
- run:
Expand Down
5 changes: 2 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -21,9 +21,8 @@ for Android TV ([beta](https://play.google.com/apps/testing/com.github.shadowsoc
* Rust with Android targets installed

```bash
$ rustup toolchain install nightly-2020-12-20
$ rustup override set nightly-2020-12-20
$ rustup target install armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
$ cd core/src/main/rust/shadowsocks-rust
$ rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
```

### BUILD
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -253,7 +253,7 @@ object BaseService {
File(Core.deviceStorage.noBackupFilesDir, "stat_udp"),
File(configRoot, CONFIG_FILE_UDP),
"-u", false)
data.localDns = LocalDnsWorker(this::rawResolver).apply { start() }
data.localDns = LocalDnsWorker(this::rawResolver, DataStore.portLocalDns + 1).apply { start() }
}

fun startRunner() {
Expand Down
74 changes: 35 additions & 39 deletions core/src/main/java/com/github/shadowsocks/bg/LocalDnsWorker.kt
Original file line number Diff line number Diff line change
@@ -1,8 +1,6 @@
package com.github.shadowsocks.bg

import android.net.LocalSocket
import com.github.shadowsocks.Core
import com.github.shadowsocks.net.ConcurrentLocalSocketListener
import com.github.shadowsocks.net.ConcurrentUdpSocketListener
import com.github.shadowsocks.net.DnsResolverCompat
import com.github.shadowsocks.utils.readableMessage
import kotlinx.coroutines.CancellationException
Expand All @@ -12,47 +10,45 @@ import kotlinx.coroutines.launch
import org.xbill.DNS.Message
import org.xbill.DNS.Rcode
import timber.log.Timber
import java.io.DataInputStream
import java.io.DataOutputStream
import java.io.File
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(
"LocalDnsThread", File(Core.deviceStorage.noBackupFilesDir, "local_dns_path")), CoroutineScope {
override fun acceptInternal(socket: LocalSocket) = error("big no no")
override fun accept(socket: LocalSocket) {
class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray, port: Int) : ConcurrentUdpSocketListener(
"LocalDnsThread", port), CoroutineScope {

override fun handle(channel: DatagramChannel, sender: SocketAddress, query: ByteBuffer) {
launch {
socket.use {
val input = DataInputStream(socket.inputStream)
val query = try {
ByteArray(input.readUnsignedShort()).also { input.read(it) }
} catch (e: IOException) { // connection early close possibly due to resolving timeout
return@use Timber.d(e)
query.flip()
val data = ByteArray(query.remaining())
query.get(data)
try {
resolver(data)
} 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 {
resolver(query)
} 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(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)
}
val response = ByteBuffer.allocate(1024)
response.clear()
response.put(r)
response.flip()
channel.send(response, sender)
} catch (e: IOException) {
Timber.d(e.readableMessage)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -91,7 +91,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
}.let { dns ->
cmd += arrayListOf(
"--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}")
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
/*******************************************************************************
* *
* 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() } }
}
}
72 changes: 72 additions & 0 deletions core/src/main/java/com/github/shadowsocks/net/UdpSocketListener.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,72 @@
/*******************************************************************************
* *
* 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() }
}
}
2 changes: 0 additions & 2 deletions core/src/main/res/values/arrays.xml
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,6 @@
<item>AES-128-GCM</item>
<item>AES-256-GCM</item>
<item>CHACHA20-IETF-POLY1305</item>
<item>XCHACHA20-IETF-POLY1305</item>
</string-array>

<string-array name="enc_method_value" translatable="false">
Expand All @@ -35,7 +34,6 @@
<item>aes-128-gcm</item>
<item>aes-256-gcm</item>
<item>chacha20-ietf-poly1305</item>
<item>xchacha20-ietf-poly1305</item>
</string-array>

<string-array name="bypass_private_route" translatable="false">
Expand Down
2 changes: 1 addition & 1 deletion core/src/main/rust/shadowsocks-rust
Submodule shadowsocks-rust updated 58 files
+3 −6 .github/workflows/build-and-test.yml
+3 −4 .github/workflows/build-release.yml
+1 −0 .gitignore
+348 −136 Cargo.lock
+3 −4 Cargo.toml
+1 −0 README.md
+3 −14 bin/common/monitor/windows.rs
+0 −8 bin/common/validator/mod.rs
+21 −9 bin/sslocal.rs
+18 −0 bin/ssmanager.rs
+18 −0 bin/ssserver.rs
+25 −0 configs/log4rs.yaml
+12 −15 crates/shadowsocks-service/Cargo.toml
+73 −39 crates/shadowsocks-service/src/config.rs
+12 −0 crates/shadowsocks-service/src/lib.rs
+13 −1 crates/shadowsocks-service/src/local/context.rs
+2 −1 crates/shadowsocks-service/src/local/dns/client_cache.rs
+0 −67 crates/shadowsocks-service/src/local/dns/config.rs
+0 −220 crates/shadowsocks-service/src/local/dns/dns_resolver.rs
+0 −2 crates/shadowsocks-service/src/local/dns/mod.rs
+41 −74 crates/shadowsocks-service/src/local/dns/server.rs
+2 −1 crates/shadowsocks-service/src/local/dns/upstream.rs
+12 −5 crates/shadowsocks-service/src/local/http/http_stream.rs
+53 −29 crates/shadowsocks-service/src/local/loadbalancing/ping_balancer.rs
+2 −2 crates/shadowsocks-service/src/local/loadbalancing/server_stat.rs
+60 −28 crates/shadowsocks-service/src/local/mod.rs
+3 −1 crates/shadowsocks-service/src/local/redir/tcprelay/mod.rs
+224 −117 crates/shadowsocks-service/src/local/redir/udprelay/mod.rs
+2 −1 crates/shadowsocks-service/src/local/socks/server/mod.rs
+223 −117 crates/shadowsocks-service/src/local/socks/server/socks5/udprelay.rs
+2 −1 crates/shadowsocks-service/src/local/tunnel/tcprelay.rs
+223 −66 crates/shadowsocks-service/src/local/tunnel/udprelay.rs
+17 −3 crates/shadowsocks-service/src/manager/mod.rs
+9 −5 crates/shadowsocks-service/src/manager/server.rs
+26 −7 crates/shadowsocks-service/src/server/mod.rs
+9 −9 crates/shadowsocks-service/src/server/server.rs
+16 −27 crates/shadowsocks-service/src/server/tcprelay.rs
+113 −87 crates/shadowsocks-service/src/server/udprelay.rs
+3 −3 crates/shadowsocks/Cargo.toml
+111 −46 crates/shadowsocks/src/dns_resolver/resolver.rs
+34 −38 crates/shadowsocks/src/dns_resolver/trust_dns_resolver.rs
+8 −9 crates/shadowsocks/src/manager/datagram.rs
+35 −2 crates/shadowsocks/src/net/mod.rs
+42 −0 crates/shadowsocks/src/net/option.rs
+106 −8 crates/shadowsocks/src/net/tcp.rs
+8 −8 crates/shadowsocks/src/net/udp.rs
+26 −10 crates/shadowsocks/src/relay/sys/unix/mod.rs
+38 −32 crates/shadowsocks/src/relay/sys/unix/uds.rs
+16 −10 crates/shadowsocks/src/relay/sys/windows/mod.rs
+3 −3 crates/shadowsocks/src/relay/tcprelay/aead.rs
+20 −3 crates/shadowsocks/src/relay/tcprelay/proxy_listener.rs
+1 −1 crates/shadowsocks/src/relay/tcprelay/stream.rs
+1 −0 rust-toolchain
+1 −1 tests/dns.rs
+5 −1 tests/http.rs
+1 −1 tests/socks4.rs
+1 −1 tests/socks5.rs
+1 −1 tests/tunnel.rs

2 comments on commit 700f4d2

@Mygod
Copy link
Contributor

@Mygod Mygod commented on 700f4d2 Jan 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This commits introduces a java.nio.BufferOverflowException at LocalDnsWorker.kt:47.

@madeye
Copy link
Contributor Author

@madeye madeye commented on 700f4d2 Jan 4, 2021

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@Mygod Yes, I should allocate r.size bytes here.

Please sign in to comment.