Update DoH (#2293)

This commit is contained in:
南宫雪珊 2022-12-12 12:03:43 +08:00 committed by GitHub
parent 583a5a541d
commit 673d97573c
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
6 changed files with 166 additions and 95 deletions

View File

@ -90,7 +90,7 @@ android {
} }
} }
} }
namespace = "org.lsposed.manager" namespace = defaultManagerPackageName
} }
autoResConfig { autoResConfig {

View File

@ -32,6 +32,7 @@ import android.os.Build;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.os.Process; import android.os.Process;
import android.provider.Settings;
import android.system.Os; import android.system.Os;
import android.text.TextUtils; import android.text.TextUtils;
import android.util.Log; import android.util.Log;
@ -43,7 +44,7 @@ import org.lsposed.hiddenapibypass.HiddenApiBypass;
import org.lsposed.manager.adapters.AppHelper; import org.lsposed.manager.adapters.AppHelper;
import org.lsposed.manager.repo.RepoLoader; import org.lsposed.manager.repo.RepoLoader;
import org.lsposed.manager.ui.activity.CrashReportActivity; import org.lsposed.manager.ui.activity.CrashReportActivity;
import org.lsposed.manager.util.DoHDNS; import org.lsposed.manager.util.CloudflareDNS;
import org.lsposed.manager.util.ModuleUtil; import org.lsposed.manager.util.ModuleUtil;
import org.lsposed.manager.util.Telemetry; import org.lsposed.manager.util.Telemetry;
import org.lsposed.manager.util.ThemeUtil; import org.lsposed.manager.util.ThemeUtil;
@ -86,7 +87,6 @@ public class App extends Application {
static { static {
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) { if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
// TODO: set specific class name
HiddenApiBypass.addHiddenApiExemptions(""); HiddenApiBypass.addHiddenApiExemptions("");
} }
Looper.myQueue().addIdleHandler(() -> { Looper.myQueue().addIdleHandler(() -> {
@ -101,19 +101,17 @@ public class App extends Application {
Looper.myQueue().addIdleHandler(() -> { Looper.myQueue().addIdleHandler(() -> {
if (App.getInstance() == null || App.getExecutorService() == null) return true; if (App.getInstance() == null || App.getExecutorService() == null) return true;
App.getExecutorService().submit(() -> { App.getExecutorService().submit(() -> AppHelper.getDenyList(false));
AppHelper.getDenyList(false);
});
return false; return false;
}); });
Looper.myQueue().addIdleHandler(() -> { Looper.myQueue().addIdleHandler(() -> {
if (App.getInstance() == null || App.getExecutorService() == null) return true; if (App.getInstance() == null || App.getExecutorService() == null) return true;
App.getExecutorService().submit((Runnable) ModuleUtil::getInstance); App.getExecutorService().submit(ModuleUtil::getInstance);
return false; return false;
}); });
Looper.myQueue().addIdleHandler(() -> { Looper.myQueue().addIdleHandler(() -> {
if (App.getInstance() == null || App.getExecutorService() == null) return true; if (App.getInstance() == null || App.getExecutorService() == null) return true;
App.getExecutorService().submit((Runnable) RepoLoader::getInstance); App.getExecutorService().submit(RepoLoader::getInstance);
return false; return false;
}); });
} }
@ -139,7 +137,7 @@ public class App extends Application {
} }
public static ExecutorService getExecutorService() { public static ExecutorService getExecutorService() {
return instance.executorService; return executorService;
} }
public static boolean isParasitic() { public static boolean isParasitic() {
@ -208,8 +206,11 @@ public class App extends Application {
instance = this; instance = this;
pref = PreferenceManager.getDefaultSharedPreferences(this); pref = PreferenceManager.getDefaultSharedPreferences(this);
if ("CN".equals(Locale.getDefault().getCountry())) { if (!pref.contains("doh")) {
if (!pref.contains("doh")) { var name = "private_dns_mode";
if ("hostname".equals(Settings.Global.getString(getContentResolver(), name))) {
pref.edit().putBoolean("doh", false).apply();
} else {
pref.edit().putBoolean("doh", true).apply(); pref.edit().putBoolean("doh", true).apply();
} }
} }
@ -264,26 +265,24 @@ public class App extends Application {
@NonNull @NonNull
public static OkHttpClient getOkHttpClient() { public static OkHttpClient getOkHttpClient() {
if (okHttpClient == null) { if (okHttpClient != null) return okHttpClient;
OkHttpClient.Builder builder = new OkHttpClient.Builder().cache(getOkHttpCache()); var builder = new OkHttpClient.Builder()
builder.addInterceptor(chain -> { .cache(getOkHttpCache())
var request = chain.request().newBuilder(); .dns(new CloudflareDNS());
request.header("User-Agent", TAG); if (BuildConfig.DEBUG) {
return chain.proceed(request.build()); var log = new HttpLoggingInterceptor();
});
HttpLoggingInterceptor log = new HttpLoggingInterceptor();
log.setLevel(HttpLoggingInterceptor.Level.HEADERS); log.setLevel(HttpLoggingInterceptor.Level.HEADERS);
if (BuildConfig.DEBUG) builder.addInterceptor(log); builder.addInterceptor(log);
okHttpClient = builder.dns(new DoHDNS(builder.build())).build();
} }
okHttpClient = builder.build();
return okHttpClient; return okHttpClient;
} }
@NonNull @NonNull
private static Cache getOkHttpCache() { public static Cache getOkHttpCache() {
if (okHttpCache == null) { if (okHttpCache != null) return okHttpCache;
okHttpCache = new Cache(new File(App.getInstance().getCacheDir(), "http_cache"), 50L * 1024L * 1024L); long size50MiB = 50 * 1024 * 1024;
} okHttpCache = new Cache(new File(instance.getCacheDir(), "http_cache"), size50MiB);
return okHttpCache; return okHttpCache;
} }

View File

@ -48,6 +48,7 @@ import org.lsposed.manager.databinding.FragmentSettingsBinding;
import org.lsposed.manager.repo.RepoLoader; import org.lsposed.manager.repo.RepoLoader;
import org.lsposed.manager.ui.activity.MainActivity; import org.lsposed.manager.ui.activity.MainActivity;
import org.lsposed.manager.util.BackupUtils; import org.lsposed.manager.util.BackupUtils;
import org.lsposed.manager.util.CloudflareDNS;
import org.lsposed.manager.util.LangList; import org.lsposed.manager.util.LangList;
import org.lsposed.manager.util.NavUtil; import org.lsposed.manager.util.NavUtil;
import org.lsposed.manager.util.ShortcutUtil; import org.lsposed.manager.util.ShortcutUtil;
@ -285,6 +286,22 @@ public class SettingsFragment extends BaseFragment {
}); });
} }
MaterialSwitchPreference prefDoH = findPreference("doh");
if (prefDoH != null) {
var dns = (CloudflareDNS) App.getOkHttpClient().dns();
if (!dns.noProxy) {
prefDoH.setEnabled(false);
prefDoH.setVisible(false);
var group = prefDoH.getParent();
assert group != null;
group.setVisible(false);
}
prefDoH.setOnPreferenceChangeListener((p, v) -> {
dns.DoH = (boolean) v;
return true;
});
}
SimpleMenuPreference language = findPreference("language"); SimpleMenuPreference language = findPreference("language");
if (language != null) { if (language != null) {
var tag = language.getValue(); var tag = language.getValue();

View File

@ -0,0 +1,66 @@
package org.lsposed.manager.util;
import android.os.Build;
import androidx.annotation.NonNull;
import org.lsposed.manager.App;
import java.net.InetAddress;
import java.net.Proxy;
import java.net.ProxySelector;
import java.net.UnknownHostException;
import java.util.List;
import okhttp3.ConnectionSpec;
import okhttp3.Dns;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.dnsoverhttps.DnsOverHttps;
import okhttp3.internal.platform.Platform;
public final class CloudflareDNS implements Dns {
private static final HttpUrl url = HttpUrl.get("https://cloudflare-dns.com/dns-query");
public boolean DoH = App.getPreferences().getBoolean("doh", false);
public boolean noProxy = ProxySelector.getDefault().select(url.uri()).get(0) == Proxy.NO_PROXY;
private final Dns cloudflare;
public CloudflareDNS() {
var trustManager = Platform.get().platformTrustManager();
var tls = ConnectionSpec.RESTRICTED_TLS;
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.Q) {
//noinspection deprecation
tls = new ConnectionSpec.Builder(tls)
.supportsTlsExtensions(false)
.build();
}
var builder = new DnsOverHttps.Builder()
.resolvePrivateAddresses(true)
.url(HttpUrl.get("https://cloudflare-dns.com/dns-query"))
.client(new OkHttpClient.Builder()
.cache(App.getOkHttpCache())
.sslSocketFactory(new NoSniFactory(), trustManager)
.connectionSpecs(List.of(tls))
.build());
try {
builder.bootstrapDnsHosts(List.of(
InetAddress.getByName("1.1.1.1"),
InetAddress.getByName("1.0.0.1"),
InetAddress.getByName("2606:4700:4700::1111"),
InetAddress.getByName("2606:4700:4700::1001")));
} catch (UnknownHostException ignored) {
}
cloudflare = builder.build();
}
@NonNull
@Override
public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
if (DoH && noProxy) {
return cloudflare.lookup(hostname);
} else {
return SYSTEM.lookup(hostname);
}
}
}

View File

@ -1,70 +0,0 @@
/*
* This file is part of LSPosed.
*
* LSPosed 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.
*
* LSPosed 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 LSPosed. If not, see <https://www.gnu.org/licenses/>.
*
* Copyright (C) 2020 EdXposed Contributors
* Copyright (C) 2021 LSPosed Contributors
*/
package org.lsposed.manager.util;
import androidx.annotation.NonNull;
import org.lsposed.manager.App;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.List;
import java.util.Locale;
import okhttp3.Dns;
import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.dnsoverhttps.DnsOverHttps;
public class DoHDNS implements Dns {
private static DnsOverHttps cloudflare;
private static DnsOverHttps tuna;
private static DnsOverHttps dnspod;
public DoHDNS(OkHttpClient client) {
var builder = new DnsOverHttps.Builder().resolvePrivateAddresses(true).client(client);
cloudflare = builder.url(HttpUrl.get("https://cloudflare-dns.com/dns-query")).build();
tuna = builder.url(HttpUrl.get("https://101.6.6.6:8443/dns-query")).build();
dnspod = builder.url(HttpUrl.get("https://doh.pub/dns-query")).build();
}
@NonNull
@Override
public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
if (App.getPreferences().getBoolean("doh", false)) {
try {
return cloudflare.lookup(hostname);
} catch (UnknownHostException e) {
try {
if ("CN".equals(Locale.getDefault().getCountry()))
return tuna.lookup(hostname);
} catch (UnknownHostException exception) {
try {
return dnspod.lookup(hostname);
} catch (UnknownHostException ignored) {
}
}
}
}
return SYSTEM.lookup(hostname);
}
}

View File

@ -0,0 +1,59 @@
package org.lsposed.manager.util;
import java.io.IOException;
import java.net.InetAddress;
import java.net.Socket;
import javax.net.ssl.SSLSocketFactory;
public final class NoSniFactory extends SSLSocketFactory {
private static final SSLSocketFactory defaultFactory = (SSLSocketFactory) getDefault();
@SuppressWarnings("deprecation")
private static final android.net.SSLCertificateSocketFactory openSSLSocket =
(android.net.SSLCertificateSocketFactory) android.net.SSLCertificateSocketFactory
.getDefault(1000);
@Override
public String[] getDefaultCipherSuites() {
return defaultFactory.getDefaultCipherSuites();
}
@Override
public String[] getSupportedCipherSuites() {
return defaultFactory.getSupportedCipherSuites();
}
@Override
public Socket createSocket(Socket s, String host, int port, boolean autoClose) throws IOException {
return config(defaultFactory.createSocket(s, host, port, autoClose));
}
@Override
public Socket createSocket(String host, int port) throws IOException {
return config(defaultFactory.createSocket(host, port));
}
@Override
public Socket createSocket(String host, int port, InetAddress localHost, int localPort) throws IOException {
return config(defaultFactory.createSocket(host, port, localHost, localPort));
}
@Override
public Socket createSocket(InetAddress host, int port) throws IOException {
return config(defaultFactory.createSocket(host, port));
}
@Override
public Socket createSocket(InetAddress address, int port, InetAddress localAddress, int localPort) throws IOException {
return config(defaultFactory.createSocket(address, port, localAddress, localPort));
}
private Socket config(Socket socket) {
try {
openSSLSocket.setHostname(socket, null);
openSSLSocket.setUseSessionTickets(socket, true);
} catch (IllegalArgumentException ignored) {
}
return socket;
}
}