update fork #128

Manually merged
tristan merged 181 commits from mirror/monocles_chat_clean:master into master 2026-01-23 14:02:38 +01:00
5 changed files with 158 additions and 165 deletions
Showing only changes of commit a572fadf38 - Show all commits

Remove hardcoded DNS servers completely and improve DNS server setting

Arne 2026-01-08 18:29:26 +01:00

View file

@ -59,14 +59,14 @@ public class ConnectionSettingsFragment extends XmppPreferenceFragment {
final var dnsv4Server = (EditTextPreference) findPreference("dns_server_ipv4");
if (dnsv4Server != null) {
dnsv4Server.setText("194.242.2.2");
dnsv4Server.setText(null);
}
final var dnsv6Server = (EditTextPreference) findPreference("dns_server_ipv6");
if (dnsv6Server != null) {
dnsv6Server.setText("[2a07:e340::2]");
dnsv6Server.setText(null);
}
reconnectAccounts();
Toast.makeText(requireSettingsActivity(),R.string.dns_server_reset,Toast.LENGTH_LONG).show();
return true;
});
@ -105,7 +105,7 @@ public class ConnectionSettingsFragment extends XmppPreferenceFragment {
reconnectAccounts();
requireService().reinitializeMuclumbusService();
}
case AppSettings.SHOW_CONNECTION_OPTIONS, AppSettings.PREFER_IPV6 -> {
case AppSettings.SHOW_CONNECTION_OPTIONS, AppSettings.PREFER_IPV6, "dns_server_ipv4", "dns_server_ipv6" -> {
reconnectAccounts();
}
}
@ -146,4 +146,4 @@ public class ConnectionSettingsFragment extends XmppPreferenceFragment {
"%s is not %s",
activity.getClass().getName(), SettingsActivity.class.getName()));
}
}
}

View file

