From 8821c9d6be3730b97fec1e4af6084a5fd57eb4d5 Mon Sep 17 00:00:00 2001 From: dev4u Date: Sun, 18 Sep 2022 14:41:13 +0800 Subject: [PATCH 1/6] Update dependencies --- buildSrc/src/main/kotlin/Helpers.kt | 2 +- core/build.gradle.kts | 10 +++++----- plugin/build.gradle.kts | 6 +++--- 3 files changed, 9 insertions(+), 9 deletions(-) diff --git a/buildSrc/src/main/kotlin/Helpers.kt b/buildSrc/src/main/kotlin/Helpers.kt index 00b5f53dd7..ef75e355b5 100644 --- a/buildSrc/src/main/kotlin/Helpers.kt +++ b/buildSrc/src/main/kotlin/Helpers.kt @@ -12,7 +12,7 @@ import org.gradle.kotlin.dsl.getByName import org.jetbrains.kotlin.gradle.dsl.KotlinJvmOptions import java.util.* -const val lifecycleVersion = "2.5.0" +const val lifecycleVersion = "2.5.1" private val Project.android get() = extensions.getByName("android") private val BaseExtension.lint get() = (this as CommonExtension<*, *, *, *>).lint diff --git a/core/build.gradle.kts b/core/build.gradle.kts index af9ab5248b..737b1ab34d 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -37,7 +37,7 @@ android { cargo { module = "src/main/rust/shadowsocks-rust" libname = "sslocal" - targets = listOf("arm", "arm64", "x86", "x86_64") + targets = listOf("arm", "arm64") profile = findProperty("CARGO_PROFILE")?.toString() ?: currentFlavor extraCargoBuildArguments = listOf("--bin", libname!!) featureSpec.noDefaultBut(arrayOf( @@ -76,8 +76,8 @@ dependencies { val workVersion = "2.7.1" api(project(":plugin")) - api("androidx.core:core-ktx:1.8.0") - api("androidx.fragment:fragment-ktx:1.5.0") + api("androidx.core:core-ktx:1.9.0") + api("androidx.fragment:fragment-ktx:1.5.2") api("com.google.android.material:material:1.6.1") api("androidx.lifecycle:lifecycle-livedata-core-ktx:$lifecycleVersion") @@ -87,8 +87,8 @@ dependencies { api("androidx.work:work-runtime-ktx:$workVersion") api("com.google.android.gms:play-services-oss-licenses:17.0.0") api("com.google.code.gson:gson:2.9.0") - api("com.google.firebase:firebase-analytics-ktx:21.1.0") - api("com.google.firebase:firebase-crashlytics:18.2.11") + api("com.google.firebase:firebase-analytics-ktx:21.1.1") + api("com.google.firebase:firebase-crashlytics:18.2.13") api("com.jakewharton.timber:timber:5.0.1") api("dnsjava:dnsjava:3.5.1") api("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion") diff --git a/plugin/build.gradle.kts b/plugin/build.gradle.kts index a2559e0ff5..dc0946a694 100644 --- a/plugin/build.gradle.kts +++ b/plugin/build.gradle.kts @@ -14,7 +14,7 @@ android { dependencies { api(kotlin("stdlib-jdk8")) - api("androidx.core:core-ktx:1.7.0") - api("androidx.fragment:fragment-ktx:1.4.1") - api("com.google.android.material:material:1.6.0") + api("androidx.core:core-ktx:1.9.0") + api("androidx.fragment:fragment-ktx:1.5.2") + api("com.google.android.material:material:1.6.1") } From 0f7436d85b6fcd2ee14cb375b77c5f72911972a8 Mon Sep 17 00:00:00 2001 From: dev4u Date: Sun, 18 Sep 2022 14:44:27 +0800 Subject: [PATCH 2/6] Add okhttp lib dependencies --- core/build.gradle.kts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 737b1ab34d..cab52c13d4 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -94,6 +94,8 @@ dependencies { api("org.jetbrains.kotlinx:kotlinx-coroutines-android:$coroutinesVersion") api("org.jetbrains.kotlinx:kotlinx-coroutines-play-services:$coroutinesVersion") kapt("androidx.room:room-compiler:$roomVersion") + api("com.squareup.okhttp3:okhttp:5.0.0-alpha.10") + api("com.squareup.okhttp3:okhttp-brotli:5.0.0-alpha.10") androidTestImplementation("androidx.room:room-testing:$roomVersion") androidTestImplementation("androidx.test.ext:junit-ktx:1.1.3") } From 4d120390a0c3aa24e9203a6584013f9481687490 Mon Sep 17 00:00:00 2001 From: dev4u Date: Wed, 21 Sep 2022 01:45:22 +0800 Subject: [PATCH 3/6] Query local dns over DoH. --- .../30.json | 186 ++++++++++++++++++ .../shadowsocks/database/PrivateDatabase.kt | 9 +- .../github/shadowsocks/database/Profile.kt | 8 +- .../shadowsocks/net/DnsResolverCompat.kt | 65 ++++-- .../com/github/shadowsocks/net/DohResolver.kt | 96 +++++++++ .../com/github/shadowsocks/utils/Constants.kt | 1 + core/src/main/res/values/strings.xml | 1 + .../shadowsocks/ProfileConfigFragment.kt | 2 + .../src/main/res/drawable/ic_action_doh.xml | 28 +++ mobile/src/main/res/xml/pref_profile.xml | 5 + 10 files changed, 386 insertions(+), 15 deletions(-) create mode 100644 core/schemas/com.github.shadowsocks.database.PrivateDatabase/30.json create mode 100644 core/src/main/java/com/github/shadowsocks/net/DohResolver.kt create mode 100644 mobile/src/main/res/drawable/ic_action_doh.xml diff --git a/core/schemas/com.github.shadowsocks.database.PrivateDatabase/30.json b/core/schemas/com.github.shadowsocks.database.PrivateDatabase/30.json new file mode 100644 index 0000000000..45058507a1 --- /dev/null +++ b/core/schemas/com.github.shadowsocks.database.PrivateDatabase/30.json @@ -0,0 +1,186 @@ +{ + "formatVersion": 1, + "database": { + "version": 30, + "identityHash": "e1c2ad386fb06fcb7c9090f43b712f1d", + "entities": [ + { + "tableName": "Profile", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `name` TEXT, `host` TEXT NOT NULL, `remotePort` INTEGER NOT NULL, `password` TEXT NOT NULL, `method` TEXT NOT NULL, `route` TEXT NOT NULL, `remoteDns` TEXT NOT NULL, `localDohHostAddr` TEXT NOT NULL, `proxyApps` INTEGER NOT NULL, `bypass` INTEGER NOT NULL, `udpdns` INTEGER NOT NULL, `ipv6` INTEGER NOT NULL, `metered` INTEGER NOT NULL, `individual` TEXT NOT NULL, `plugin` TEXT, `udpFallback` INTEGER, `subscription` INTEGER NOT NULL, `tx` INTEGER NOT NULL, `rx` INTEGER NOT NULL, `userOrder` INTEGER NOT NULL)", + "fields": [ + { + "fieldPath": "id", + "columnName": "id", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "name", + "columnName": "name", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "host", + "columnName": "host", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "remotePort", + "columnName": "remotePort", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "password", + "columnName": "password", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "method", + "columnName": "method", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "route", + "columnName": "route", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "remoteDns", + "columnName": "remoteDns", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "localDohHostAddr", + "columnName": "localDohHostAddr", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "proxyApps", + "columnName": "proxyApps", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "bypass", + "columnName": "bypass", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "udpdns", + "columnName": "udpdns", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "ipv6", + "columnName": "ipv6", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "metered", + "columnName": "metered", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "individual", + "columnName": "individual", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "plugin", + "columnName": "plugin", + "affinity": "TEXT", + "notNull": false + }, + { + "fieldPath": "udpFallback", + "columnName": "udpFallback", + "affinity": "INTEGER", + "notNull": false + }, + { + "fieldPath": "subscription", + "columnName": "subscription", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "tx", + "columnName": "tx", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "rx", + "columnName": "rx", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "userOrder", + "columnName": "userOrder", + "affinity": "INTEGER", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "id" + ], + "autoGenerate": true + }, + "indices": [], + "foreignKeys": [] + }, + { + "tableName": "KeyValuePair", + "createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`key` TEXT NOT NULL, `valueType` INTEGER NOT NULL, `value` BLOB NOT NULL, PRIMARY KEY(`key`))", + "fields": [ + { + "fieldPath": "key", + "columnName": "key", + "affinity": "TEXT", + "notNull": true + }, + { + "fieldPath": "valueType", + "columnName": "valueType", + "affinity": "INTEGER", + "notNull": true + }, + { + "fieldPath": "value", + "columnName": "value", + "affinity": "BLOB", + "notNull": true + } + ], + "primaryKey": { + "columnNames": [ + "key" + ], + "autoGenerate": false + }, + "indices": [], + "foreignKeys": [] + } + ], + "views": [], + "setupQueries": [ + "CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)", + "INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e1c2ad386fb06fcb7c9090f43b712f1d')" + ] + } +} \ No newline at end of file diff --git a/core/src/main/java/com/github/shadowsocks/database/PrivateDatabase.kt b/core/src/main/java/com/github/shadowsocks/database/PrivateDatabase.kt index 2a0bcd7950..603a3fcd1e 100644 --- a/core/src/main/java/com/github/shadowsocks/database/PrivateDatabase.kt +++ b/core/src/main/java/com/github/shadowsocks/database/PrivateDatabase.kt @@ -32,7 +32,7 @@ import com.github.shadowsocks.utils.Key import kotlinx.coroutines.GlobalScope import kotlinx.coroutines.launch -@Database(entities = [Profile::class, KeyValuePair::class], version = 29) +@Database(entities = [Profile::class, KeyValuePair::class], version = 30) @TypeConverters(Profile.SubscriptionStatus::class) abstract class PrivateDatabase : RoomDatabase() { companion object { @@ -42,7 +42,8 @@ abstract class PrivateDatabase : RoomDatabase() { Migration26, Migration27, Migration28, - Migration29 + Migration29, + Migration30, ) allowMainThreadQueries() enableMultiInstanceInvalidation() @@ -78,4 +79,8 @@ abstract class PrivateDatabase : RoomDatabase() { database.execSQL("ALTER TABLE `Profile` ADD COLUMN `subscription` INTEGER NOT NULL DEFAULT " + Profile.SubscriptionStatus.UserConfigured.persistedValue) } + object Migration30 : Migration(29, 30) { + override fun migrate(database: SupportSQLiteDatabase) = + database.execSQL("ALTER TABLE `Profile` ADD COLUMN `localDohHostAddr` TEXT NOT NULL DEFAULT ''") + } } diff --git a/core/src/main/java/com/github/shadowsocks/database/Profile.kt b/core/src/main/java/com/github/shadowsocks/database/Profile.kt index bae60e1066..17de95f0c3 100644 --- a/core/src/main/java/com/github/shadowsocks/database/Profile.kt +++ b/core/src/main/java/com/github/shadowsocks/database/Profile.kt @@ -27,6 +27,7 @@ import android.util.Base64 import android.util.LongSparseArray import androidx.core.net.toUri import androidx.room.* +import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.plugin.PluginConfiguration import com.github.shadowsocks.plugin.PluginOptions import com.github.shadowsocks.preference.DataStore @@ -59,8 +60,9 @@ data class Profile( var password: String = "u1rRWTssNv0p", var method: String = "aes-256-cfb", - var route: String = "all", + var route: String = Acl.ALL, var remoteDns: String = "dns.google", + var localDohHostAddr: String = "223.5.5.5,120.53.53.53,223.6.6.6,1.12.12.12", var proxyApps: Boolean = false, var bypass: Boolean = false, var udpdns: Boolean = false, @@ -197,6 +199,7 @@ data class Profile( route = json["route"].optString ?: route if (fallback) return@apply remoteDns = json["remote_dns"].optString ?: remoteDns + localDohHostAddr = json["localDohHostAddr"].optString ?: localDohHostAddr ipv6 = json["ipv6"].optBoolean ?: ipv6 metered = json["metered"].optBoolean ?: metered (json["proxy_apps"] as? JsonObject)?.also { @@ -323,6 +326,7 @@ data class Profile( put("remarks", name) put("route", route) put("remote_dns", remoteDns) + put("localDohHostAddr", localDohHostAddr) put("ipv6", ipv6) put("metered", metered) put("proxy_apps", JSONObject().apply { @@ -346,6 +350,7 @@ data class Profile( DataStore.privateStore.putString(Key.password, password) DataStore.privateStore.putString(Key.route, route) DataStore.privateStore.putString(Key.remoteDns, remoteDns) + DataStore.privateStore.putString(Key.localDohHostAddr, localDohHostAddr) DataStore.privateStore.putString(Key.method, method) DataStore.proxyApps = proxyApps DataStore.bypass = bypass @@ -370,6 +375,7 @@ data class Profile( method = DataStore.privateStore.getString(Key.method) ?: "" route = DataStore.privateStore.getString(Key.route) ?: "" remoteDns = DataStore.privateStore.getString(Key.remoteDns) ?: "" + localDohHostAddr = DataStore.privateStore.getString(Key.localDohHostAddr) ?: "" proxyApps = DataStore.proxyApps bypass = DataStore.bypass udpdns = DataStore.privateStore.getBoolean(Key.udpdns, false) diff --git a/core/src/main/java/com/github/shadowsocks/net/DnsResolverCompat.kt b/core/src/main/java/com/github/shadowsocks/net/DnsResolverCompat.kt index c65491c067..7edfb98812 100644 --- a/core/src/main/java/com/github/shadowsocks/net/DnsResolverCompat.kt +++ b/core/src/main/java/com/github/shadowsocks/net/DnsResolverCompat.kt @@ -20,6 +20,7 @@ package com.github.shadowsocks.net +import android.annotation.SuppressLint import android.annotation.TargetApi import android.net.DnsResolver import android.net.Network @@ -32,6 +33,7 @@ import java.io.IOException import java.net.Inet4Address import java.net.Inet6Address import java.net.InetAddress +import java.time.Duration import java.util.concurrent.Executor import java.util.concurrent.Executors import kotlin.coroutines.resume @@ -46,6 +48,32 @@ sealed class DnsResolverCompat { else -> error("Unsupported API level") } } + private val isDohResolve get() = Core.currentProfile!!.main.localDohHostAddr.isEmpty().not() + private val dohResolver by lazy { + val fakeReslver = SimpleResolver() + val expanded = Core.currentProfile ?: return@lazy fakeReslver + val (profile, _) = expanded + val doh = profile.localDohHostAddr + if (doh.isEmpty()) { + return@lazy fakeReslver + } + val addrList = doh.split(",") + if (addrList.isEmpty()) { + return@lazy fakeReslver + } + val dohResolvers = arrayListOf() + val exec = Executors.newFixedThreadPool(5) + val activeNetwork = Core.connectivity.activeNetwork ?: throw IOException("no network") + addrList.forEach { + dohResolvers.add(DohResolver(activeNetwork , it, 5, Duration.ofSeconds(5), exec)) + } + ExtendedResolver(dohResolvers).apply { + Lookup.setDefaultResolver(this) +// timeout = Duration.ofSeconds(3) +// retries = 2 + loadBalance = true + } + } override suspend fun resolve(network: Network, host: String) = instance.resolve(network, host) override suspend fun resolveOnActiveNetwork(host: String) = instance.resolveOnActiveNetwork(host) @@ -85,9 +113,9 @@ sealed class DnsResolverCompat { } override suspend fun resolve(network: Network, host: String) = - withContext(unboundedIO) { network.getAllByName(host) } + withContext(unboundedIO) { network.getAllByName(host) } override suspend fun resolveOnActiveNetwork(host: String) = - withContext(unboundedIO) { InetAddress.getAllByName(host) } + withContext(unboundedIO) { InetAddress.getAllByName(host) } private suspend fun resolveRaw(query: ByteArray, networkSpecified: Boolean = true, hostResolver: suspend (String) -> Array): ByteArray { @@ -133,9 +161,9 @@ sealed class DnsResolverCompat { }.toWire() } override suspend fun resolveRaw(network: Network, query: ByteArray) = - resolveRaw(query) { resolve(network, it) } + resolveRaw(query) { resolve(network, it) } override suspend fun resolveRawOnActiveNetwork(query: ByteArray) = - resolveRaw(query, false, this::resolveOnActiveNetwork) + resolveRaw(query, false, this::resolveOnActiveNetwork) } @TargetApi(29) @@ -153,24 +181,37 @@ sealed class DnsResolverCompat { cont.invokeOnCancellation { signal.cancel() } // retry should be handled by client instead DnsResolver.getInstance().query(network, host, DnsResolver.FLAG_NO_RETRY, this, - signal, object : DnsResolver.Callback> { - override fun onAnswer(answer: Collection, rcode: Int) = + signal, object : DnsResolver.Callback> { + override fun onAnswer(answer: Collection, rcode: Int) = cont.resume(answer.toTypedArray()) - override fun onError(error: DnsResolver.DnsException) = cont.resumeWithException(IOException(error)) - }) + override fun onError(error: DnsResolver.DnsException) = cont.resumeWithException(IOException(error)) + }) } } override suspend fun resolveOnActiveNetwork(host: String) = resolve(activeNetwork, host) override suspend fun resolveRaw(network: Network, query: ByteArray): ByteArray { + val msg = Message(query) return suspendCancellableCoroutine { cont -> val signal = CancellationSignal() cont.invokeOnCancellation { signal.cancel() } - DnsResolver.getInstance().rawQuery(network, query, DnsResolver.FLAG_NO_RETRY, this, + if (isDohResolve) { + dohResolver.sendAsync(msg, this).whenCompleteAsync ({ answer, exception -> + if (null == exception) { + cont.resume(answer.toWire()) +// println("dns query response: $answer") + } else { + cont.resumeWithException(IOException(exception)) + } + }, this) + } else { + DnsResolver.getInstance().rawQuery(network, query, DnsResolver.FLAG_NO_RETRY, this, signal, object : DnsResolver.Callback { - override fun onAnswer(answer: ByteArray, rcode: Int) = cont.resume(answer) - override fun onError(error: DnsResolver.DnsException) = cont.resumeWithException(IOException(error)) - }) + override fun onAnswer(answer: ByteArray, rcode: Int) = cont.resume(answer) + override fun onError(error: DnsResolver.DnsException) = + cont.resumeWithException(IOException(error)) + }) + } } } override suspend fun resolveRawOnActiveNetwork(query: ByteArray) = resolveRaw(activeNetwork, query) diff --git a/core/src/main/java/com/github/shadowsocks/net/DohResolver.kt b/core/src/main/java/com/github/shadowsocks/net/DohResolver.kt new file mode 100644 index 0000000000..534e6bf456 --- /dev/null +++ b/core/src/main/java/com/github/shadowsocks/net/DohResolver.kt @@ -0,0 +1,96 @@ +package com.github.shadowsocks.net + +import android.net.Network +import android.os.Build +import androidx.annotation.RequiresApi +import okhttp3.* +import okhttp3.CacheControl.Companion.FORCE_NETWORK +import okhttp3.MediaType.Companion.toMediaType +import okhttp3.RequestBody.Companion.toRequestBody +import org.xbill.DNS.EDNSOption +import org.xbill.DNS.Message +import org.xbill.DNS.Resolver +import org.xbill.DNS.TSIG +import java.io.IOException +import java.time.Duration +import java.util.* +import java.util.concurrent.CompletableFuture +import java.util.concurrent.CompletionStage +import java.util.concurrent.Executor +import java.util.concurrent.ExecutorService + +class DohResolver(network: Network, host: String, maxConn: Int, private var timeout: Duration?, executor: ExecutorService) : Resolver { + + private val client: OkHttpClient + private val uriTemplate: String + override fun getTimeout(): Duration? { + return this.timeout + } + + init { + uriTemplate = "https://$host/dns-query" + val spec: ConnectionSpec = ConnectionSpec.MODERN_TLS + val dispatcher = Dispatcher(executor) + dispatcher.maxRequestsPerHost = maxConn + this.client = OkHttpClient.Builder() + .socketFactory(network.socketFactory) + .connectionSpecs(listOf(spec)) + .dispatcher(dispatcher) + .callTimeout(timeout!!) + .build() + } + + @RequiresApi(api = Build.VERSION_CODES.S) + override fun sendAsync(query: Message, executor: Executor): CompletionStage { + return this.sendAsync(query) + } + + @RequiresApi(api = Build.VERSION_CODES.N) + override fun sendAsync(query: Message): CompletionStage { +// println("OKHttp...$uriTemplate") + val bytes = query.toWire() + val body: RequestBody = bytes.toRequestBody(mediaType, 0, bytes.size) + val request: Request = Request.Builder() + .url(uriTemplate) + .cacheControl(FORCE_NETWORK) + .post(body) + .build() + val future = CompletableFuture() + val call = client.newCall(request) + call.enqueue(object : Callback { + override fun onFailure(call: Call, e: IOException) { + future.completeExceptionally(e) + } + + @Throws(IOException::class) + override fun onResponse(call: Call, response: Response) { + response.use { + if (!it.isSuccessful) { + future.cancel(true) + return@use + } + it.body.use { responseBody -> + val msg = Message(responseBody.bytes()) + future.complete(msg) + } + } + } + }) + return future + } + + override fun setPort(port: Int) {} + override fun setTCP(flag: Boolean) {} + override fun setIgnoreTruncation(flag: Boolean) {} + override fun setEDNS(version: Int, payloadSize: Int, flags: Int, options: List) {} + override fun setTSIGKey(key: TSIG) {} + override fun setTimeout(timeout: Duration?) { + this.timeout = timeout; + } +// override fun setTimeout(timeout: Duration?) { +// } + + companion object { + private val mediaType: MediaType = "application/dns-message".toMediaType() + } +} \ No newline at end of file diff --git a/core/src/main/java/com/github/shadowsocks/utils/Constants.kt b/core/src/main/java/com/github/shadowsocks/utils/Constants.kt index 52b6de20ec..d188df1c21 100644 --- a/core/src/main/java/com/github/shadowsocks/utils/Constants.kt +++ b/core/src/main/java/com/github/shadowsocks/utils/Constants.kt @@ -57,6 +57,7 @@ object Key { const val method = "encMethod" const val remotePort = "remotePortNum" const val remoteDns = "remoteDns" + const val localDohHostAddr = "localDohHostAddr" const val plugin = "plugin" const val pluginConfigure = "plugin.configure" diff --git a/core/src/main/res/values/strings.xml b/core/src/main/res/values/strings.xml index 04ed7d9d67..13c2aa2737 100644 --- a/core/src/main/res/values/strings.xml +++ b/core/src/main/res/values/strings.xml @@ -16,6 +16,7 @@ Transproxy port Remote DNS + Query Local DNS over DOH %1$s↑\t%2$s↓ Sent: \t\t\t\t\t%3$s\t↑\t%1$s\nReceived: \t%4$s\t↓\t%2$s %s/s diff --git a/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt b/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt index dc5c490ae0..326682a564 100644 --- a/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt +++ b/mobile/src/main/java/com/github/shadowsocks/ProfileConfigFragment.kt @@ -91,6 +91,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), addPreferencesFromResource(R.xml.pref_profile) findPreference(Key.remotePort)!!.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) findPreference(Key.password)!!.summaryProvider = PasswordSummaryProvider + findPreference(Key.localDohHostAddr)!!.setOnBindEditTextListener(EditTextPreferenceModifiers.Monospace) val serviceMode = DataStore.serviceMode findPreference(Key.ipv6)!!.isEnabled = serviceMode == Key.modeVpn isProxyApps = findPreference(Key.proxyApps)!! @@ -119,6 +120,7 @@ class ProfileConfigFragment : PreferenceFragmentCompat(), findPreference(Key.password)!!.isEnabled = false findPreference(Key.method)!!.isEnabled = false findPreference(Key.remotePort)!!.isEnabled = false + findPreference(Key.localDohHostAddr)!!.isEnabled = false plugin.isEnabled = false pluginConfigure.isEnabled = false udpFallback.isEnabled = false diff --git a/mobile/src/main/res/drawable/ic_action_doh.xml b/mobile/src/main/res/drawable/ic_action_doh.xml new file mode 100644 index 0000000000..e4b1b65f0e --- /dev/null +++ b/mobile/src/main/res/drawable/ic_action_doh.xml @@ -0,0 +1,28 @@ + + + + + + + + + diff --git a/mobile/src/main/res/xml/pref_profile.xml b/mobile/src/main/res/xml/pref_profile.xml index 5db0dd19a3..179a45bb35 100644 --- a/mobile/src/main/res/xml/pref_profile.xml +++ b/mobile/src/main/res/xml/pref_profile.xml @@ -66,6 +66,11 @@ app:icon="@drawable/ic_action_dns" app:title="@string/remote_dns" app:useSimpleSummaryProvider="true"/> + From f430c426c983e3d073c02c4bfec2c754daaef870 Mon Sep 17 00:00:00 2001 From: dev4u Date: Sat, 21 Jan 2023 02:24:42 +0800 Subject: [PATCH 4/6] Make changes based on code reviews. #ref: https://github.com/shadowsocks/shadowsocks-android/pull/2937#pullrequestreview-1204982297 --- core/build.gradle.kts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/build.gradle.kts b/core/build.gradle.kts index 5900ca2f77..96646af28f 100644 --- a/core/build.gradle.kts +++ b/core/build.gradle.kts @@ -37,7 +37,7 @@ android { cargo { module = "src/main/rust/shadowsocks-rust" libname = "sslocal" - targets = listOf("arm", "arm64") + targets = listOf("arm", "arm64", "x86", "x86_64") profile = findProperty("CARGO_PROFILE")?.toString() ?: currentFlavor extraCargoBuildArguments = listOf("--bin", libname!!) featureSpec.noDefaultBut(arrayOf( From 0097968d412a9e931f9de4fd5a53014bdf5382c3 Mon Sep 17 00:00:00 2001 From: dev4u Date: Sat, 21 Jan 2023 02:44:31 +0800 Subject: [PATCH 5/6] Sync rust submodule. --- core/src/main/rust/shadowsocks-rust | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/core/src/main/rust/shadowsocks-rust b/core/src/main/rust/shadowsocks-rust index 3b802b0453..c87eadbb72 160000 --- a/core/src/main/rust/shadowsocks-rust +++ b/core/src/main/rust/shadowsocks-rust @@ -1 +1 @@ -Subproject commit 3b802b04531d86451972454dd9f7d9850adbf206 +Subproject commit c87eadbb722e5bbcc3e967ca5f460e93b4bb4595 From 32369d6f57ba7d91296fe53f7a75cdab80392d76 Mon Sep 17 00:00:00 2001 From: dev4u Date: Thu, 26 Jan 2023 13:51:00 +0800 Subject: [PATCH 6/6] 1. Jcenter is obsolete, use mavenCentral instead 2. Update docker image's version --- .circleci/config.yml | 5 +++-- repositories.gradle.kts | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/.circleci/config.yml b/.circleci/config.yml index 291add5c70..09bbf491b4 100644 --- a/.circleci/config.yml +++ b/.circleci/config.yml @@ -3,10 +3,11 @@ jobs: build: working_directory: ~/code docker: - - image: cimg/android:2022.09.2-ndk + - image: cimg/android:2022.12-ndk +# ref: https://circleci.com/developer/images/image/cimg/android environment: GRADLE_OPTS: -Dorg.gradle.workers.max=1 -Dorg.gradle.daemon=false -Dkotlin.compiler.execution.strategy="in-process" - RUST_VERSION: 1.66.0 + RUST_VERSION: 1.66.1 steps: - checkout - run: git submodule update --init --recursive diff --git a/repositories.gradle.kts b/repositories.gradle.kts index 6a6a56afed..3d60cba95a 100644 --- a/repositories.gradle.kts +++ b/repositories.gradle.kts @@ -5,5 +5,5 @@ rootProject.extra.apply { repositories { google() - jcenter() + mavenCentral() }