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