diff --git a/app/src/main/java/eu/faircode/email/DnsHelper.java b/app/src/main/java/eu/faircode/email/DnsHelper.java
index abba732508..24d12c2f1f 100644
--- a/app/src/main/java/eu/faircode/email/DnsHelper.java
+++ b/app/src/main/java/eu/faircode/email/DnsHelper.java
@@ -33,7 +33,9 @@ import androidx.annotation.NonNull;
import androidx.preference.PreferenceManager;
import org.minidns.AbstractDnsClient;
+import org.minidns.DnsCache;
import org.minidns.DnsClient;
+import org.minidns.cache.LruCache;
import org.minidns.dane.DaneVerifier;
import org.minidns.dnsmessage.DnsMessage;
import org.minidns.dnsqueryresult.DnsQueryResult;
@@ -465,6 +467,26 @@ public class DnsHelper {
return result;
}
+ static void clear(Context context) {
+ try {
+ SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context);
+ SharedPreferences.Editor editor = prefs.edit();
+ for (String key : prefs.getAll().keySet())
+ if (key != null && key.startsWith("dns."))
+ editor.remove(key);
+ editor.apply();
+
+ for (ResolverApi resolver : new ResolverApi[]{DnssecResolverApi.INSTANCE, ResolverApi.INSTANCE}) {
+ AbstractDnsClient client = resolver.getClient();
+ DnsCache cache = client.getCache();
+ if (cache instanceof LruCache)
+ ((LruCache) cache).clear();
+ }
+ } catch (Throwable ex) {
+ Log.e(ex);
+ }
+ }
+
static boolean hasDnsSec() {
if (BuildConfig.PLAY_STORE_RELEASE)
return false;
diff --git a/app/src/main/java/eu/faircode/email/EmailService.java b/app/src/main/java/eu/faircode/email/EmailService.java
index c2c2bc8775..b22b185cb6 100644
--- a/app/src/main/java/eu/faircode/email/EmailService.java
+++ b/app/src/main/java/eu/faircode/email/EmailService.java
@@ -637,8 +637,20 @@ public class EmailService implements AutoCloseable {
// throw new MailConnectException(
// new SocketConnectException("Debug", new IOException("Test"), host, port, 0));
- main = DnsHelper.getByName(context, host, dnssec);
- EntityLog.log(context, EntityLog.Type.Network, "Main address=" + main);
+ String key = "dns." + host;
+ try {
+ main = DnsHelper.getByName(context, host, dnssec);
+ EntityLog.log(context, EntityLog.Type.Network, "Main address=" + main);
+ prefs.edit().putString(key, main.getHostAddress()).apply();
+ } catch (UnknownHostException ex) {
+ String last = prefs.getString(key, null);
+ if (TextUtils.isEmpty(last))
+ throw ex;
+ else {
+ EntityLog.log(context, EntityLog.Type.Network, "Using " + key + "=" + last);
+ main = DnsHelper.getByName(context, last, dnssec);
+ }
+ }
boolean prefer_ip4 = prefs.getBoolean("prefer_ip4", true);
if (prefer_ip4 && main instanceof Inet6Address) {
diff --git a/app/src/main/java/eu/faircode/email/FragmentOptionsConnection.java b/app/src/main/java/eu/faircode/email/FragmentOptionsConnection.java
index 30176c583f..c5dce09817 100644
--- a/app/src/main/java/eu/faircode/email/FragmentOptionsConnection.java
+++ b/app/src/main/java/eu/faircode/email/FragmentOptionsConnection.java
@@ -99,6 +99,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
private SwitchCompat swDnsCustom;
private TextView tvDnsExtra;
private EditText etDnsExtra;
+ private SwitchCompat swDnsClear;
private SwitchCompat swTcpKeepAlive;
private SwitchCompat swSslUpdate;
private SwitchCompat swSslHarden;
@@ -133,7 +134,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
"download_headers", "download_eml", "download_plain",
"require_validated", "require_validated_captive", "vpn_only",
"timeout", "prefer_ip4", "bind_socket", "standalone_vpn",
- "dns_extra", "dns_custom",
+ "dns_extra", "dns_custom", "dns_clear",
"tcp_keep_alive",
"ssl_update", "ssl_harden", "ssl_harden_strict", "cert_strict", "cert_transparency", "check_names",
"open_safe", "http_redirect",
@@ -169,6 +170,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
swDnsCustom = view.findViewById(R.id.swDnsCustom);
tvDnsExtra = view.findViewById(R.id.tvDnsExtra);
etDnsExtra = view.findViewById(R.id.etDnsExtra);
+ swDnsClear = view.findViewById(R.id.swDnsClear);
swTcpKeepAlive = view.findViewById(R.id.swTcpKeepAlive);
swSslUpdate = view.findViewById(R.id.swSslUpdate);
swSslHarden = view.findViewById(R.id.swSslHarden);
@@ -352,6 +354,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
swDnsCustom.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton buttonView, boolean checked) {
+ DnsHelper.clear(buttonView.getContext());
prefs.edit().putBoolean("dns_custom", checked).apply();
tvDnsExtra.setEnabled(checked || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q);
etDnsExtra.setEnabled(checked || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q);
@@ -375,6 +378,14 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
}
});
+ swDnsClear.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
+ @Override
+ public void onCheckedChanged(CompoundButton buttonView, boolean checked) {
+ DnsHelper.clear(buttonView.getContext());
+ prefs.edit().putBoolean("dns_clear", checked).apply();
+ }
+ });
+
swTcpKeepAlive.setOnCheckedChangeListener(new CompoundButton.OnCheckedChangeListener() {
@Override
public void onCheckedChanged(CompoundButton compoundButton, boolean checked) {
@@ -764,6 +775,7 @@ public class FragmentOptionsConnection extends FragmentBase implements SharedPre
etDnsExtra.setText(prefs.getString("dns_extra", null));
tvDnsExtra.setEnabled(swDnsCustom.isChecked() || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q);
etDnsExtra.setEnabled(swDnsCustom.isChecked() || Build.VERSION.SDK_INT < Build.VERSION_CODES.Q);
+ swDnsClear.setChecked(prefs.getBoolean("dns_clear", false));
swTcpKeepAlive.setChecked(prefs.getBoolean("tcp_keep_alive", false));
swSslUpdate.setChecked(prefs.getBoolean("ssl_update", true));
swSslHarden.setChecked(prefs.getBoolean("ssl_harden", false));
diff --git a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java
index 64996bdad6..494da43b3d 100644
--- a/app/src/main/java/eu/faircode/email/ServiceSynchronize.java
+++ b/app/src/main/java/eu/faircode/email/ServiceSynchronize.java
@@ -3092,6 +3092,7 @@ public class ServiceSynchronize extends ServiceBase implements SharedPreferences
if (ConnectionHelper.isConnected(ServiceSynchronize.this, active)) {
lastActive = active;
lastAcquired = new Date().getTime();
+ DnsHelper.clear(ServiceSynchronize.this);
EntityLog.log(ServiceSynchronize.this, EntityLog.Type.Network,
reason + ": new active network=" + active + "/" + lastActive);
}
diff --git a/app/src/main/res/layout/fragment_options_connection.xml b/app/src/main/res/layout/fragment_options_connection.xml
index 0959a2baeb..da2aa99d2b 100644
--- a/app/src/main/res/layout/fragment_options_connection.xml
+++ b/app/src/main/res/layout/fragment_options_connection.xml
@@ -475,6 +475,17 @@
app:layout_constraintStart_toStartOf="parent"
app:layout_constraintTop_toBottomOf="@id/tvDnsExtra" />
+
+
Standalone VPN
Preferred DNS server addresses (comma separated)
Use custom DNS resolver
+ Clear DNS cache on connectivity changes
TCP keep alive
Use updated SSL provider
Harden SSL connections