Skip to content

Commit 700f4d2

Browse files
authored
Switch to local UDP DNS resolver (#2635)
* Switch to the local UDP DNS resolver * Update shadowsocks-rust * Revert the rustup commands * Fix #2642
1 parent f44de5b commit 700f4d2

File tree

9 files changed

+150
-50
lines changed

9 files changed

+150
-50
lines changed

.circleci/config.yml

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -7,11 +7,10 @@ jobs:
77
environment:
88
GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy="in-process"
99
steps:
10-
- run: rustup toolchain install nightly-2020-12-20
11-
- run: rustup override set nightly-2020-12-20
12-
- run: rustup target install armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
1310
- checkout
1411
- run: git submodule update --init --recursive
12+
- run: rustup update
13+
- run: cd core/src/main/rust/shadowsocks-rust && rustup target add armv7-linux-androideabi aarch64-linux-android i686-linux-android x86_64-linux-android
1514
- restore_cache:
1615
key: jars-{{ checksum "build.gradle.kts" }}
1716
- run:

README.md

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -21,9 +21,8 @@ for Android TV ([beta](https://play.google.com/apps/testing/com.github.shadowsoc
2121
* Rust with Android targets installed
2222

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

2928
### BUILD

core/src/main/java/com/github/shadowsocks/bg/BaseService.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -253,7 +253,7 @@ object BaseService {
253253
File(Core.deviceStorage.noBackupFilesDir, "stat_udp"),
254254
File(configRoot, CONFIG_FILE_UDP),
255255
"-u", false)
256-
data.localDns = LocalDnsWorker(this::rawResolver).apply { start() }
256+
data.localDns = LocalDnsWorker(this::rawResolver, DataStore.portLocalDns + 1).apply { start() }
257257
}
258258

259259
fun startRunner() {

core/src/main/java/com/github/shadowsocks/bg/LocalDnsWorker.kt

Lines changed: 35 additions & 39 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
package com.github.shadowsocks.bg
22

3-
import android.net.LocalSocket
4-
import com.github.shadowsocks.Core
5-
import com.github.shadowsocks.net.ConcurrentLocalSocketListener
3+
import com.github.shadowsocks.net.ConcurrentUdpSocketListener
64
import com.github.shadowsocks.net.DnsResolverCompat
75
import com.github.shadowsocks.utils.readableMessage
86
import kotlinx.coroutines.CancellationException
@@ -12,47 +10,45 @@ import kotlinx.coroutines.launch
1210
import org.xbill.DNS.Message
1311
import org.xbill.DNS.Rcode
1412
import timber.log.Timber
15-
import java.io.DataInputStream
16-
import java.io.DataOutputStream
17-
import java.io.File
1813
import java.io.IOException
14+
import java.net.SocketAddress
15+
import java.nio.ByteBuffer
16+
import java.nio.channels.DatagramChannel
1917

20-
class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray) : ConcurrentLocalSocketListener(
21-
"LocalDnsThread", File(Core.deviceStorage.noBackupFilesDir, "local_dns_path")), CoroutineScope {
22-
override fun acceptInternal(socket: LocalSocket) = error("big no no")
23-
override fun accept(socket: LocalSocket) {
18+
class LocalDnsWorker(private val resolver: suspend (ByteArray) -> ByteArray, port: Int) : ConcurrentUdpSocketListener(
19+
"LocalDnsThread", port), CoroutineScope {
20+
21+
override fun handle(channel: DatagramChannel, sender: SocketAddress, query: ByteBuffer) {
2422
launch {
25-
socket.use {
26-
val input = DataInputStream(socket.inputStream)
27-
val query = try {
28-
ByteArray(input.readUnsignedShort()).also { input.read(it) }
29-
} catch (e: IOException) { // connection early close possibly due to resolving timeout
30-
return@use Timber.d(e)
23+
query.flip()
24+
val data = ByteArray(query.remaining())
25+
query.get(data)
26+
try {
27+
resolver(data)
28+
} catch (e: Exception) {
29+
when (e) {
30+
is TimeoutCancellationException -> Timber.w("Resolving timed out")
31+
is CancellationException -> {
32+
} // ignore
33+
is IOException -> Timber.d(e)
34+
else -> Timber.w(e)
35+
}
36+
try {
37+
DnsResolverCompat.prepareDnsResponse(Message(data)).apply {
38+
header.rcode = Rcode.SERVFAIL
39+
}.toWire()
40+
} catch (_: IOException) {
41+
byteArrayOf() // return empty if cannot parse packet
3142
}
43+
}?.let { r ->
3244
try {
33-
resolver(query)
34-
} catch (e: Exception) {
35-
when (e) {
36-
is TimeoutCancellationException -> Timber.w("Resolving timed out")
37-
is CancellationException -> { } // ignore
38-
is IOException -> Timber.d(e)
39-
else -> Timber.w(e)
40-
}
41-
try {
42-
DnsResolverCompat.prepareDnsResponse(Message(query)).apply {
43-
header.rcode = Rcode.SERVFAIL
44-
}.toWire()
45-
} catch (_: IOException) {
46-
byteArrayOf() // return empty if cannot parse packet
47-
}
48-
}?.let { response ->
49-
try {
50-
val output = DataOutputStream(socket.outputStream)
51-
output.writeShort(response.size)
52-
output.write(response)
53-
} catch (e: IOException) {
54-
Timber.d(e.readableMessage)
55-
}
45+
val response = ByteBuffer.allocate(1024)
46+
response.clear()
47+
response.put(r)
48+
response.flip()
49+
channel.send(response, sender)
50+
} catch (e: IOException) {
51+
Timber.d(e.readableMessage)
5652
}
5753
}
5854
}

core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -91,7 +91,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro
9191
}.let { dns ->
9292
cmd += arrayListOf(
9393
"--dns-addr", "${DataStore.listenAddress}:${DataStore.portLocalDns}",
94-
"--local-dns-addr", "local_dns_path",
94+
"--local-dns-addr", "127.0.0.1:${DataStore.portLocalDns + 1}",
9595
"--remote-dns-addr", "${dns.host ?: "0.0.0.0"}:${if (dns.port < 0) 53 else dns.port}")
9696
}
9797

Lines changed: 36 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,36 @@
1+
/*******************************************************************************
2+
* *
3+
* Copyright (C) 2019 by Max Lv <[email protected]> *
4+
* Copyright (C) 2019 by Mygod Studio <[email protected]> *
5+
* *
6+
* This program is free software: you can redistribute it and/or modify *
7+
* it under the terms of the GNU General Public License as published by *
8+
* the Free Software Foundation, either version 3 of the License, or *
9+
* (at your option) any later version. *
10+
* *
11+
* This program is distributed in the hope that it will be useful, *
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14+
* GNU General Public License for more details. *
15+
* *
16+
* You should have received a copy of the GNU General Public License *
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
18+
* *
19+
*******************************************************************************/
20+
21+
package com.github.shadowsocks.net
22+
23+
import kotlinx.coroutines.*
24+
import timber.log.Timber
25+
26+
abstract class ConcurrentUdpSocketListener(name: String, port: Int) : UdpSocketListener(name, port),
27+
CoroutineScope {
28+
override val coroutineContext = Dispatchers.IO + SupervisorJob() + CoroutineExceptionHandler { _, t -> Timber.w(t) }
29+
30+
override fun shutdown(scope: CoroutineScope) {
31+
running = false
32+
cancel()
33+
super.shutdown(scope)
34+
coroutineContext[Job]!!.also { job -> scope.launch { job.join() } }
35+
}
36+
}
Lines changed: 72 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,72 @@
1+
/*******************************************************************************
2+
* *
3+
* Copyright (C) 2017 by Max Lv <[email protected]> *
4+
* Copyright (C) 2017 by Mygod Studio <[email protected]> *
5+
* *
6+
* This program is free software: you can redistribute it and/or modify *
7+
* it under the terms of the GNU General Public License as published by *
8+
* the Free Software Foundation, either version 3 of the License, or *
9+
* (at your option) any later version. *
10+
* *
11+
* This program is distributed in the hope that it will be useful, *
12+
* but WITHOUT ANY WARRANTY; without even the implied warranty of *
13+
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the *
14+
* GNU General Public License for more details. *
15+
* *
16+
* You should have received a copy of the GNU General Public License *
17+
* along with this program. If not, see <http://www.gnu.org/licenses/>. *
18+
* *
19+
*******************************************************************************/
20+
21+
package com.github.shadowsocks.net
22+
23+
import android.annotation.SuppressLint
24+
import kotlinx.coroutines.CoroutineScope
25+
import kotlinx.coroutines.channels.Channel
26+
import kotlinx.coroutines.channels.sendBlocking
27+
import kotlinx.coroutines.launch
28+
import timber.log.Timber
29+
import java.io.IOException
30+
import java.net.InetSocketAddress
31+
import java.net.SocketAddress
32+
import java.nio.ByteBuffer
33+
import java.nio.channels.DatagramChannel
34+
35+
abstract class UdpSocketListener(name: String, val port: Int) : Thread(name) {
36+
37+
private val udpChannel = DatagramChannel.open()
38+
private val closeChannel = Channel<Unit>(1)
39+
40+
@Volatile
41+
protected var running = true
42+
43+
/**
44+
* Inherited class do not need to close input/output streams as they will be closed automatically.
45+
*/
46+
protected abstract fun handle(channel: DatagramChannel, sender: SocketAddress, query: ByteBuffer)
47+
48+
final override fun run() {
49+
udpChannel.socket().bind(InetSocketAddress(port))
50+
udpChannel.configureBlocking(true)
51+
udpChannel.use {
52+
while (running) {
53+
try {
54+
val query = ByteBuffer.allocate(1024)
55+
query.clear()
56+
udpChannel.receive(query)?.let { handle(udpChannel, it, query) }
57+
} catch (e: IOException) {
58+
if (running) Timber.w(e)
59+
continue
60+
}
61+
}
62+
}
63+
closeChannel.sendBlocking(Unit)
64+
}
65+
66+
@SuppressLint("NewApi")
67+
open fun shutdown(scope: CoroutineScope) {
68+
running = false
69+
udpChannel.close()
70+
scope.launch { closeChannel.receive() }
71+
}
72+
}

core/src/main/res/values/arrays.xml

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,6 @@
1616
<item>AES-128-GCM</item>
1717
<item>AES-256-GCM</item>
1818
<item>CHACHA20-IETF-POLY1305</item>
19-
<item>XCHACHA20-IETF-POLY1305</item>
2019
</string-array>
2120

2221
<string-array name="enc_method_value" translatable="false">
@@ -35,7 +34,6 @@
3534
<item>aes-128-gcm</item>
3635
<item>aes-256-gcm</item>
3736
<item>chacha20-ietf-poly1305</item>
38-
<item>xchacha20-ietf-poly1305</item>
3937
</string-array>
4038

4139
<string-array name="bypass_private_route" translatable="false">
Submodule shadowsocks-rust updated 58 files

0 commit comments

Comments
 (0)