Drop domain fronting and other network hacks since #1147
This commit is contained in:
1 parent
cf95e7b034
commit
2d20da70b8
8 files changed
+6
-248
No files matched your search
@@ -226,7 +226,6 @@ dependencies {
|
||||
implementation(platform("com.squareup.okhttp3:okhttp-bom:5.0.0-alpha.11"))
|
||||
implementation("com.squareup.okhttp3:okhttp-android")
|
||||
implementation("com.squareup.okhttp3:okhttp-coroutines")
|
||||
implementation("com.squareup.okhttp3:okhttp-dnsoverhttps")
|
||||
|
||||
implementation("com.squareup.okio:okio-jvm:3.3.0")
|
||||
|
||||
|
||||
@@ -18,6 +18,7 @@ package com.hippo.ehviewer
|
||||
|
||||
import android.app.Application
|
||||
import android.content.ComponentCallbacks2
|
||||
import android.os.Build
|
||||
import androidx.appcompat.app.AppCompatDelegate
|
||||
import androidx.collection.LruCache
|
||||
import androidx.lifecycle.ProcessLifecycleOwner
|
||||
@@ -26,11 +27,8 @@ import coil.decode.ImageDecoderDecoder
|
||||
import coil.util.DebugLogger
|
||||
import com.chuckerteam.chucker.api.ChuckerInterceptor
|
||||
import com.hippo.ehviewer.client.EhCookieStore
|
||||
import com.hippo.ehviewer.client.EhDns
|
||||
import com.hippo.ehviewer.client.EhSSLSocketFactory
|
||||
import com.hippo.ehviewer.client.EhTagDatabase
|
||||
import com.hippo.ehviewer.client.data.GalleryDetail
|
||||
import com.hippo.ehviewer.client.install
|
||||
import com.hippo.ehviewer.coil.MergeInterceptor
|
||||
import com.hippo.ehviewer.coil.diskCache
|
||||
import com.hippo.ehviewer.coil.imageLoader
|
||||
@@ -48,6 +46,9 @@ import com.hippo.ehviewer.yorozuya.FileUtils
|
||||
import eu.kanade.tachiyomi.util.lang.launchIO
|
||||
import eu.kanade.tachiyomi.util.lang.withUIContext
|
||||
import kotlinx.coroutines.DelicateCoroutinesApi
|
||||
import okhttp3.AsyncDns
|
||||
import okhttp3.Dns
|
||||
import okhttp3.android.AndroidAsyncDns
|
||||
import okio.Path.Companion.toOkioPath
|
||||
import splitties.arch.room.roomDb
|
||||
import splitties.init.appCtx
|
||||
@@ -146,15 +147,15 @@ class EhApplication : Application(), ImageLoaderFactory {
|
||||
|
||||
companion object {
|
||||
val nonCacheOkHttpClient by lazy {
|
||||
val systemDns = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) AsyncDns.toDns(AndroidAsyncDns.IPv4, AndroidAsyncDns.IPv6) else Dns.SYSTEM
|
||||
httpClient {
|
||||
cookieJar(EhCookieStore)
|
||||
dns(EhDns)
|
||||
dns(systemDns)
|
||||
addInterceptor(
|
||||
ChuckerInterceptor.Builder(appCtx).apply {
|
||||
alwaysReadResponseBody(false)
|
||||
}.build(),
|
||||
)
|
||||
if (Settings.dF) install(EhSSLSocketFactory)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package com.hippo.ehviewer
|
||||
|
||||
import com.hippo.ehviewer.client.hosts
|
||||
import com.hippo.ehviewer.client.hostsDsl
|
||||
|
||||
val builtInHosts = hostsDsl {
|
||||
hosts("ehgt.org", "gt0.ehgt.org", "gt1.ehgt.org", "gt2.ehgt.org", "gt3.ehgt.org") {
|
||||
"37.48.89.44" blockedInCN false
|
||||
"81.171.10.48" blockedInCN false
|
||||
"178.162.139.24" blockedInCN false
|
||||
"178.162.140.212" blockedInCN false
|
||||
"2001:1af8:4700:a062:8::47de" blockedInCN false
|
||||
"2001:1af8:4700:a062:9::47de" blockedInCN true
|
||||
"2001:1af8:4700:a0c9:4::47de" blockedInCN false
|
||||
"2001:1af8:4700:a0c9:3::47de" blockedInCN true
|
||||
}
|
||||
hosts("e-hentai.org") {
|
||||
"104.20.134.21" blockedInCN false
|
||||
"104.20.135.21" blockedInCN false
|
||||
"172.67.0.127" blockedInCN false
|
||||
}
|
||||
hosts("exhentai.org") {
|
||||
"178.175.128.252" blockedInCN false
|
||||
"178.175.129.252" blockedInCN false
|
||||
"178.175.129.254" blockedInCN false
|
||||
"178.175.128.254" blockedInCN false
|
||||
"178.175.132.20" blockedInCN false
|
||||
"178.175.132.22" blockedInCN false
|
||||
}
|
||||
hosts("s.exhentai.org") {
|
||||
"178.175.129.254" blockedInCN false
|
||||
"178.175.128.254" blockedInCN false
|
||||
"178.175.132.22" blockedInCN false
|
||||
}
|
||||
hosts("repo.e-hentai.org") {
|
||||
"94.100.28.57" blockedInCN true
|
||||
"94.100.29.73" blockedInCN true
|
||||
}
|
||||
hosts("forums.e-hentai.org") {
|
||||
"94.100.18.243" blockedInCN false
|
||||
}
|
||||
hosts("ul.ehgt.org") {
|
||||
"94.100.24.82" blockedInCN true
|
||||
"94.100.24.72" blockedInCN true
|
||||
}
|
||||
hosts("raw.githubusercontent.com") {
|
||||
"151.101.0.133" blockedInCN true
|
||||
"151.101.64.133" blockedInCN false
|
||||
"151.101.128.133" blockedInCN false
|
||||
"151.101.192.133" blockedInCN false
|
||||
}
|
||||
}
|
||||
@@ -12,8 +12,6 @@ import kotlin.reflect.KProperty
|
||||
|
||||
object Settings : DataStorePreferences(null) {
|
||||
private const val KEY_SHOW_TAG_TRANSLATIONS = "show_tag_translations"
|
||||
private const val KEY_BUILT_IN_HOSTS = "built_in_hosts_2"
|
||||
private const val KEY_DOMAIN_FRONTING = "domain_fronting"
|
||||
|
||||
var downloadScheme by stringOrNullPref("image_scheme", null)
|
||||
var downloadAuthority by stringOrNullPref("image_authority", null)
|
||||
@@ -48,14 +46,11 @@ object Settings : DataStorePreferences(null) {
|
||||
var saveParseErrorBody by boolPref("save_parse_error_body", true)
|
||||
var saveCrashLog by boolPref("save_crash_log", true)
|
||||
var security by boolPref("require_unlock", false)
|
||||
var builtInHosts by boolPref(KEY_BUILT_IN_HOSTS, false)
|
||||
var removeImageFiles by boolPref("include_pic", true)
|
||||
var needSignIn by boolPref("need_sign_in", true)
|
||||
var blackDarkTheme by boolPref("black_dark_theme", false).observed { updateWhenAmoledModeChanges() }
|
||||
var preloadThumbAggressively by boolPref("preload_thumb_aggressively", false)
|
||||
var dF by boolPref(KEY_DOMAIN_FRONTING, false)
|
||||
var downloadOriginImage by boolPref("download_origin_image", false)
|
||||
var bypassVpn by boolPref("bypass_vpn", true)
|
||||
var thumbSizeDp by intPref("thumb_size_", 120)
|
||||
var favLocalCount by intPref("fav_local", 0)
|
||||
var favCloudCount by intPref("fav_cloud", 0)
|
||||
@@ -78,8 +73,6 @@ object Settings : DataStorePreferences(null) {
|
||||
init {
|
||||
if ("CN" == Locale.getDefault().country) {
|
||||
edit {
|
||||
if (KEY_BUILT_IN_HOSTS !in prefs) builtInHosts = true
|
||||
if (KEY_DOMAIN_FRONTING !in prefs) dF = true
|
||||
if (KEY_SHOW_TAG_TRANSLATIONS !in prefs) showTagTranslations = true
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,34 +0,0 @@
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import android.os.Build
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.builtInHosts
|
||||
import com.hippo.ehviewer.ui.settings.EhDoH
|
||||
import okhttp3.AsyncDns
|
||||
import okhttp3.Dns
|
||||
import okhttp3.android.AndroidAsyncDns
|
||||
import java.net.InetAddress
|
||||
|
||||
private typealias HostsMap = MutableMap<String, List<InetAddress>>
|
||||
|
||||
fun hostsDsl(builder: HostsMap.() -> Unit): HostsMap = mutableMapOf<String, List<InetAddress>>().apply(builder)
|
||||
|
||||
fun interface HostMapBuilder {
|
||||
infix fun String.blockedInCN(boolean: Boolean)
|
||||
}
|
||||
|
||||
fun HostsMap.hosts(vararg hosts: String, builder: HostMapBuilder.() -> Unit) = apply {
|
||||
hosts.forEach { host ->
|
||||
fun String.toInetAddress() = InetAddress.getByName(this).let { InetAddress.getByAddress(host, it.address) }
|
||||
mutableListOf<InetAddress>().apply {
|
||||
HostMapBuilder { if (!(Settings.dF && it)) add(toInetAddress()) }.apply(builder)
|
||||
put(host, this)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val systemDns = if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.Q) AsyncDns.toDns(AndroidAsyncDns.IPv4, AndroidAsyncDns.IPv6) else Dns.SYSTEM
|
||||
|
||||
object EhDns : Dns {
|
||||
override fun lookup(hostname: String): List<InetAddress> = builtInHosts[hostname].takeIf { Settings.builtInHosts } ?: EhDoH.lookup(hostname) ?: systemDns.lookup(hostname)
|
||||
}
|
||||
@@ -1,55 +0,0 @@
|
||||
/*
|
||||
* Copyright 2022 Tarsin Norbin
|
||||
*
|
||||
* This file is part of EhViewer
|
||||
*
|
||||
* EhViewer 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.
|
||||
*
|
||||
* EhViewer 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 EhViewer.
|
||||
* If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
package com.hippo.ehviewer.client
|
||||
|
||||
import android.util.Log
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.builtInHosts
|
||||
import okhttp3.OkHttpClient
|
||||
import java.net.InetAddress
|
||||
import java.net.Socket
|
||||
import java.security.KeyStore
|
||||
import javax.net.ssl.SSLSocket
|
||||
import javax.net.ssl.SSLSocketFactory
|
||||
import javax.net.ssl.TrustManagerFactory
|
||||
import javax.net.ssl.X509TrustManager
|
||||
|
||||
private const val EXCEPTIONAL_DOMAIN = "hath.network"
|
||||
private val sslSocketFactory = SSLSocketFactory.getDefault() as SSLSocketFactory
|
||||
|
||||
object EhSSLSocketFactory : SSLSocketFactory() {
|
||||
override fun getDefaultCipherSuites(): Array<String> = sslSocketFactory.defaultCipherSuites
|
||||
override fun getSupportedCipherSuites(): Array<String> = sslSocketFactory.supportedCipherSuites
|
||||
override fun createSocket(s: Socket, host: String, port: Int, autoClose: Boolean): Socket {
|
||||
val address = s.inetAddress.hostAddress.takeIf { host in builtInHosts || EXCEPTIONAL_DOMAIN in host || host in Settings.dohUrl }
|
||||
Log.d("EhSSLSocketFactory", "Host: $host Address: $address")
|
||||
return sslSocketFactory.createSocket(s, address ?: host, port, autoClose) as SSLSocket
|
||||
}
|
||||
override fun createSocket(host: String, port: Int): Socket = sslSocketFactory.createSocket(host, port)
|
||||
override fun createSocket(host: String, port: Int, localHost: InetAddress, localPort: Int): Socket = sslSocketFactory.createSocket(host, port, localHost, localPort)
|
||||
override fun createSocket(host: InetAddress, port: Int): Socket = sslSocketFactory.createSocket(host, port)
|
||||
override fun createSocket(address: InetAddress, port: Int, localAddress: InetAddress, localPort: Int): Socket = sslSocketFactory.createSocket(address, port, localAddress, localPort)
|
||||
}
|
||||
|
||||
fun OkHttpClient.Builder.install(sslSocketFactory: SSLSocketFactory) = apply {
|
||||
val factory = TrustManagerFactory.getInstance(TrustManagerFactory.getDefaultAlgorithm())!!
|
||||
factory.init(null as KeyStore?)
|
||||
val manager = factory.trustManagers!!
|
||||
val trustManager = manager.filterIsInstance<X509TrustManager>().first()
|
||||
sslSocketFactory(sslSocketFactory, trustManager)
|
||||
}
|
||||
@@ -24,14 +24,10 @@ import android.content.pm.verify.domain.DomainVerificationManager
|
||||
import android.content.pm.verify.domain.DomainVerificationUserState
|
||||
import android.graphics.Bitmap
|
||||
import android.graphics.ImageDecoder
|
||||
import android.net.ConnectivityManager
|
||||
import android.net.Network
|
||||
import android.net.NetworkCapabilities
|
||||
import android.net.NetworkRequest
|
||||
import android.net.Uri
|
||||
import android.os.Build
|
||||
import android.os.Bundle
|
||||
import android.util.Log
|
||||
import android.view.View
|
||||
import android.widget.Toast
|
||||
import androidx.activity.result.contract.ActivityResultContracts
|
||||
@@ -201,9 +197,6 @@ class MainActivity : EhActivity() {
|
||||
}
|
||||
|
||||
override fun onCreate(savedInstanceState: Bundle?) {
|
||||
if (Settings.dF && Settings.bypassVpn) {
|
||||
bypassVpn()
|
||||
}
|
||||
super.onCreate(savedInstanceState)
|
||||
binding = ActivityMainBinding.inflate(layoutInflater)
|
||||
setContentView(binding.root)
|
||||
@@ -251,19 +244,6 @@ class MainActivity : EhActivity() {
|
||||
}
|
||||
}
|
||||
|
||||
private fun bypassVpn() {
|
||||
val network = connectivityManager.activeNetwork
|
||||
val capabilities = connectivityManager.getNetworkCapabilities(network)
|
||||
capabilities?.let {
|
||||
if (it.hasTransport(NetworkCapabilities.TRANSPORT_VPN)) {
|
||||
val builder = NetworkRequest.Builder()
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_FOREGROUND)
|
||||
.addCapability(NetworkCapabilities.NET_CAPABILITY_NOT_VPN)
|
||||
connectivityManager.registerNetworkCallback(builder.build(), mNetworkCallback)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
@Throws(PackageManager.NameNotFoundException::class)
|
||||
private fun checkAppLinkVerify() {
|
||||
@@ -484,25 +464,4 @@ class MainActivity : EhActivity() {
|
||||
super.onProvideAssistContent(outContent)
|
||||
mShareUrl?.let { outContent?.webUri = Uri.parse(mShareUrl) }
|
||||
}
|
||||
|
||||
private val mNetworkCallback = object : ConnectivityManager.NetworkCallback() {
|
||||
private val TAG = "mNetworkCallback"
|
||||
|
||||
override fun onAvailable(network: Network) {
|
||||
Log.d(TAG, "onAvailable: $network")
|
||||
connectivityManager.bindProcessToNetwork(network)
|
||||
availableNetworks.add(network)
|
||||
}
|
||||
|
||||
override fun onLost(network: Network) {
|
||||
Log.d(TAG, "onLost: $network")
|
||||
val activeNetwork = availableNetworks.last()
|
||||
availableNetworks.remove(network)
|
||||
if (network == activeNetwork) {
|
||||
connectivityManager.bindProcessToNetwork(
|
||||
availableNetworks.takeIf { it.isNotEmpty() }?.last(),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package com.hippo.ehviewer.ui.settings
|
||||
|
||||
import android.annotation.SuppressLint
|
||||
import android.content.DialogInterface
|
||||
import android.content.Intent
|
||||
import android.net.Uri
|
||||
import android.provider.Settings.ACTION_APPLICATION_DETAILS_SETTINGS
|
||||
@@ -32,15 +31,12 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.res.stringResource
|
||||
import com.hippo.ehviewer.AppConfig
|
||||
import com.hippo.ehviewer.BuildConfig
|
||||
import com.hippo.ehviewer.EhApplication
|
||||
import com.hippo.ehviewer.EhDB
|
||||
import com.hippo.ehviewer.R
|
||||
import com.hippo.ehviewer.Settings
|
||||
import com.hippo.ehviewer.client.EhEngine
|
||||
import com.hippo.ehviewer.client.data.FavListUrlBuilder
|
||||
import com.hippo.ehviewer.client.systemDns
|
||||
import com.hippo.ehviewer.ui.LocalNavController
|
||||
import com.hippo.ehviewer.ui.legacy.EditTextDialogBuilder
|
||||
import com.hippo.ehviewer.ui.tools.observed
|
||||
import com.hippo.ehviewer.util.LogCat
|
||||
import com.hippo.ehviewer.util.ReadableTime
|
||||
@@ -48,10 +44,7 @@ import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import moe.tarsin.coroutines.runSuspendCatching
|
||||
import okhttp3.HttpUrl.Companion.toHttpUrl
|
||||
import okhttp3.dnsoverhttps.DnsOverHttps
|
||||
import java.io.File
|
||||
import java.net.InetAddress
|
||||
import java.util.zip.ZipEntry
|
||||
import java.util.zip.ZipOutputStream
|
||||
|
||||
@@ -133,37 +126,6 @@ fun AdvancedScreen() {
|
||||
entryValueRes = R.array.app_language_entry_values,
|
||||
value = Settings::language,
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(id = R.string.settings_advanced_built_in_hosts_title),
|
||||
value = Settings::builtInHosts,
|
||||
)
|
||||
Preference(title = stringResource(id = R.string.settings_advanced_dns_over_http_title)) {
|
||||
val builder = EditTextDialogBuilder(context, Settings.dohUrl, context.getString(R.string.settings_advanced_dns_over_http_hint))
|
||||
builder.setTitle(R.string.settings_advanced_dns_over_http_title)
|
||||
builder.setPositiveButton(android.R.string.ok, null)
|
||||
val dialog = builder.create().apply { show() }
|
||||
dialog.getButton(DialogInterface.BUTTON_POSITIVE).setOnClickListener {
|
||||
val text = builder.text.trim()
|
||||
runCatching {
|
||||
doh = if (text.isNotBlank()) buildDoHDNS(text) else null
|
||||
}.onFailure {
|
||||
builder.setError("Invalid URL!")
|
||||
}.onSuccess {
|
||||
Settings.dohUrl = text
|
||||
dialog.dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
SwitchPreference(
|
||||
title = stringResource(id = R.string.settings_advanced_domain_fronting_title),
|
||||
summary = stringResource(id = R.string.settings_advanced_domain_fronting_summary),
|
||||
value = Settings::dF,
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(id = R.string.settings_advanced_bypass_vpn_title),
|
||||
summary = stringResource(id = R.string.settings_advanced_bypass_vpn_summary),
|
||||
value = Settings::bypassVpn,
|
||||
)
|
||||
SwitchPreference(
|
||||
title = stringResource(id = R.string.preload_thumb_aggressively),
|
||||
value = Settings::preloadThumbAggressively,
|
||||
@@ -265,18 +227,3 @@ fun AdvancedScreen() {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun buildDoHDNS(url: String): DnsOverHttps {
|
||||
return DnsOverHttps.Builder().apply {
|
||||
client(EhApplication.okHttpClient)
|
||||
url(url.toHttpUrl())
|
||||
post(true)
|
||||
systemDns(systemDns)
|
||||
}.build()
|
||||
}
|
||||
|
||||
private var doh: DnsOverHttps? = Settings.dohUrl.runCatching { buildDoHDNS(this) }.getOrNull()
|
||||
|
||||
object EhDoH {
|
||||
fun lookup(hostname: String): List<InetAddress>? = doh?.runCatching { lookup(hostname).takeIf { it.isNotEmpty() } }?.onFailure { it.printStackTrace() }?.getOrNull()
|
||||
}
|
||||
Reference in new issue
Block a user