summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorcyberta <cyberta@riseup.net>2023-04-18 12:21:50 +0000
committercyberta <cyberta@riseup.net>2023-04-18 12:21:50 +0000
commit7e1d7e2b0f4358be1e34cceef1282e6f8feb8a9e (patch)
treeb27c49d88dc1e176823ab452177e3af712390809
parent821cac0b60b85d0956cbe97de84766f660b907a6 (diff)
parent3ce9d2a5df2a193fd85f82b8201de57f1026302b (diff)
Merge branch 'DoH' into 'master'
DoH See merge request leap/bitmask_android!239
-rw-r--r--app/build.gradle4
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java5
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java2
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java115
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java29
-rw-r--r--app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java8
-rw-r--r--app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java8
7 files changed, 128 insertions, 43 deletions
diff --git a/app/build.gradle b/app/build.gradle
index 6db759b4..07cf4ca5 100644
--- a/app/build.gradle
+++ b/app/build.gradle
@@ -426,7 +426,9 @@ dependencies {
compileOnly 'com.squareup.dagger:dagger-compiler:1.2.2'
implementation 'com.github.pedrovgs:renderers:1.5'
implementation 'com.google.code.gson:gson:2.8.6'
- implementation 'com.squareup.okhttp3:okhttp:3.12.12'
+ implementation 'com.squareup.okhttp3:okhttp:4.10.0'
+ implementation 'com.squareup.okhttp3:okhttp-dnsoverhttps:4.10.0'
+ implementation 'org.conscrypt:conscrypt-android:2.5.2'
implementation 'androidx.legacy:legacy-support-core-utils:1.0.0'
implementation 'androidx.annotation:annotation:1.4.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
index 828ef27d..0ccef0ae 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/BitmaskApp.java
@@ -34,6 +34,10 @@ import androidx.appcompat.app.AppCompatDelegate;
import androidx.localbroadcastmanager.content.LocalBroadcastManager;
import androidx.multidex.MultiDexApplication;
+import org.conscrypt.Conscrypt;
+
+import java.security.Security;
+
import se.leap.bitmaskclient.BuildConfig;
import se.leap.bitmaskclient.appUpdate.DownloadBroadcastReceiver;
import se.leap.bitmaskclient.base.models.ProviderObservable;
@@ -59,6 +63,7 @@ public class BitmaskApp extends MultiDexApplication {
super.onCreate();
// Normal app init code...*/
PRNGFixes.apply();
+ Security.insertProviderAt(Conscrypt.newProvider(), 1);
SharedPreferences preferences = getSharedPreferences(SHARED_PREFERENCES, MODE_PRIVATE);
providerObservable = ProviderObservable.getInstance();
providerObservable.updateProvider(getSavedProviderFromSharedPreferences(preferences));
diff --git a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
index d65f6b52..2412efdd 100644
--- a/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
+++ b/app/src/main/java/se/leap/bitmaskclient/base/utils/ConfigHelper.java
@@ -277,7 +277,7 @@ public class ConfigHelper {
}
public static String getDomainFromMainURL(@NonNull String mainUrl) throws NullPointerException {
- return PublicSuffixDatabase.get().getEffectiveTldPlusOne(mainUrl).replaceFirst("http[s]?://", "").replaceFirst("/.*", "");
+ return PublicSuffixDatabase.Companion.get().getEffectiveTldPlusOne(mainUrl).replaceFirst("http[s]?://", "").replaceFirst("/.*", "");
}
public static boolean isCalyxOSWithTetheringSupport(Context context) {
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
index 5655e7b7..e8249692 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/DnsResolver.java
@@ -1,5 +1,9 @@
package se.leap.bitmaskclient.providersetup.connectivity;
+import static java.net.InetAddress.getByName;
+
+import android.util.Log;
+
import androidx.annotation.NonNull;
import java.net.InetAddress;
@@ -9,34 +13,109 @@ import java.util.List;
import de.blinkt.openvpn.core.VpnStatus;
import okhttp3.Dns;
+import okhttp3.HttpUrl;
+import okhttp3.OkHttpClient;
+import okhttp3.dnsoverhttps.DnsOverHttps;
import se.leap.bitmaskclient.base.models.Provider;
import se.leap.bitmaskclient.base.models.ProviderObservable;
import se.leap.bitmaskclient.base.utils.IPAddress;
-class DnsResolver implements Dns {
+public class DnsResolver implements Dns {
+ OkHttpClient dohHttpClient;
+ boolean preferDoH;
+
+ public DnsResolver(OkHttpClient dohHttpClient, boolean preferDoH) {
+ this.dohHttpClient = dohHttpClient;
+ this.preferDoH = preferDoH;
+ }
+ @NonNull
@Override
public List<InetAddress> lookup(@NonNull String hostname) throws UnknownHostException {
+ Log.d("DNS", "trying to resolve DNS for " + hostname);
+ List<InetAddress> list = null;
+ if (preferDoH) {
+ if ((list = tryLookupDoH(hostname)) == null) {
+ list = tryLookupSystemDNS(hostname);
+ }
+ } else {
+ if ((list = tryLookupSystemDNS(hostname)) == null) {
+ list = tryLookupDoH(hostname);
+ }
+ }
+
+ if (list != null) {
+ return list;
+ }
+
+ Log.d("DNS", "try hard coded IPs");
+ // let's check if there's an hard-coded IP we can use
+ ProviderObservable observable = ProviderObservable.getInstance();
+ Provider currentProvider;
+ if (observable.getProviderForDns() != null) {
+ currentProvider = observable.getProviderForDns();
+ } else {
+ currentProvider = observable.getCurrentProvider();
+ }
+ String ip = currentProvider.getIpForHostname(hostname);
+ if (!ip.isEmpty()) {
+ VpnStatus.logWarning("[API] Normal DNS resolution for " + hostname + " seems to be blocked. Circumventing.");
+ ArrayList<InetAddress> addresses = new ArrayList<>();
+ addresses.add(InetAddress.getByAddress(hostname, IPAddress.asBytes(ip)));
+ return addresses;
+ } else {
+ VpnStatus.logWarning("[API] Could not resolve DNS for " + hostname);
+ throw new UnknownHostException("Hostname " + hostname + " not found");
+ }
+ }
+
+ private List<InetAddress> tryLookupSystemDNS(@NonNull String hostname) throws RuntimeException, UnknownHostException {
try {
+ Log.d("DNS", "trying to resolve " + hostname + "with system DNS");
return Dns.SYSTEM.lookup(hostname);
} catch (UnknownHostException e) {
- ProviderObservable observable = ProviderObservable.getInstance();
- Provider currentProvider;
- if (observable.getProviderForDns() != null) {
- currentProvider = observable.getProviderForDns();
- } else {
- currentProvider = observable.getCurrentProvider();
- }
- String ip = currentProvider.getIpForHostname(hostname);
- if (!ip.isEmpty()) {
- VpnStatus.logWarning("[API] Normal DNS resolution for " + hostname + " seems to be blocked. Circumventing.");
- ArrayList<InetAddress> addresses = new ArrayList<>();
- addresses.add(InetAddress.getByAddress(hostname, IPAddress.asBytes(ip)));
- return addresses;
- } else {
- VpnStatus.logWarning("[API] Could not resolve DNS for " + hostname);
- throw new UnknownHostException("Hostname " + hostname + " not found");
- }
+ e.printStackTrace();
+ return null;
+ }
+ }
+
+ private List<InetAddress> tryLookupDoH(@NonNull String hostname) throws UnknownHostException {
+ DnsOverHttps njallaDoH = new DnsOverHttps.Builder().client(dohHttpClient)
+ .url(HttpUrl.get("https://dns.njal.la/dns-query"))
+ .bootstrapDnsHosts(getByName("95.215.19.53"), getByName("2001:67c:2354:2::53"))
+ .build();
+ try {
+ Log.d("DNS", "DoH via dns.njal.la");
+ return njallaDoH.lookup(hostname);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ Log.e("DNS", "DoH via dns.njal.la failed");
+ }
+
+ DnsOverHttps quad9 = new DnsOverHttps.Builder().client(dohHttpClient)
+ .url(HttpUrl.get("https://dns.quad9.net/dns-query"))
+ .bootstrapDnsHosts(getByName("9.9.9.9"), getByName("149.112.112.112"), getByName("2620:fe::fe"), getByName("2620:fe::9"))
+ .build();
+ try {
+ Log.d("DNS", "DoH via dns.quad9.net");
+ return quad9.lookup(hostname);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ Log.e("DNS", "DoH via dns.quad9.net failed");
+ }
+
+ DnsOverHttps cloudFlareDoHClient = new DnsOverHttps.Builder().client(dohHttpClient)
+ .url(HttpUrl.get("https://1.1.1.1/dns-query"))
+ .bootstrapDnsHosts(getByName("1.1.1.1"), getByName("1.0.0.1"))
+ .build();
+
+ try {
+ Log.d("DNS", "DoH via cloudflare 1.1.1.1");
+ return cloudFlareDoHClient.lookup(hostname);
+ } catch (UnknownHostException e) {
+ e.printStackTrace();
+ Log.e("DNS", "DoH via cloudflare failed");
}
+ return null;
}
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
index ea619263..97393551 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/OkHttpClientGenerator.java
@@ -17,8 +17,16 @@
package se.leap.bitmaskclient.providersetup.connectivity;
+import static android.text.TextUtils.isEmpty;
+import static se.leap.bitmaskclient.R.string.certificate_error;
+import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
+import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message;
+import static se.leap.bitmaskclient.R.string.keyChainAccessError;
+import static se.leap.bitmaskclient.R.string.server_unreachable_message;
+import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
+import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
+
import android.content.res.Resources;
-import android.net.LocalSocketAddress;
import android.os.Build;
import androidx.annotation.NonNull;
@@ -29,7 +37,6 @@ import org.json.JSONObject;
import java.io.IOException;
import java.net.InetSocketAddress;
import java.net.Proxy;
-import java.net.SocketAddress;
import java.net.UnknownHostException;
import java.security.KeyManagementException;
import java.security.KeyStoreException;
@@ -49,16 +56,6 @@ import okhttp3.HttpUrl;
import okhttp3.OkHttpClient;
import okhttp3.TlsVersion;
-import static android.text.TextUtils.isEmpty;
-import static se.leap.bitmaskclient.R.string.certificate_error;
-import static se.leap.bitmaskclient.R.string.error_io_exception_user_message;
-import static se.leap.bitmaskclient.R.string.error_no_such_algorithm_exception_user_message;
-import static se.leap.bitmaskclient.R.string.keyChainAccessError;
-import static se.leap.bitmaskclient.R.string.proxy;
-import static se.leap.bitmaskclient.R.string.server_unreachable_message;
-import static se.leap.bitmaskclient.providersetup.ProviderAPI.ERRORS;
-import static se.leap.bitmaskclient.base.utils.ConfigHelper.getProviderFormattedString;
-
/**
* Created by cyberta on 08.01.18.
*/
@@ -68,7 +65,7 @@ public class OkHttpClientGenerator {
Resources resources;
private final static String PROXY_HOST = "127.0.0.1";
- public OkHttpClientGenerator(/*SharedPreferences preferences,*/ Resources resources) {
+ public OkHttpClientGenerator(Resources resources) {
this.resources = resources;
}
@@ -133,13 +130,15 @@ public class OkHttpClientGenerator {
} else {
sslCompatFactory = new TLSCompatSocketFactory();
}
- sslCompatFactory.initSSLSocketFactory(clientBuilder);
clientBuilder.cookieJar(getCookieJar())
.connectionSpecs(Collections.singletonList(spec));
- clientBuilder.dns(new DnsResolver());
+
if (proxyPort != -1) {
clientBuilder.proxy(new Proxy(Proxy.Type.HTTP, new InetSocketAddress(PROXY_HOST, proxyPort)));
}
+
+ clientBuilder.dns(new DnsResolver(clientBuilder.build(), true));
+ sslCompatFactory.initSSLSocketFactory(clientBuilder);
return clientBuilder.build();
}
diff --git a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java
index cc68b5a8..1420d666 100644
--- a/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java
+++ b/app/src/main/java/se/leap/bitmaskclient/providersetup/connectivity/TLSCompatSocketFactory.java
@@ -28,8 +28,7 @@ import se.leap.bitmaskclient.base.utils.ConfigHelper;
/**
* Created by cyberta on 24.10.17.
- * This class ensures that modern TLS algorithms will also be used on old devices (Android 4.1 - Android 4.4.4) in order to avoid
- * attacks like POODLE.
+ * This class ensures that modern TLS algorithms will also be used on old devices
*/
public class TLSCompatSocketFactory extends SSLSocketFactory {
@@ -150,9 +149,8 @@ public class TLSCompatSocketFactory extends SSLSocketFactory {
}
private Socket enableTLSOnSocket(Socket socket) throws IllegalArgumentException {
- if(socket != null && (socket instanceof SSLSocket)) {
- ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.2"});
- //TODO: add a android version check as soon as a new Android API or bcjsse supports TLSv1.3
+ if((socket instanceof SSLSocket)) {
+ ((SSLSocket)socket).setEnabledProtocols(new String[] {"TLSv1.3", "TLSv1.2"});
}
return socket;
diff --git a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
index d30e8b7e..c272970d 100644
--- a/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
+++ b/app/src/test/java/se/leap/bitmaskclient/testutils/MockHelper.java
@@ -74,6 +74,7 @@ import se.leap.bitmaskclient.base.utils.ConfigHelper;
import se.leap.bitmaskclient.base.utils.FileHelper;
import se.leap.bitmaskclient.base.utils.InputStreamHelper;
import se.leap.bitmaskclient.base.utils.PreferenceHelper;
+import se.leap.bitmaskclient.providersetup.connectivity.DnsResolver;
import se.leap.bitmaskclient.providersetup.connectivity.OkHttpClientGenerator;
import se.leap.bitmaskclient.testutils.BackendMockResponses.BackendMockProvider;
import se.leap.bitmaskclient.testutils.matchers.BundleMatcher;
@@ -577,13 +578,14 @@ public class MockHelper {
public static OkHttpClientGenerator mockClientGenerator(boolean resolveDNS) throws UnknownHostException {
OkHttpClientGenerator mockClientGenerator = mock(OkHttpClientGenerator.class);
- OkHttpClient mockedOkHttpClient = mock(OkHttpClient.class, RETURNS_DEEP_STUBS);
+ OkHttpClient mockedOkHttpClient = mock(OkHttpClient.class);
+ DnsResolver mockedDnsResolver = mock(DnsResolver.class);
when(mockClientGenerator.initCommercialCAHttpClient(any(JSONObject.class), anyInt())).thenReturn(mockedOkHttpClient);
when(mockClientGenerator.initSelfSignedCAHttpClient(anyString(), anyInt(), any(JSONObject.class))).thenReturn(mockedOkHttpClient);
if (resolveDNS) {
- when(mockedOkHttpClient.dns().lookup(anyString())).thenReturn(new ArrayList<>());
+ when(mockedDnsResolver.lookup(anyString())).thenReturn(new ArrayList<>());
} else {
- when(mockedOkHttpClient.dns().lookup(anyString())).thenThrow(new UnknownHostException());
+ when(mockedDnsResolver.lookup(anyString())).thenThrow(new UnknownHostException());
}
return mockClientGenerator;
}