@ -12,6 +12,11 @@ package org.minidns;
import static org.webrtc.ApplicationContextProvider.getApplicationContext;
import android.content.SharedPreferences;
import android.text.TextUtils;
import androidx.preference.PreferenceManager;
import org.minidns.MiniDnsException.ErrorResponseException;
import org.minidns.MiniDnsException.NoQueryPossibleException;
import org.minidns.dnsmessage.DnsMessage;
@ -23,7 +28,6 @@ import org.minidns.dnsserverlookup.AndroidUsingReflection;
import org.minidns.dnsserverlookup.DnsServerLookupMechanism;
import org.minidns.dnsserverlookup.UnixUsingEtcResolvConf;
import org.minidns.record.Record.TYPE;
import org.minidns.util.CollectionsUtil;
import org.minidns.util.InetAddressUtil;
import org.minidns.util.MultipleIoException;
@ -39,11 +43,8 @@ import java.util.List;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.logging.Level;
import eu.siacs.conversations.R;
/**
* A minimal DNS client for SRV/A/AAAA/NS and CNAME lookups, with IDN support.
* This circumvents the missing javax.naming package on android.
@ -52,37 +53,20 @@ public class DnsClient extends AbstractDnsClient {
static final List<DnsServerLookupMechanism> LOOKUP_MECHANISMS = new CopyOnWriteArrayList<>();
static final Set<Inet4Address> STATIC_IPV4_DNS_SERVERS = new CopyOnWriteArraySet<>();
static final Set<Inet6Address> STATIC_IPV6_DNS_SERVERS = new CopyOnWriteArraySet<>();
static {
addDnsServerLookupMechanism(AndroidUsingExec.INSTANCE);
addDnsServerLookupMechanism(AndroidUsingReflection.INSTANCE);
addDnsServerLookupMechanism(UnixUsingEtcResolvConf.INSTANCE);
try {
Inet4Address dnsforgeV4Dns = InetAddressUtil.ipv4From(eu.siacs.conversations.Conversations.getContext().getString(R.string.default_dns_server_ipv4));
STATIC_IPV4_DNS_SERVERS.add(dnsforgeV4Dns);
} catch (IllegalArgumentException e) {
LOGGER.log(Level.WARNING, "Could not add static IPv4 DNS Server", e);
}
try {
Inet6Address dnsforgeV6Dns = InetAddressUtil.ipv6From(eu.siacs.conversations.Conversations.getContext().getString(R.string.default_dns_server_ipv6));
STATIC_IPV6_DNS_SERVERS.add(dnsforgeV6Dns);
} catch (IllegalArgumentException e) {
LOGGER.log(Level.WARNING, "Could not add static IPv6 DNS Server", e);
}
}
private static final Set<String> blacklistedDnsServers = Collections.newSetFromMap(new ConcurrentHashMap<String, Boolean>(4));
private final Set<InetAddress> nonRaServers = Collections.newSetFromMap(new ConcurrentHashMap<InetAddress, Boolean>(4));
private boolean askForDnssec = false;
private boolean askForDnssec = true;
private boolean disableResultFilter = false;
private boolean useHardcodedDnsServers = true;
private boolean useHardcodedDnsServers = false;
/**
* Create a new DNS client using the global default cache.
@ -103,36 +87,25 @@ public class DnsClient extends AbstractDnsClient {
}
private List<InetAddress> getServerAddresses() {
List<InetAddress> dnsServerAddresses = findDnsAddresses();
List<InetAddress> hardcodedDnsServers = new ArrayList<>();
if (useHardcodedDnsServers) {
InetAddress primaryHardcodedDnsServer, secondaryHardcodedDnsServer = null;
switch (ipVersionSetting) {
case v4v6:
primaryHardcodedDnsServer = getRandomHardcodedIpv4DnsServer();
secondaryHardcodedDnsServer = getRandomHarcodedIpv6DnsServer();
break;
case v6v4:
primaryHardcodedDnsServer = getRandomHarcodedIpv6DnsServer();
secondaryHardcodedDnsServer = getRandomHardcodedIpv4DnsServer();
break;
case v4only:
primaryHardcodedDnsServer = getRandomHardcodedIpv4DnsServer();
break;
case v6only:
primaryHardcodedDnsServer = getRandomHarcodedIpv6DnsServer();
break;
default:
throw new AssertionError("Unknown ipVersionSetting: " + ipVersionSetting);
InetAddress primaryHardcodedDnsServer = getRandomHardcodedIpv4DnsServer();
if (primaryHardcodedDnsServer != null) {
hardcodedDnsServers.add(primaryHardcodedDnsServer);
}
dnsServerAddresses.add(primaryHardcodedDnsServer);
InetAddress secondaryHardcodedDnsServer = getRandomHarcodedIpv6DnsServer();
if (secondaryHardcodedDnsServer != null) {
dnsServerAddresses.add(secondaryHardcodedDnsServer);
hardcodedDnsServers.add(secondaryHardcodedDnsServer);
}
}
return dnsServerAddresses;
if (!hardcodedDnsServers.isEmpty()) {
// If custom servers are defined, use them exclusively.
return hardcodedDnsServers;
} else {
// Otherwise, fall back to the system's DNS servers.
return findDnsAddresses();
}
}
@Override
@ -150,6 +123,10 @@ public class DnsClient extends AbstractDnsClient {
List<InetAddress> dnsServerAddresses = getServerAddresses();
if (dnsServerAddresses.isEmpty()) {
throw new NoQueryPossibleException(q);
}
List<IOException> ioExceptions = new ArrayList<>(dnsServerAddresses.size());
for (InetAddress dns : dnsServerAddresses) {
if (nonRaServers.contains(dns)) {
@ -179,22 +156,22 @@ public class DnsClient extends AbstractDnsClient {
}
switch (responseMessage.responseCode) {
case NO_ERROR:
case NX_DOMAIN:
break;
default:
String warning = "Response from " + dns + " asked for " + q.getQuestion() + " with error code: "
+ responseMessage.responseCode + '.';
if (!LOGGER.isLoggable(Level.FINE)) {
// Only append the responseMessage is log level is not fine. If it is fine or higher, the
// response has already been logged.
warning += "\n" + responseMessage;
}
LOGGER.warning(warning);
case NO_ERROR:
case NX_DOMAIN:
break;
default:
String warning = "Response from " + dns + " asked for " + q.getQuestion() + " with error code: "
+ responseMessage.responseCode + '.';
if (!LOGGER.isLoggable(Level.FINE)) {
// Only append the responseMessage is log level is not fine. If it is fine or higher, the
// response has already been logged.
warning += "\n" + responseMessage;
}
LOGGER.warning(warning);
ErrorResponseException exception = new ErrorResponseException(q, dnsQueryResult);
ioExceptions.add(exception);
continue;
ErrorResponseException exception = new ErrorResponseException(q, dnsQueryResult);
ioExceptions.add(exception);
continue;
}
return dnsQueryResult;
@ -259,7 +236,7 @@ public class DnsClient extends AbstractDnsClient {
} catch (SecurityException exception) {
LOGGER.log(Level.WARNING, "Could not lookup DNS server", exception);
}
if (res == null) {
if (res == null || res.isEmpty()) {
LOGGER.log(TRACE_LOG_LEVEL, "DnsServerLookupMechanism '" + mechanism.getName() + "' did not return any DNS server");
continue;
}
@ -279,15 +256,7 @@ public class DnsClient extends AbstractDnsClient {
new Object[] { mechanism.getName(), dnsServers });
}
assert !res.isEmpty();
// We could cache if res only contains IP addresses and avoid the verification in case. Not sure if its really that beneficial
// though, because the list returned by the server mechanism is rather short.
// Verify the returned DNS servers: Ensure that only valid IP addresses are returned. We want to avoid that something else,
// especially a valid DNS name is returned, as this would cause the following String to InetAddress conversation using
// getByName(String) to cause a DNS lookup, which would be performed outside of the realm of MiniDNS and therefore also outside
// of its DNSSEC guarantees.
// Verify the returned DNS servers: Ensure that only valid IP addresses are returned.
Iterator<String> it = res.iterator();
while (it.hasNext()) {
String potentialDnsServer = it.next();
@ -297,7 +266,7 @@ public class DnsClient extends AbstractDnsClient {
it.remove();
} else if (blacklistedDnsServers.contains(potentialDnsServer)) {
LOGGER.fine("The DNS server lookup mechanism '" + mechanism.getName()
+ "' returned a blacklisted result: '" + potentialDnsServer + "'");
+ "' returned a blacklisted result: '" + potentialDnsServer + "'");
it.remove();
}
}
@ -307,7 +276,7 @@ public class DnsClient extends AbstractDnsClient {
}
LOGGER.warning("The DNS server lookup mechanism '" + mechanism.getName()
+ "' returned not a single valid IP address after sanitazion");
+ "' returned not a single valid IP address after sanitazion");
res = null;
}
@ -346,9 +315,7 @@ public class DnsClient extends AbstractDnsClient {
int validServerAddresses = 0;
for (String dnsServerString : res) {
// The following invariant must hold: "dnsServerString is a IP address". Therefore findDNS() must only return a List of Strings
// representing IP addresses. Otherwise the following call of getByName(String) may perform a DNS lookup without MiniDNS being
// involved. Something we want to avoid.
// The following invariant must hold: "dnsServerString is a IP address".
assert InetAddressUtil.isIpAddress(dnsServerString);
InetAddress dnsServerAddress;
@ -380,20 +347,20 @@ public class DnsClient extends AbstractDnsClient {
List<InetAddress> dnsServers = new ArrayList<>(validServerAddresses);
switch (setting) {
case v4v6:
dnsServers.addAll(ipv4DnsServer);
dnsServers.addAll(ipv6DnsServer);
break;
case v6v4:
dnsServers.addAll(ipv6DnsServer);
dnsServers.addAll(ipv4DnsServer);
break;
case v4only:
dnsServers.addAll(ipv4DnsServer);
break;
case v6only:
dnsServers.addAll(ipv6DnsServer);
break;
case v4v6:
dnsServers.addAll(ipv4DnsServer);
dnsServers.addAll(ipv6DnsServer);
break;
case v6v4:
dnsServers.addAll(ipv6DnsServer);
dnsServers.addAll(ipv4DnsServer);
break;
case v4only:
dnsServers.addAll(ipv4DnsServer);
break;
case v6only:
dnsServers.addAll(ipv6DnsServer);
break;
}
return dnsServers;
}
@ -404,15 +371,10 @@ public class DnsClient extends AbstractDnsClient {
return;
}
synchronized (LOOKUP_MECHANISMS) {
// We can't use Collections.sort(CopyOnWriteArrayList) with Java 7. So we first create a temp array, sort it, and replace
// LOOKUP_MECHANISMS with the result. For more information about the Java 7 Collections.sort(CopyOnWriteArarayList) issue see
// http://stackoverflow.com/a/34827492/194894
// TODO: Remove that workaround once MiniDNS is Java 8 only.
ArrayList<DnsServerLookupMechanism> tempList = new ArrayList<>(LOOKUP_MECHANISMS.size() + 1);
tempList.addAll(LOOKUP_MECHANISMS);
tempList.add(dnsServerLookup);
// Sadly, this Collections.sort() does not with the CopyOnWriteArrayList on Java 7.
Collections.sort(tempList);
LOOKUP_MECHANISMS.clear();
@ -459,11 +421,29 @@ public class DnsClient extends AbstractDnsClient {
}
public InetAddress getRandomHardcodedIpv4DnsServer() {
return CollectionsUtil.getRandomFrom(STATIC_IPV4_DNS_SERVERS, insecureRandom);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String dnsServer = preferences.getString("dns_server_ipv4", null);
if (TextUtils.isEmpty(dnsServer)) {
return null;
}
try {
return InetAddressUtil.ipv4From(dnsServer);
} catch (IllegalArgumentException e) {
return null;
}
}
public InetAddress getRandomHarcodedIpv6DnsServer() {
return CollectionsUtil.getRandomFrom(STATIC_IPV6_DNS_SERVERS, insecureRandom);
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String dnsServer = preferences.getString("dns_server_ipv6", null);
if (TextUtils.isEmpty(dnsServer)) {
return null;
}
try {
return InetAddressUtil.ipv6From(dnsServer);
} catch (IllegalArgumentException e) {
return null;
}
}
private static Question getReverseIpLookupQuestionFor(DnsName dnsName) {
@ -491,6 +471,6 @@ public class DnsClient extends AbstractDnsClient {
throw new IllegalArgumentException("The provided inetAddress '" + inetAddress
+ "' is neither of type Inet4Address nor Inet6Address");
}
}
}
}
}

View file

@ -12,7 +12,10 @@ package org.minidns.constants;
import static org.webrtc.ApplicationContextProvider.getApplicationContext;
import android.content.res.Resources;
import android.content.SharedPreferences;
import android.text.TextUtils;
import androidx.preference.PreferenceManager;
import org.minidns.util.InetAddressUtil;
@ -46,9 +49,9 @@ public class DnsRootServer {
rootServerInet4Address('k', 193, 0, 14, 129),
rootServerInet4Address('l', 199, 7, 83, 42),
rootServerInet4Address('m', 202, 12, 27, 33),
};
};
protected static final Inet6Address[] IPV6_ROOT_SERVERS = new Inet6Address[] {
protected static final Inet6Address[] IPV6_ROOT_SERVERS = new Inet6Address[] {
rootServerInet6Address('a', 0x2001, 0x0503, 0xba3e, 0x0000, 0x0000, 0x000, 0x0002, 0x0030),
rootServerInet6Address('b', 0x2001, 0x0500, 0x0084, 0x0000, 0x0000, 0x000, 0x0000, 0x000b),
rootServerInet6Address('c', 0x2001, 0x0500, 0x0002, 0x0000, 0x0000, 0x000, 0x0000, 0x000c),
@ -59,65 +62,75 @@ public class DnsRootServer {
rootServerInet6Address('j', 0x2001, 0x0503, 0x0c27, 0x0000, 0x0000, 0x000, 0x0002, 0x0030),
rootServerInet6Address('l', 0x2001, 0x0500, 0x0003, 0x0000, 0x0000, 0x000, 0x0000, 0x0042),
rootServerInet6Address('m', 0x2001, 0x0dc3, 0x0000, 0x0000, 0x0000, 0x000, 0x0000, 0x0035),
};
};
private static Inet4Address rootServerInet4Address(char rootServerId, int addr0, int addr1, int addr2, int addr3) {
Inet4Address inetAddress;
String name = rootServerId + ".root-servers.net";
try {
inetAddress = (Inet4Address) InetAddress.getByAddress(name, new byte[] { (byte) addr0, (byte) addr1, (byte) addr2,
(byte) addr3 });
IPV4_ROOT_SERVER_MAP.put(rootServerId, inetAddress);
} catch (UnknownHostException e) {
// This should never happen, if it does it's our fault!
throw new RuntimeException(e);
}
return inetAddress;
private static Inet4Address rootServerInet4Address(char rootServerId, int addr0, int addr1, int addr2, int addr3) {
Inet4Address inetAddress;
String name = rootServerId + ".root-servers.net";
try {
inetAddress = (Inet4Address) InetAddress.getByAddress(name, new byte[] { (byte) addr0, (byte) addr1, (byte) addr2,
(byte) addr3 });
IPV4_ROOT_SERVER_MAP.put(rootServerId, inetAddress);
} catch (UnknownHostException e) {
// This should never happen, if it does it's our fault!
throw new RuntimeException(e);
}
private static Inet6Address rootServerInet6Address(char rootServerId, int addr0, int addr1, int addr2, int addr3, int addr4, int addr5, int addr6, int addr7) {
Inet6Address inetAddress;
String name = rootServerId + ".root-servers.net";
try {
inetAddress = (Inet6Address) InetAddress.getByAddress(name, new byte[] {
// @formatter:off
(byte) (addr0 >> 8), (byte) addr0, (byte) (addr1 >> 8), (byte) addr1,
(byte) (addr2 >> 8), (byte) addr2, (byte) (addr3 >> 8), (byte) addr3,
(byte) (addr4 >> 8), (byte) addr4, (byte) (addr5 >> 8), (byte) addr5,
(byte) (addr6 >> 8), (byte) addr6, (byte) (addr7 >> 8), (byte) addr7
// @formatter:on
});
IPV6_ROOT_SERVER_MAP.put(rootServerId, inetAddress);
} catch (UnknownHostException e) {
// This should never happen, if it does it's our fault!
throw new RuntimeException(e);
}
return inetAddress;
}
return inetAddress;
}
public static Inet4Address getRandomIpv4RootServer(Random random) {
if (getApplicationContext().getString(R.string.default_dns_server_ipv4).equals("194.242.2.2")) {
return IPV4_ROOT_SERVERS[random.nextInt(IPV4_ROOT_SERVERS.length)];
} else {
return InetAddressUtil.ipv4From(eu.siacs.conversations.Conversations.getContext().getString(R.string.default_dns_server_ipv4));
private static Inet6Address rootServerInet6Address(char rootServerId, int addr0, int addr1, int addr2, int addr3, int addr4, int addr5, int addr6, int addr7) {
Inet6Address inetAddress;
String name = rootServerId + ".root-servers.net";
try {
inetAddress = (Inet6Address) InetAddress.getByAddress(name, new byte[] {
// @formatter:off
(byte) (addr0 >> 8), (byte) addr0, (byte) (addr1 >> 8), (byte) addr1,
(byte) (addr2 >> 8), (byte) addr2, (byte) (addr3 >> 8), (byte) addr3,
(byte) (addr4 >> 8), (byte) addr4, (byte) (addr5 >> 8), (byte) addr5,
(byte) (addr6 >> 8), (byte) addr6, (byte) (addr7 >> 8), (byte) addr7
// @formatter:on
});
IPV6_ROOT_SERVER_MAP.put(rootServerId, inetAddress);
} catch (UnknownHostException e) {
// This should never happen, if it does it's our fault!
throw new RuntimeException(e);
}
return inetAddress;
}
public static Inet4Address getRandomIpv4RootServer(Random random) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String dnsServer = preferences.getString("dns_server_ipv4", null);
if (!TextUtils.isEmpty(dnsServer)) {
try {
return InetAddressUtil.ipv4From(dnsServer);
} catch (IllegalArgumentException e) {
// Invalid format, do not fall back to root servers
}
}
return null;
}
public static Inet6Address getRandomIpv6RootServer(Random random) {
if (getApplicationContext().getString(R.string.default_dns_server_ipv6).equals("[2a07:e340::2]")) {
return IPV6_ROOT_SERVERS[random.nextInt(IPV6_ROOT_SERVERS.length)];
} else {
return InetAddressUtil.ipv6From(eu.siacs.conversations.Conversations.getContext().getString(R.string.default_dns_server_ipv6));
public static Inet6Address getRandomIpv6RootServer(Random random) {
SharedPreferences preferences = PreferenceManager.getDefaultSharedPreferences(getApplicationContext());
String dnsServer = preferences.getString("dns_server_ipv6", null);
if (!TextUtils.isEmpty(dnsServer)) {
try {
return InetAddressUtil.ipv6From(dnsServer);
} catch (IllegalArgumentException e) {
// Invalid format, do not fall back to root servers
}
}
return null;
}
public static Inet4Address getIpv4RootServerById(char id) {
return IPV4_ROOT_SERVER_MAP.get(id);
}
public static Inet4Address getIpv4RootServerById(char id) {
return IPV4_ROOT_SERVER_MAP.get(id);
}
public static Inet6Address getIpv6RootServerById(char id) {
return IPV6_ROOT_SERVER_MAP.get(id);
}
public static Inet6Address getIpv6RootServerById(char id) {
return IPV6_ROOT_SERVER_MAP.get(id);
}
}
}

View file

@ -62,8 +62,8 @@
<bool name="show_nav_bar">true</bool>
<bool name="enforce_dane">false</bool>
<string name="default_chat_requests">disable</string>
<string name="default_dns_server_ipv4">194.242.2.2</string>
<string name="default_dns_server_ipv6">[2a07:e340::2]</string>
<string name="default_dns_server_ipv4"></string>
<string name="default_dns_server_ipv6"></string>
<bool name="default_custom_tab">true</bool>
<integer name="automatic_backup">0</integer>
<bool name="call_integration">true</bool>

View file

@ -1317,16 +1317,16 @@
<string name="pref_showtextformatting_sum">Show text formatting toolbar in chat while keyboard is shown</string>
<string name="pref_showtextformatting">Text formatting toolbar</string>
<string name="pref_chat_requests">Hide chats in Chat Requests area</string>
<string name="pref_dns_server_ipv4_summary">Change the default privacy focused IPv4 DNS server (Mullvad). Empty the field to use the DNS server of your device or VPN</string>
<string name="pref_dns_server_ipv4_summary">Add a custom IPv4 DNS server. Empty the field to use the DNS server of your device or VPN</string>
<string name="pref_dns_server_ipv4_title">IPv4 DNS server</string>
<string name="pref_dns_server_ipv6_title">IPv6 DNS server</string>
<string name="pref_dns_server_ipv6_summary">Change the default privacy focused IPv6 DNS server (Mullvad). Empty the field to use the DNS server of your device or VPN</string>
<string name="pref_dns_server_ipv6_summary">Add a custom IPv6 DNS server. Empty the field to use the DNS server of your device or VPN</string>
<string name="all_chats">All chats</string>
<string name="unread_chats">Unread chats</string>
<string name="direct_messages">Direct messages</string>
<string name="channels">Channels</string>
<string name="manage_account">Manage account</string>
<string name="pref_reset_dns_server_summary">Reset the default DNS servers to the privacy focused DNS provider Mullvad</string>
<string name="pref_reset_dns_server_summary">Reset the custom DNS servers to the Android systems DNS server</string>
<string name="pref_reset_dns_server_title">Reset DNS</string>
<string name="dns_server_reset">DNS reset</string>
<string name="pref_category_settings">Settings</string>