From 013fc188da0206d2e5fad0f29158dfcc61b03dc3 Mon Sep 17 00:00:00 2001 From: Mygod Date: Tue, 21 Apr 2020 05:14:44 +0800 Subject: [PATCH] Deprecate hosts --- .../com/github/shadowsocks/bg/BaseService.kt | 12 ++--- .../github/shadowsocks/bg/ProxyInstance.kt | 25 ++-------- .../shadowsocks/bg/TransproxyService.kt | 5 +- .../com/github/shadowsocks/bg/VpnService.kt | 10 ++-- .../shadowsocks/net/DnsResolverCompat.kt | 40 --------------- .../com/github/shadowsocks/net/HostsFile.kt | 38 -------------- .../preference/HostsSummaryProvider.kt | 33 ------------ .../com/github/shadowsocks/utils/Constants.kt | 1 - .../GlobalSettingsPreferenceFragment.kt | 38 -------------- ...owsableEditTextPreferenceDialogFragment.kt | 50 ------------------- .../main/res/drawable/ic_maps_menu_book.xml | 19 ------- mobile/src/main/res/xml/pref_global.xml | 6 +-- .../shadowsocks/tv/MainPreferenceFragment.kt | 21 -------- tv/src/main/res/xml/pref_main.xml | 5 +- 14 files changed, 19 insertions(+), 284 deletions(-) delete mode 100644 core/src/main/java/com/github/shadowsocks/net/HostsFile.kt delete mode 100644 core/src/main/java/com/github/shadowsocks/preference/HostsSummaryProvider.kt delete mode 100644 mobile/src/main/java/com/github/shadowsocks/preference/BrowsableEditTextPreferenceDialogFragment.kt delete mode 100644 mobile/src/main/res/drawable/ic_maps_menu_book.xml diff --git a/core/src/main/java/com/github/shadowsocks/bg/BaseService.kt b/core/src/main/java/com/github/shadowsocks/bg/BaseService.kt index 622374d450..91e5391d5c 100644 --- a/core/src/main/java/com/github/shadowsocks/bg/BaseService.kt +++ b/core/src/main/java/com/github/shadowsocks/bg/BaseService.kt @@ -35,10 +35,8 @@ import com.github.shadowsocks.aidl.IShadowsocksServiceCallback import com.github.shadowsocks.aidl.TrafficStats import com.github.shadowsocks.core.R import com.github.shadowsocks.net.DnsResolverCompat -import com.github.shadowsocks.net.HostsFile import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.utils.Action -import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.broadcastReceiver import com.github.shadowsocks.utils.readableMessage import com.google.firebase.analytics.FirebaseAnalytics @@ -239,7 +237,7 @@ object BaseService { val isVpnService get() = false - suspend fun startProcesses(hosts: HostsFile) { + suspend fun startProcesses() { val configRoot = (if (Build.VERSION.SDK_INT < 24 || app.getSystemService() ?.isUserUnlocked != false) app else Core.deviceStorage).noBackupFilesDir val udpFallback = data.udpFallback @@ -315,7 +313,6 @@ object BaseService { listOfNotNull(data.proxy, data.udpFallback).forEach { it.trafficMonitor?.persistStats(it.profile.id) } suspend fun preInit() { } - suspend fun getActiveNetwork() = if (Build.VERSION.SDK_INT >= 23) Core.connectivity.activeNetwork else null suspend fun resolver(host: String) = DnsResolverCompat.resolveOnActiveNetwork(host) suspend fun rawResolver(query: ByteArray) = DnsResolverCompat.resolveRawOnActiveNetwork(query) suspend fun openConnection(url: URL) = url.openConnection() @@ -355,9 +352,8 @@ object BaseService { try { Executable.killAll() // clean up old processes preInit() - val hosts = HostsFile(DataStore.publicStore.getString(Key.hosts) ?: "") - proxy.init(this@Interface, hosts) - data.udpFallback?.init(this@Interface, hosts) + proxy.init(this@Interface) + data.udpFallback?.init(this@Interface) if (profile.route == Acl.CUSTOM_RULES) try { withContext(Dispatchers.IO) { Acl.customRules.flatten(10, this@Interface::openConnection).also { @@ -372,7 +368,7 @@ object BaseService { Timber.w(it) stopRunner(false, it.readableMessage) } - startProcesses(hosts) + startProcesses() proxy.scheduleUpdate() data.udpFallback?.scheduleUpdate() diff --git a/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt b/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt index e3e47b3846..65fead71f7 100644 --- a/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt +++ b/core/src/main/java/com/github/shadowsocks/bg/ProxyInstance.kt @@ -26,8 +26,6 @@ import com.github.shadowsocks.Core import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.acl.AclSyncer import com.github.shadowsocks.database.Profile -import com.github.shadowsocks.net.DnsResolverCompat -import com.github.shadowsocks.net.HostsFile import com.github.shadowsocks.plugin.PluginConfiguration import com.github.shadowsocks.plugin.PluginManager import com.github.shadowsocks.preference.DataStore @@ -52,7 +50,7 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro val plugin by lazy { PluginManager.init(PluginConfiguration(profile.plugin ?: "")) } private var scheduleConfigUpdate = false - suspend fun init(service: BaseService.Interface, hosts: HostsFile) { + suspend fun init(service: BaseService.Interface) { if (profile.isSponsored) { scheduleConfigUpdate = true val mdg = MessageDigest.getInstance("SHA-1") @@ -86,23 +84,10 @@ class ProxyInstance(val profile: Profile, private val route: String = profile.ro // it's hard to resolve DNS on a specific interface so we'll do it here if (profile.host.parseNumericAddress() == null) { - profile.host = hosts.resolve(profile.host).run { - if (isEmpty()) try { - service.resolver(profile.host).firstOrNull() - } catch (_: IOException) { - null - } else { - val network = service.getActiveNetwork() ?: throw UnknownHostException() - val hasIpv4 = DnsResolverCompat.haveIpv4(network) - val hasIpv6 = DnsResolverCompat.haveIpv6(network) - firstOrNull { - when (it) { - is Inet4Address -> hasIpv4 - is Inet6Address -> hasIpv6 - else -> error(it) - } - } - } + profile.host = try { + service.resolver(profile.host).firstOrNull() + } catch (_: IOException) { + null }?.hostAddress ?: throw UnknownHostException() } } diff --git a/core/src/main/java/com/github/shadowsocks/bg/TransproxyService.kt b/core/src/main/java/com/github/shadowsocks/bg/TransproxyService.kt index ff30cf5329..ddb7d6b45d 100644 --- a/core/src/main/java/com/github/shadowsocks/bg/TransproxyService.kt +++ b/core/src/main/java/com/github/shadowsocks/bg/TransproxyService.kt @@ -23,7 +23,6 @@ package com.github.shadowsocks.bg import android.app.Service import android.content.Intent import com.github.shadowsocks.Core -import com.github.shadowsocks.net.HostsFile import com.github.shadowsocks.preference.DataStore import java.io.File @@ -57,9 +56,9 @@ redsocks { File(applicationInfo.nativeLibraryDir, Executable.REDSOCKS).absolutePath, "-c", "redsocks.conf")) } - override suspend fun startProcesses(hosts: HostsFile) { + override suspend fun startProcesses() { startRedsocksDaemon() - super.startProcesses(hosts) + super.startProcesses() } override fun onDestroy() { diff --git a/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt b/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt index cbbbf03f99..18cf47f179 100644 --- a/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt +++ b/core/src/main/java/com/github/shadowsocks/bg/VpnService.kt @@ -34,7 +34,10 @@ import com.github.shadowsocks.Core import com.github.shadowsocks.VpnRequestActivity import com.github.shadowsocks.acl.Acl import com.github.shadowsocks.core.R -import com.github.shadowsocks.net.* +import com.github.shadowsocks.net.ConcurrentLocalSocketListener +import com.github.shadowsocks.net.DefaultNetworkListener +import com.github.shadowsocks.net.DnsResolverCompat +import com.github.shadowsocks.net.Subnet import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.utils.Key import com.github.shadowsocks.utils.closeQuietly @@ -137,7 +140,6 @@ class VpnService : BaseVpnService(), BaseService.Interface { } override suspend fun preInit() = DefaultNetworkListener.start(this) { underlyingNetwork = it } - override suspend fun getActiveNetwork() = DefaultNetworkListener.get() override suspend fun resolver(host: String) = DnsResolverCompat.resolve(DefaultNetworkListener.get(), host) override suspend fun rawResolver(query: ByteArray) = // no need to listen for network here as this is only used for forwarding local DNS queries. @@ -145,9 +147,9 @@ class VpnService : BaseVpnService(), BaseService.Interface { DnsResolverCompat.resolveRaw(underlyingNetwork ?: throw IOException("no network"), query) override suspend fun openConnection(url: URL) = DefaultNetworkListener.get().openConnection(url) - override suspend fun startProcesses(hosts: HostsFile) { + override suspend fun startProcesses() { worker = ProtectWorker().apply { start() } - super.startProcesses(hosts) + super.startProcesses() sendFd(startVpn()) } 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 e62d35ce89..203cd532cc 100644 --- a/core/src/main/java/com/github/shadowsocks/net/DnsResolverCompat.kt +++ b/core/src/main/java/com/github/shadowsocks/net/DnsResolverCompat.kt @@ -29,14 +29,10 @@ import android.os.CancellationSignal import android.os.Looper import android.system.ErrnoException import android.system.Os -import android.system.OsConstants import com.github.shadowsocks.Core -import com.github.shadowsocks.utils.closeQuietly import com.github.shadowsocks.utils.int -import com.github.shadowsocks.utils.parseNumericAddress import kotlinx.coroutines.* import org.xbill.DNS.* -import timber.log.Timber import java.io.FileDescriptor import java.io.IOException import java.net.Inet4Address @@ -58,42 +54,6 @@ sealed class DnsResolverCompat { } } - /** - * Based on: https://android.googlesource.com/platform/frameworks/base/+/9f97f97/core/java/android/net/util/DnsUtils.java#341 - */ - private val address4 = "8.8.8.8".parseNumericAddress()!! - private val address6 = "2000::".parseNumericAddress()!! - suspend fun haveIpv4(network: Network) = checkConnectivity(network, OsConstants.AF_INET, address4) - suspend fun haveIpv6(network: Network) = checkConnectivity(network, OsConstants.AF_INET6, address6) - private suspend fun checkConnectivity(network: Network, domain: Int, addr: InetAddress) = try { - val socket = Os.socket(domain, OsConstants.SOCK_DGRAM, OsConstants.IPPROTO_UDP) - try { - instance.bindSocket(network, socket) - instance.connectUdp(socket, addr) - } finally { - socket.closeQuietly() - } - true - } catch (e: IOException) { - if ((e.cause as? ErrnoException)?.errno == OsConstants.EPERM) checkConnectivity(network, addr) else false - } catch (_: ErrnoException) { - false - } catch (e: ReflectiveOperationException) { - check(Build.VERSION.SDK_INT < 23) - Timber.w(e) - checkConnectivity(network, addr) - } - private fun checkConnectivity(network: Network, addr: InetAddress): Boolean { - return Core.connectivity.getLinkProperties(network)?.routes?.any { - try { - it.matches(addr) - } catch (e: RuntimeException) { - Timber.w(e) - false - } - } == true - } - override fun bindSocket(network: Network, socket: FileDescriptor) = instance.bindSocket(network, socket) override suspend fun resolve(network: Network, host: String) = instance.resolve(network, host) override suspend fun resolveOnActiveNetwork(host: String) = instance.resolveOnActiveNetwork(host) diff --git a/core/src/main/java/com/github/shadowsocks/net/HostsFile.kt b/core/src/main/java/com/github/shadowsocks/net/HostsFile.kt deleted file mode 100644 index bc4ad5c607..0000000000 --- a/core/src/main/java/com/github/shadowsocks/net/HostsFile.kt +++ /dev/null @@ -1,38 +0,0 @@ -/******************************************************************************* - * * - * Copyright (C) 2019 by Max Lv * - * Copyright (C) 2019 by Mygod Studio * - * * - * 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 . * - * * - *******************************************************************************/ - -package com.github.shadowsocks.net - -import com.github.shadowsocks.utils.parseNumericAddress -import java.net.InetAddress - -class HostsFile(input: String = "") { - private val map = mutableMapOf>() - init { - for (line in input.lineSequence()) { - val entries = line.substringBefore('#').splitToSequence(' ', '\t').filter { it.isNotEmpty() } - val address = entries.firstOrNull()?.parseNumericAddress() ?: continue - for (hostname in entries.drop(1)) map.computeIfAbsent(hostname) { LinkedHashSet(1) }.add(address) - } - } - - val configuredHostnames get() = map.size - fun resolve(hostname: String) = map[hostname]?.shuffled() ?: emptyList() -} diff --git a/core/src/main/java/com/github/shadowsocks/preference/HostsSummaryProvider.kt b/core/src/main/java/com/github/shadowsocks/preference/HostsSummaryProvider.kt deleted file mode 100644 index a3afa4bef7..0000000000 --- a/core/src/main/java/com/github/shadowsocks/preference/HostsSummaryProvider.kt +++ /dev/null @@ -1,33 +0,0 @@ -/******************************************************************************* - * * - * Copyright (C) 2019 by Max Lv * - * Copyright (C) 2019 by Mygod Studio * - * * - * 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 . * - * * - *******************************************************************************/ - -package com.github.shadowsocks.preference - -import androidx.preference.EditTextPreference -import androidx.preference.Preference -import com.github.shadowsocks.core.R -import com.github.shadowsocks.net.HostsFile - -object HostsSummaryProvider : Preference.SummaryProvider { - override fun provideSummary(preference: EditTextPreference?): CharSequence { - val count = HostsFile(preference!!.text ?: "").configuredHostnames - return preference.context.resources.getQuantityString(R.plurals.hosts_summary, count, count) - } -} 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 0a57fed740..52b6de20ec 100644 --- a/core/src/main/java/com/github/shadowsocks/utils/Constants.kt +++ b/core/src/main/java/com/github/shadowsocks/utils/Constants.kt @@ -64,7 +64,6 @@ object Key { const val dirty = "profileDirty" - const val hosts = "hosts" const val assetUpdateTime = "assetUpdateTime" // TV specific values diff --git a/mobile/src/main/java/com/github/shadowsocks/GlobalSettingsPreferenceFragment.kt b/mobile/src/main/java/com/github/shadowsocks/GlobalSettingsPreferenceFragment.kt index 23b8545e78..5e20ee2431 100644 --- a/mobile/src/main/java/com/github/shadowsocks/GlobalSettingsPreferenceFragment.kt +++ b/mobile/src/main/java/com/github/shadowsocks/GlobalSettingsPreferenceFragment.kt @@ -20,8 +20,6 @@ package com.github.shadowsocks -import android.app.Activity -import android.content.Intent import android.os.Build import android.os.Bundle import android.view.View @@ -30,24 +28,14 @@ import androidx.preference.Preference import androidx.preference.PreferenceFragmentCompat import androidx.preference.SwitchPreference import com.github.shadowsocks.bg.BaseService -import com.github.shadowsocks.plugin.showAllowingStateLoss -import com.github.shadowsocks.preference.BrowsableEditTextPreferenceDialogFragment import com.github.shadowsocks.preference.DataStore import com.github.shadowsocks.preference.EditTextPreferenceModifiers -import com.github.shadowsocks.preference.HostsSummaryProvider import com.github.shadowsocks.utils.DirectBoot import com.github.shadowsocks.utils.Key -import com.github.shadowsocks.utils.readableMessage import com.github.shadowsocks.utils.remove import com.github.shadowsocks.widget.MainListListener class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { - companion object { - private const val REQUEST_BROWSE = 1 - } - - private val hosts by lazy { findPreference(Key.hosts)!! } - override fun onCreatePreferences(savedInstanceState: Bundle?, rootKey: String?) { preferenceManager.preferenceDataStore = DataStore.publicStore DataStore.initGlobal() @@ -63,8 +51,6 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { true } else canToggleLocked.remove() - hosts.setOnBindEditTextListener(EditTextPreferenceModifiers.Monospace) - hosts.summaryProvider = HostsSummaryProvider val serviceMode = findPreference(Key.serviceMode)!! val portProxy = findPreference(Key.portProxy)!! portProxy.setOnBindEditTextListener(EditTextPreferenceModifiers.Port) @@ -78,7 +64,6 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { } val listener: (BaseService.State) -> Unit = { val stopped = it == BaseService.State.Stopped - hosts.isEnabled = stopped serviceMode.isEnabled = stopped portProxy.isEnabled = stopped portLocalDns.isEnabled = stopped @@ -96,29 +81,6 @@ class GlobalSettingsPreferenceFragment : PreferenceFragmentCompat() { listView.setOnApplyWindowInsetsListener(MainListListener) } - override fun onDisplayPreferenceDialog(preference: Preference?) { - if (preference == hosts) BrowsableEditTextPreferenceDialogFragment().apply { - setKey(hosts.key) - setTargetFragment(this@GlobalSettingsPreferenceFragment, REQUEST_BROWSE) - }.showAllowingStateLoss(parentFragmentManager, hosts.key) else super.onDisplayPreferenceDialog(preference) - } - - override fun onActivityResult(requestCode: Int, resultCode: Int, data: Intent?) { - when (requestCode) { - REQUEST_BROWSE -> { - if (resultCode != Activity.RESULT_OK) return - val activity = activity as MainActivity - try { - // we read and persist all its content here to avoid content URL permission issues - hosts.text = activity.contentResolver.openInputStream(data!!.data!!)!!.bufferedReader().readText() - } catch (e: Exception) { - activity.snackbar(e.readableMessage).show() - } - } - else -> super.onActivityResult(requestCode, resultCode, data) - } - } - override fun onDestroy() { MainActivity.stateListener = null super.onDestroy() diff --git a/mobile/src/main/java/com/github/shadowsocks/preference/BrowsableEditTextPreferenceDialogFragment.kt b/mobile/src/main/java/com/github/shadowsocks/preference/BrowsableEditTextPreferenceDialogFragment.kt deleted file mode 100644 index 83f480803e..0000000000 --- a/mobile/src/main/java/com/github/shadowsocks/preference/BrowsableEditTextPreferenceDialogFragment.kt +++ /dev/null @@ -1,50 +0,0 @@ -/******************************************************************************* - * * - * Copyright (C) 2019 by Max Lv * - * Copyright (C) 2019 by Mygod Studio * - * * - * 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 . * - * * - *******************************************************************************/ - -package com.github.shadowsocks.preference - -import android.content.ActivityNotFoundException -import android.content.Intent -import androidx.appcompat.app.AlertDialog -import androidx.core.os.bundleOf -import androidx.preference.EditTextPreferenceDialogFragmentCompat -import com.github.shadowsocks.MainActivity -import com.github.shadowsocks.R - -class BrowsableEditTextPreferenceDialogFragment : EditTextPreferenceDialogFragmentCompat() { - fun setKey(key: String) { - arguments = bundleOf(Pair(ARG_KEY, key)) - } - - override fun onPrepareDialogBuilder(builder: AlertDialog.Builder) { - super.onPrepareDialogBuilder(builder) - builder.setNeutralButton(R.string.browse) { _, _ -> - val activity = activity as MainActivity - try { - targetFragment!!.startActivityForResult(Intent(Intent.ACTION_GET_CONTENT).apply { - addCategory(Intent.CATEGORY_OPENABLE) - type = "*/*" - }, targetRequestCode) - return@setNeutralButton - } catch (_: ActivityNotFoundException) { } catch (_: SecurityException) { } - activity.snackbar(activity.getString(R.string.file_manager_missing)).show() - } - } -} diff --git a/mobile/src/main/res/drawable/ic_maps_menu_book.xml b/mobile/src/main/res/drawable/ic_maps_menu_book.xml deleted file mode 100644 index 38e3e255d7..0000000000 --- a/mobile/src/main/res/drawable/ic_maps_menu_book.xml +++ /dev/null @@ -1,19 +0,0 @@ - - - - - - diff --git a/mobile/src/main/res/xml/pref_global.xml b/mobile/src/main/res/xml/pref_global.xml index 9b839fdce8..9a4808d8b3 100644 --- a/mobile/src/main/res/xml/pref_global.xml +++ b/mobile/src/main/res/xml/pref_global.xml @@ -1,6 +1,6 @@ + app:initialExpandedChildrenCount="2"> - super.onPreferenceTreeClick(preference) } - override fun onDisplayPreferenceDialog(preference: Preference?) { - if (preference != hosts || startFilesForResult(Intent(Intent.ACTION_GET_CONTENT).setType("*/*"), REQUEST_HOSTS)) - super.onDisplayPreferenceDialog(preference) - } - private fun startFilesForResult(intent: Intent, requestCode: Int): Boolean { try { startActivityForResult(intent.addCategory(Intent.CATEGORY_OPENABLE), requestCode) @@ -291,16 +280,6 @@ class MainPreferenceFragment : LeanbackPreferenceFragmentCompat(), ShadowsocksCo Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show() } } - REQUEST_HOSTS -> { - if (resultCode != Activity.RESULT_OK) return - val context = requireContext() - try { - // we read and persist all its content here to avoid content URL permission issues - hosts.text = context.contentResolver.openInputStream(data!!.data!!)!!.bufferedReader().readText() - } catch (e: Exception) { - Toast.makeText(context, e.readableMessage, Toast.LENGTH_SHORT).show() - } - } else -> super.onActivityResult(requestCode, resultCode, data) } } diff --git a/tv/src/main/res/xml/pref_main.xml b/tv/src/main/res/xml/pref_main.xml index 7e8be9d2f9..1e60152712 100644 --- a/tv/src/main/res/xml/pref_main.xml +++ b/tv/src/main/res/xml/pref_main.xml @@ -20,14 +20,11 @@ + app:initialExpandedChildrenCount="1"> -