diff --git a/build.gradle b/build.gradle
index e16a28846c0d67edf3902549672da02b42692592..bcc19b7c957f6d22aa7f0b44cd499f470014d331 100644
--- a/build.gradle
+++ b/build.gradle
@@ -73,7 +73,7 @@ dependencies {
//zxing stopped supporting Java 7 so we have to stick with 3.3.3
//https://github.com/zxing/zxing/issues/1170
implementation 'com.google.zxing:core:3.3.3'
- implementation 'de.measite.minidns:minidns-hla:0.2.4'
+ implementation 'org.minidns:minidns-hla:1.0.4'
implementation 'me.leolin:ShortcutBadger:1.1.22@aar'
implementation 'org.whispersystems:signal-protocol-android:2.6.2'
implementation "com.wefika:flowlayout:0.4.1"
diff --git a/src/cheogram/res/drawable/shield.xml b/src/cheogram/res/drawable/shield.xml
new file mode 100644
index 0000000000000000000000000000000000000000..83ae5149cd98be79ffd5c717bdbd8e51c5e7858b
--- /dev/null
+++ b/src/cheogram/res/drawable/shield.xml
@@ -0,0 +1,10 @@
+
+
+
diff --git a/src/cheogram/res/drawable/shield_question.xml b/src/cheogram/res/drawable/shield_question.xml
new file mode 100644
index 0000000000000000000000000000000000000000..017b47736878871a6c64743606260ff4fa5fa1e1
--- /dev/null
+++ b/src/cheogram/res/drawable/shield_question.xml
@@ -0,0 +1,11 @@
+
+
+
diff --git a/src/main/java/de/gultsch/minidns/AndroidDNSClient.java b/src/main/java/de/gultsch/minidns/AndroidDNSClient.java
deleted file mode 100644
index 2c3731871c1f7ab7cf2fb5eaa869867dc31fd93c..0000000000000000000000000000000000000000
--- a/src/main/java/de/gultsch/minidns/AndroidDNSClient.java
+++ /dev/null
@@ -1,221 +0,0 @@
-package de.gultsch.minidns;
-
-import android.content.Context;
-import android.net.ConnectivityManager;
-import android.net.LinkProperties;
-import android.net.Network;
-import android.os.Build;
-import android.util.Log;
-
-import androidx.collection.LruCache;
-
-import com.google.common.base.Objects;
-import com.google.common.base.Strings;
-import com.google.common.collect.Collections2;
-import com.google.common.collect.ImmutableList;
-
-import de.measite.minidns.AbstractDNSClient;
-import de.measite.minidns.DNSMessage;
-import de.measite.minidns.Record;
-import de.measite.minidns.record.Data;
-
-import eu.siacs.conversations.Config;
-
-import java.io.IOException;
-import java.net.InetAddress;
-import java.time.Duration;
-import java.util.Collections;
-import java.util.List;
-
-public class AndroidDNSClient extends AbstractDNSClient {
-
- private static final long DNS_MAX_TTL = 86_400L;
-
- private static final LruCache QUERY_CACHE =
- new LruCache<>(1024);
- private final Context context;
- private final NetworkDataSource networkDataSource = new NetworkDataSource();
- private boolean askForDnssec = false;
-
- public AndroidDNSClient(final Context context) {
- super();
- this.setDataSource(networkDataSource);
- this.context = context;
- }
-
- private static String getPrivateDnsServerName(final LinkProperties linkProperties) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- return linkProperties.getPrivateDnsServerName();
- } else {
- return null;
- }
- }
-
- private static boolean isPrivateDnsActive(final LinkProperties linkProperties) {
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.P) {
- return linkProperties.isPrivateDnsActive();
- } else {
- return false;
- }
- }
-
- @Override
- protected DNSMessage.Builder newQuestion(final DNSMessage.Builder message) {
- message.setRecursionDesired(true);
- message.getEdnsBuilder()
- .setUdpPayloadSize(networkDataSource.getUdpPayloadSize())
- .setDnssecOk(askForDnssec);
- return message;
- }
-
- @Override
- protected DNSMessage query(final DNSMessage.Builder queryBuilder) throws IOException {
- final DNSMessage question = newQuestion(queryBuilder).build();
- for (final DNSServer dnsServer : getDNSServers()) {
- final QuestionServerTuple cacheKey = new QuestionServerTuple(dnsServer, question);
- final DNSMessage cachedResponse = queryCache(cacheKey);
- if (cachedResponse != null) {
- return cachedResponse;
- }
- final DNSMessage response = this.networkDataSource.query(question, dnsServer);
- if (response == null) {
- continue;
- }
- switch (response.responseCode) {
- case NO_ERROR:
- case NX_DOMAIN:
- break;
- default:
- continue;
- }
- cacheQuery(cacheKey, response);
- return response;
- }
- return null;
- }
-
- public boolean isAskForDnssec() {
- return askForDnssec;
- }
-
- public void setAskForDnssec(boolean askForDnssec) {
- this.askForDnssec = askForDnssec;
- }
-
- private List getDNSServers() {
- final ImmutableList.Builder dnsServerBuilder = new ImmutableList.Builder<>();
- final ConnectivityManager connectivityManager =
- (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
- final Network[] networks = getActiveNetworks(connectivityManager);
- for (final Network network : networks) {
- final LinkProperties linkProperties = connectivityManager.getLinkProperties(network);
- if (linkProperties == null) {
- continue;
- }
- final String privateDnsServerName = getPrivateDnsServerName(linkProperties);
- if (Strings.isNullOrEmpty(privateDnsServerName)) {
- final boolean isPrivateDns = isPrivateDnsActive(linkProperties);
- for (final InetAddress dnsServer : linkProperties.getDnsServers()) {
- if (isPrivateDns) {
- dnsServerBuilder.add(new DNSServer(dnsServer, Transport.TLS));
- } else {
- dnsServerBuilder.add(new DNSServer(dnsServer));
- }
- }
- } else {
- dnsServerBuilder.add(new DNSServer(privateDnsServerName, Transport.TLS));
- }
- }
- return dnsServerBuilder.build();
- }
-
- private Network[] getActiveNetworks(final ConnectivityManager connectivityManager) {
- if (connectivityManager == null) {
- return new Network[0];
- }
- if (android.os.Build.VERSION.SDK_INT >= android.os.Build.VERSION_CODES.M) {
- final Network activeNetwork = connectivityManager.getActiveNetwork();
- if (activeNetwork != null) {
- return new Network[] {activeNetwork};
- }
- }
- return connectivityManager.getAllNetworks();
- }
-
- private DNSMessage queryCache(final QuestionServerTuple key) {
- final DNSMessage cachedResponse;
- synchronized (QUERY_CACHE) {
- cachedResponse = QUERY_CACHE.get(key);
- if (cachedResponse == null) {
- return null;
- }
- final long expiresIn = expiresIn(cachedResponse);
- if (expiresIn < 0) {
- QUERY_CACHE.remove(key);
- return null;
- }
- if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O) {
- Log.d(
- Config.LOGTAG,
- "DNS query came from cache. expires in " + Duration.ofMillis(expiresIn));
- }
- }
- return cachedResponse;
- }
-
- private void cacheQuery(final QuestionServerTuple key, final DNSMessage response) {
- if (response.receiveTimestamp <= 0) {
- return;
- }
- synchronized (QUERY_CACHE) {
- QUERY_CACHE.put(key, response);
- }
- }
-
- private static long ttl(final DNSMessage dnsMessage) {
- final List> answerSection = dnsMessage.answerSection;
- if (answerSection == null || answerSection.isEmpty()) {
- final List> authoritySection = dnsMessage.authoritySection;
- if (authoritySection == null || authoritySection.isEmpty()) {
- return 0;
- } else {
- return Collections.min(Collections2.transform(authoritySection, d -> d.ttl));
- }
-
- } else {
- return Collections.min(Collections2.transform(answerSection, d -> d.ttl));
- }
- }
-
- private static long expiresAt(final DNSMessage dnsMessage) {
- return dnsMessage.receiveTimestamp + (Math.min(DNS_MAX_TTL, ttl(dnsMessage)) * 1000L);
- }
-
- private static long expiresIn(final DNSMessage dnsMessage) {
- return expiresAt(dnsMessage) - System.currentTimeMillis();
- }
-
- private static class QuestionServerTuple {
- private final DNSServer dnsServer;
- private final DNSMessage question;
-
- private QuestionServerTuple(final DNSServer dnsServer, final DNSMessage question) {
- this.dnsServer = dnsServer;
- this.question = question.asNormalizedVersion();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- QuestionServerTuple that = (QuestionServerTuple) o;
- return Objects.equal(dnsServer, that.dnsServer)
- && Objects.equal(question, that.question);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(dnsServer, question);
- }
- }
-}
diff --git a/src/main/java/de/gultsch/minidns/DNSServer.java b/src/main/java/de/gultsch/minidns/DNSServer.java
deleted file mode 100644
index 7486ec2c60c6a6601220b37bf19efbfc96f9e888..0000000000000000000000000000000000000000
--- a/src/main/java/de/gultsch/minidns/DNSServer.java
+++ /dev/null
@@ -1,104 +0,0 @@
-package de.gultsch.minidns;
-
-import com.google.common.base.MoreObjects;
-import com.google.common.base.Objects;
-import com.google.common.base.Preconditions;
-import com.google.common.collect.Iterables;
-
-import java.net.InetAddress;
-import java.util.Arrays;
-import java.util.Collections;
-import java.util.List;
-
-import javax.annotation.Nonnull;
-
-public final class DNSServer {
-
- public final InetAddress inetAddress;
- public final String hostname;
- public final int port;
- public final List transports;
-
- public DNSServer(InetAddress inetAddress, Integer port, Transport transport) {
- this.inetAddress = inetAddress;
- this.port = port == null ? 0 : port;
- this.transports = Collections.singletonList(transport);
- this.hostname = null;
- }
-
- public DNSServer(final String hostname, final Integer port, final Transport transport) {
- Preconditions.checkArgument(
- Arrays.asList(Transport.HTTPS, Transport.TLS).contains(transport),
- "hostname validation only works with TLS based transports");
- this.hostname = hostname;
- this.port = port == null ? 0 : port;
- this.transports = Collections.singletonList(transport);
- this.inetAddress = null;
- }
-
- public DNSServer(final String hostname, final Transport transport) {
- this(hostname, Transport.DEFAULT_PORTS.get(transport), transport);
- }
-
- public DNSServer(InetAddress inetAddress, Transport transport) {
- this(inetAddress, Transport.DEFAULT_PORTS.get(transport), transport);
- }
-
- public DNSServer(final InetAddress inetAddress) {
- this(inetAddress, 53, Arrays.asList(Transport.UDP, Transport.TCP));
- }
-
- public DNSServer(final InetAddress inetAddress, int port, List transports) {
- this(inetAddress, null, port, transports);
- }
-
- private DNSServer(
- final InetAddress inetAddress,
- final String hostname,
- final int port,
- final List transports) {
- this.inetAddress = inetAddress;
- this.hostname = hostname;
- this.port = port;
- this.transports = transports;
- }
-
- public Transport uniqueTransport() {
- return Iterables.getOnlyElement(this.transports);
- }
-
- public DNSServer asUniqueTransport(final Transport transport) {
- Preconditions.checkArgument(
- this.transports.contains(transport),
- "This DNS server does not have transport ",
- transport);
- return new DNSServer(inetAddress, hostname, port, Collections.singletonList(transport));
- }
-
- @Override
- @Nonnull
- public String toString() {
- return MoreObjects.toStringHelper(this)
- .add("inetAddress", inetAddress)
- .add("hostname", hostname)
- .add("port", port)
- .add("transports", transports)
- .toString();
- }
-
- @Override
- public boolean equals(Object o) {
- if (this == o) return true;
- if (o == null || getClass() != o.getClass()) return false;
- DNSServer dnsServer = (DNSServer) o;
- return port == dnsServer.port
- && Objects.equal(inetAddress, dnsServer.inetAddress)
- && Objects.equal(hostname, dnsServer.hostname)
- && Objects.equal(transports, dnsServer.transports);
- }
-
- @Override
- public int hashCode() {
- return Objects.hashCode(inetAddress, hostname, port, transports);
- }
-}
diff --git a/src/main/java/de/gultsch/minidns/DNSSocket.java b/src/main/java/de/gultsch/minidns/DNSSocket.java
deleted file mode 100644
index a3403115a73c097e231626436453f59ac3b7a0bc..0000000000000000000000000000000000000000
--- a/src/main/java/de/gultsch/minidns/DNSSocket.java
+++ /dev/null
@@ -1,199 +0,0 @@
-package de.gultsch.minidns;
-
-import android.util.Log;
-
-import com.google.common.base.Preconditions;
-import com.google.common.base.Strings;
-import com.google.common.util.concurrent.ListenableFuture;
-import com.google.common.util.concurrent.SettableFuture;
-
-import de.measite.minidns.DNSMessage;
-
-import eu.siacs.conversations.Config;
-
-import org.conscrypt.OkHostnameVerifier;
-
-import java.io.Closeable;
-import java.io.DataInputStream;
-import java.io.DataOutputStream;
-import java.io.EOFException;
-import java.io.IOException;
-import java.net.InetSocketAddress;
-import java.net.Socket;
-import java.net.SocketAddress;
-import java.security.cert.Certificate;
-import java.security.cert.X509Certificate;
-import java.util.HashMap;
-import java.util.Iterator;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.Semaphore;
-import java.util.concurrent.TimeUnit;
-import java.util.concurrent.TimeoutException;
-
-import javax.net.ssl.SSLPeerUnverifiedException;
-import javax.net.ssl.SSLSession;
-import javax.net.ssl.SSLSocket;
-import javax.net.ssl.SSLSocketFactory;
-
-final class DNSSocket implements Closeable {
-
- public static final int QUERY_TIMEOUT = 5_000;
-
- private final Semaphore semaphore = new Semaphore(1);
- private final Map> inFlightQueries = new HashMap<>();
- private final Socket socket;
- private final DataInputStream dataInputStream;
- private final DataOutputStream dataOutputStream;
-
- private DNSSocket(
- final Socket socket,
- final DataInputStream dataInputStream,
- final DataOutputStream dataOutputStream) {
- this.socket = socket;
- this.dataInputStream = dataInputStream;
- this.dataOutputStream = dataOutputStream;
- new Thread(this::readDNSMessages).start();
- }
-
- private void readDNSMessages() {
- try {
- while (socket.isConnected()) {
- final DNSMessage response = readDNSMessage();
- final SettableFuture future;
- synchronized (inFlightQueries) {
- future = inFlightQueries.remove(response.id);
- }
- if (future != null) {
- future.set(response);
- } else {
- Log.e(Config.LOGTAG, "no in flight query found for response id " + response.id);
- }
- }
- evictInFlightQueries(new EOFException());
- } catch (final IOException e) {
- evictInFlightQueries(e);
- }
- }
-
- private void evictInFlightQueries(final Exception e) {
- synchronized (inFlightQueries) {
- final Iterator>> iterator =
- inFlightQueries.entrySet().iterator();
- while (iterator.hasNext()) {
- final Map.Entry> entry = iterator.next();
- entry.getValue().setException(e);
- iterator.remove();
- }
- }
- }
-
- private static DNSSocket of(final Socket socket) throws IOException {
- final DataInputStream dataInputStream = new DataInputStream(socket.getInputStream());
- final DataOutputStream dataOutputStream = new DataOutputStream(socket.getOutputStream());
- return new DNSSocket(socket, dataInputStream, dataOutputStream);
- }
-
- public static DNSSocket connect(final DNSServer dnsServer) throws IOException {
- switch (dnsServer.uniqueTransport()) {
- case TCP:
- return connectTcpSocket(dnsServer);
- case TLS:
- return connectTlsSocket(dnsServer);
- default:
- throw new IllegalStateException("This is not a socket based transport");
- }
- }
-
- private static DNSSocket connectTcpSocket(final DNSServer dnsServer) throws IOException {
- Preconditions.checkArgument(dnsServer.uniqueTransport() == Transport.TCP);
- final SocketAddress socketAddress =
- new InetSocketAddress(dnsServer.inetAddress, dnsServer.port);
- final Socket socket = new Socket();
- socket.connect(socketAddress, QUERY_TIMEOUT / 2);
- socket.setSoTimeout(QUERY_TIMEOUT);
- return DNSSocket.of(socket);
- }
-
- private static DNSSocket connectTlsSocket(final DNSServer dnsServer) throws IOException {
- Preconditions.checkArgument(dnsServer.uniqueTransport() == Transport.TLS);
- final SSLSocketFactory factory = (SSLSocketFactory) SSLSocketFactory.getDefault();
- final SSLSocket sslSocket = (SSLSocket) factory.createSocket();
- if (Strings.isNullOrEmpty(dnsServer.hostname)) {
- final SocketAddress socketAddress =
- new InetSocketAddress(dnsServer.inetAddress, dnsServer.port);
- sslSocket.connect(socketAddress, QUERY_TIMEOUT / 2);
- sslSocket.setSoTimeout(QUERY_TIMEOUT);
- sslSocket.startHandshake();
- } else {
- final SocketAddress socketAddress = new InetSocketAddress(dnsServer.hostname, dnsServer.port);
- sslSocket.connect(socketAddress, QUERY_TIMEOUT / 2);
- sslSocket.setSoTimeout(QUERY_TIMEOUT);
- sslSocket.startHandshake();
- final SSLSession session = sslSocket.getSession();
- final Certificate[] peerCertificates = session.getPeerCertificates();
- if (peerCertificates.length == 0 || !(peerCertificates[0] instanceof X509Certificate)) {
- throw new IOException("Peer did not provide X509 certificates");
- }
- final X509Certificate certificate = (X509Certificate) peerCertificates[0];
- if (!OkHostnameVerifier.strictInstance().verify(dnsServer.hostname, certificate)) {
- throw new SSLPeerUnverifiedException("Peer did not provide valid certificates");
- }
- }
- return DNSSocket.of(sslSocket);
- }
-
- public DNSMessage query(final DNSMessage query) throws IOException, InterruptedException {
- try {
- return queryAsync(query).get(QUERY_TIMEOUT, TimeUnit.MILLISECONDS);
- } catch (final ExecutionException e) {
- final Throwable cause = e.getCause();
- if (cause instanceof IOException) {
- throw (IOException) cause;
- } else {
- throw new IOException(e);
- }
- } catch (final TimeoutException e) {
- throw new IOException(e);
- }
- }
-
- public ListenableFuture queryAsync(final DNSMessage query)
- throws InterruptedException, IOException {
- final SettableFuture responseFuture = SettableFuture.create();
- synchronized (this.inFlightQueries) {
- this.inFlightQueries.put(query.id, responseFuture);
- }
- this.semaphore.acquire();
- try {
- query.writeTo(this.dataOutputStream);
- this.dataOutputStream.flush();
- } finally {
- this.semaphore.release();
- }
- return responseFuture;
- }
-
- private DNSMessage readDNSMessage() throws IOException {
- final int length = this.dataInputStream.readUnsignedShort();
- byte[] data = new byte[length];
- int read = 0;
- while (read < length) {
- read += this.dataInputStream.read(data, read, length - read);
- }
- return new DNSMessage(data);
- }
-
- @Override
- public void close() throws IOException {
- this.socket.close();
- }
-
- public void closeQuietly() {
- try {
- this.socket.close();
- } catch (final IOException ignored) {
-
- }
- }
-}
diff --git a/src/main/java/de/gultsch/minidns/NetworkDataSource.java b/src/main/java/de/gultsch/minidns/NetworkDataSource.java
deleted file mode 100644
index 93909891d54a37b7a7c0b5570cb3d2fb7f8f885e..0000000000000000000000000000000000000000
--- a/src/main/java/de/gultsch/minidns/NetworkDataSource.java
+++ /dev/null
@@ -1,160 +0,0 @@
-package de.gultsch.minidns;
-
-import android.util.Log;
-
-import androidx.annotation.NonNull;
-
-import com.google.common.cache.CacheBuilder;
-import com.google.common.cache.CacheLoader;
-import com.google.common.cache.LoadingCache;
-import com.google.common.cache.RemovalListener;
-import com.google.common.collect.ImmutableList;
-
-import de.measite.minidns.DNSMessage;
-import de.measite.minidns.MiniDNSException;
-import de.measite.minidns.source.DNSDataSource;
-import de.measite.minidns.util.MultipleIoException;
-
-import eu.siacs.conversations.Config;
-
-import java.io.IOException;
-import java.net.DatagramPacket;
-import java.net.DatagramSocket;
-import java.net.InetAddress;
-import java.util.ArrayList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ExecutionException;
-import java.util.concurrent.TimeUnit;
-
-public class NetworkDataSource extends DNSDataSource {
-
- private static final LoadingCache socketCache =
- CacheBuilder.newBuilder()
- .removalListener(
- (RemovalListener)
- notification -> {
- final DNSServer dnsServer = notification.getKey();
- final DNSSocket dnsSocket = notification.getValue();
- if (dnsSocket == null) {
- return;
- }
- Log.d(Config.LOGTAG, "closing connection to " + dnsServer);
- dnsSocket.closeQuietly();
- })
- .expireAfterAccess(5, TimeUnit.MINUTES)
- .build(
- new CacheLoader() {
- @Override
- @NonNull
- public DNSSocket load(@NonNull final DNSServer dnsServer)
- throws Exception {
- Log.d(Config.LOGTAG, "establishing connection to " + dnsServer);
- return DNSSocket.connect(dnsServer);
- }
- });
-
- private static List transportsForPort(final int port) {
- final ImmutableList.Builder transportBuilder = new ImmutableList.Builder<>();
- for (final Map.Entry entry : Transport.DEFAULT_PORTS.entrySet()) {
- if (entry.getValue().equals(port)) {
- transportBuilder.add(entry.getKey());
- }
- }
- return transportBuilder.build();
- }
-
- @Override
- public DNSMessage query(final DNSMessage message, final InetAddress address, final int port)
- throws IOException {
- final List transports = transportsForPort(port);
- Log.w(
- Config.LOGTAG,
- "using legacy DataSource interface. guessing transports "
- + transports
- + " from port");
- if (transports.isEmpty()) {
- throw new IOException(String.format("No transports found for port %d", port));
- }
- return query(message, new DNSServer(address, port, transports));
- }
-
- public DNSMessage query(final DNSMessage message, final DNSServer dnsServer)
- throws IOException {
- Log.d(Config.LOGTAG, "using " + dnsServer);
- final List ioExceptions = new ArrayList<>();
- for (final Transport transport : dnsServer.transports) {
- try {
- final DNSMessage response =
- queryWithUniqueTransport(message, dnsServer.asUniqueTransport(transport));
- if (response != null && !response.truncated) {
- return response;
- }
- } catch (final IOException e) {
- ioExceptions.add(e);
- } catch (final InterruptedException e) {
- throw new IOException(e);
- }
- }
- MultipleIoException.throwIfRequired(ioExceptions);
- return null;
- }
-
- private DNSMessage queryWithUniqueTransport(final DNSMessage message, final DNSServer dnsServer)
- throws IOException, InterruptedException {
- final Transport transport = dnsServer.uniqueTransport();
- switch (transport) {
- case UDP:
- return queryUdp(message, dnsServer.inetAddress, dnsServer.port);
- case TCP:
- case TLS:
- return queryDnsSocket(message, dnsServer);
- default:
- throw new IOException(
- String.format("Transport %s has not been implemented", transport));
- }
- }
-
- protected DNSMessage queryUdp(
- final DNSMessage message, final InetAddress address, final int port)
- throws IOException {
- final DatagramPacket request = message.asDatagram(address, port);
- final byte[] buffer = new byte[udpPayloadSize];
- try (final DatagramSocket socket = new DatagramSocket()) {
- socket.setSoTimeout(timeout);
- socket.send(request);
- final DatagramPacket response = new DatagramPacket(buffer, buffer.length);
- socket.receive(response);
- DNSMessage dnsMessage = new DNSMessage(response.getData());
- if (dnsMessage.id != message.id) {
- throw new MiniDNSException.IdMismatch(message, dnsMessage);
- }
- return dnsMessage;
- }
- }
-
- protected DNSMessage queryDnsSocket(final DNSMessage message, final DNSServer dnsServer)
- throws IOException, InterruptedException {
- final DNSSocket cachedDnsSocket = socketCache.getIfPresent(dnsServer);
- if (cachedDnsSocket != null) {
- try {
- return cachedDnsSocket.query(message);
- } catch (final IOException e) {
- Log.d(
- Config.LOGTAG,
- "IOException occurred at cached socket. invalidating and falling through to new socket creation");
- socketCache.invalidate(dnsServer);
- }
- }
- try {
- return socketCache.get(dnsServer).query(message);
- } catch (final ExecutionException e) {
- final Throwable cause = e.getCause();
- if (cause instanceof IOException) {
- throw (IOException) cause;
- } else {
- throw new IOException(cause);
- }
- }
- }
-}
diff --git a/src/main/java/de/gultsch/minidns/Transport.java b/src/main/java/de/gultsch/minidns/Transport.java
deleted file mode 100644
index 3aabfacaa57177e6c3d1d6f5250f8303a3120063..0000000000000000000000000000000000000000
--- a/src/main/java/de/gultsch/minidns/Transport.java
+++ /dev/null
@@ -1,23 +0,0 @@
-package de.gultsch.minidns;
-
-import com.google.common.collect.ImmutableMap;
-
-import java.util.Map;
-
-public enum Transport {
- UDP,
- TCP,
- TLS,
- HTTPS;
-
- public static final Map DEFAULT_PORTS;
-
- static {
- final ImmutableMap.Builder builder = new ImmutableMap.Builder<>();
- builder.put(Transport.UDP, 53);
- builder.put(Transport.TCP, 53);
- builder.put(Transport.TLS, 853);
- builder.put(Transport.HTTPS, 443);
- DEFAULT_PORTS = builder.build();
- }
-}
diff --git a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
index dab84da05ead6515da48abcd1391b401d15ece9d..99054c2fdf5154ab5f0e7bbd9637ae87ef8fce4d 100644
--- a/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
+++ b/src/main/java/eu/siacs/conversations/ui/EditAccountActivity.java
@@ -1265,6 +1265,14 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
} else {
this.binding.otherDeviceKeysCard.setVisibility(View.GONE);
}
+ this.binding.verificationBox.setVisibility(View.VISIBLE);
+ if (mAccount.getXmppConnection() != null && mAccount.getXmppConnection().resolverAuthenticated()) {
+ this.binding.verificationMessage.setText("DNSSEC Verified");
+ this.binding.verificationIndicator.setImageResource(R.drawable.shield);
+ } else {
+ this.binding.verificationMessage.setText("Not DNSSEC Verified");
+ this.binding.verificationIndicator.setImageResource(R.drawable.shield_question);
+ }
} else {
final TextInputLayout errorLayout;
if (this.mAccount.errorStatus()) {
@@ -1287,6 +1295,7 @@ public class EditAccountActivity extends OmemoActivity implements OnAccountUpdat
removeErrorsOnAllBut(errorLayout);
this.binding.stats.setVisibility(View.GONE);
this.binding.otherDeviceKeysCard.setVisibility(View.GONE);
+ this.binding.verificationBox.setVisibility(View.GONE);
}
}
diff --git a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
index 21d1e9f0f116c962a7acdb11f580f08b0f959ba5..fe16d7aba5b8f1eaea840a7b3ac8d6cc0bf8de1d 100644
--- a/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
+++ b/src/main/java/eu/siacs/conversations/ui/adapter/AccountAdapter.java
@@ -69,6 +69,11 @@ public class AccountAdapter extends ArrayAdapter {
viewHolder.binding.accountStatus.setTextColor(StyledAttributes.getColor(activity, R.attr.TextColorError));
break;
}
+ if (account.getXmppConnection() != null && account.getXmppConnection().resolverAuthenticated()) {
+ viewHolder.binding.verificationIndicator.setImageResource(R.drawable.shield);
+ } else {
+ viewHolder.binding.verificationIndicator.setImageResource(R.drawable.shield_question);
+ }
final boolean isDisabled = (account.getStatus() == Account.State.DISABLED);
viewHolder.binding.tglAccountStatus.setOnCheckedChangeListener(null);
viewHolder.binding.tglAccountStatus.setChecked(!isDisabled);
diff --git a/src/main/java/eu/siacs/conversations/utils/AndroidUsingExecLowPriority.java b/src/main/java/eu/siacs/conversations/utils/AndroidUsingExecLowPriority.java
index d8c46b08f7ae2b1b3b71bd2abd0a58752fb58719..8f31f2eecc39e7fb7badfdda6d1b9607e07afb48 100644
--- a/src/main/java/eu/siacs/conversations/utils/AndroidUsingExecLowPriority.java
+++ b/src/main/java/eu/siacs/conversations/utils/AndroidUsingExecLowPriority.java
@@ -19,17 +19,20 @@ import java.net.InetAddress;
import java.util.HashSet;
import java.util.logging.Level;
-import de.measite.minidns.dnsserverlookup.AbstractDNSServerLookupMechanism;
-import de.measite.minidns.dnsserverlookup.AndroidUsingReflection;
-import de.measite.minidns.dnsserverlookup.DNSServerLookupMechanism;
-import de.measite.minidns.util.PlatformDetection;
+import java.util.ArrayList;
+import java.util.List;
+
+import org.minidns.dnsserverlookup.AbstractDnsServerLookupMechanism;
+import org.minidns.dnsserverlookup.AndroidUsingReflection;
+import org.minidns.dnsserverlookup.DnsServerLookupMechanism;
+import org.minidns.util.PlatformDetection;
/**
* Try to retrieve the list of DNS server by executing getprop.
*/
-public class AndroidUsingExecLowPriority extends AbstractDNSServerLookupMechanism {
+public class AndroidUsingExecLowPriority extends AbstractDnsServerLookupMechanism {
- public static final DNSServerLookupMechanism INSTANCE = new AndroidUsingExecLowPriority();
+ public static final DnsServerLookupMechanism INSTANCE = new AndroidUsingExecLowPriority();
public static final int PRIORITY = AndroidUsingReflection.PRIORITY + 1;
private AndroidUsingExecLowPriority() {
@@ -37,7 +40,7 @@ public class AndroidUsingExecLowPriority extends AbstractDNSServerLookupMechanis
}
@Override
- public String[] getDnsServerAddresses() {
+ public List getDnsServerAddresses() {
try {
Process process = Runtime.getRuntime().exec("getprop");
InputStream inputStream = process.getInputStream();
@@ -76,7 +79,7 @@ public class AndroidUsingExecLowPriority extends AbstractDNSServerLookupMechanis
}
}
if (server.size() > 0) {
- return server.toArray(new String[server.size()]);
+ return new ArrayList<>(server);
}
} catch (IOException e) {
LOGGER.log(Level.WARNING, "Exception in findDNSByExec", e);
diff --git a/src/main/java/eu/siacs/conversations/utils/AndroidUsingLinkProperties.java b/src/main/java/eu/siacs/conversations/utils/AndroidUsingLinkProperties.java
index 658e7abcd7e8d1046fc51b02a3944e48aacf265f..e3bd5e7af59a1728aa04625e2274ea0ada0c3dbb 100644
--- a/src/main/java/eu/siacs/conversations/utils/AndroidUsingLinkProperties.java
+++ b/src/main/java/eu/siacs/conversations/utils/AndroidUsingLinkProperties.java
@@ -14,10 +14,10 @@ import java.net.InetAddress;
import java.util.ArrayList;
import java.util.List;
-import de.measite.minidns.dnsserverlookup.AbstractDNSServerLookupMechanism;
-import de.measite.minidns.dnsserverlookup.AndroidUsingExec;
+import org.minidns.dnsserverlookup.AbstractDnsServerLookupMechanism;
+import org.minidns.dnsserverlookup.AndroidUsingExec;
-public class AndroidUsingLinkProperties extends AbstractDNSServerLookupMechanism {
+public class AndroidUsingLinkProperties extends AbstractDnsServerLookupMechanism {
private final Context context;
@@ -33,11 +33,11 @@ public class AndroidUsingLinkProperties extends AbstractDNSServerLookupMechanism
@Override
@TargetApi(21)
- public String[] getDnsServerAddresses() {
+ public List getDnsServerAddresses() {
final ConnectivityManager connectivityManager = (ConnectivityManager) context.getSystemService(Context.CONNECTIVITY_SERVICE);
final Network[] networks = connectivityManager == null ? null : connectivityManager.getAllNetworks();
if (networks == null) {
- return new String[0];
+ return new ArrayList<>();
}
final Network activeNetwork = getActiveNetwork(connectivityManager);
final List servers = new ArrayList<>();
@@ -58,7 +58,7 @@ public class AndroidUsingLinkProperties extends AbstractDNSServerLookupMechanism
servers.addAll(vpnOffset, getIPv4First(linkProperties.getDnsServers()));
}
}
- return servers.toArray(new String[0]);
+ return servers;
}
@TargetApi(23)
diff --git a/src/main/java/eu/siacs/conversations/utils/Resolver.java b/src/main/java/eu/siacs/conversations/utils/Resolver.java
index 915209413e51b77b85c7c8f9f2492087dce0f747..4193f84e3079917b688dfd38b39c0d663bf9a6c2 100644
--- a/src/main/java/eu/siacs/conversations/utils/Resolver.java
+++ b/src/main/java/eu/siacs/conversations/utils/Resolver.java
@@ -20,26 +20,26 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.List;
-import de.gultsch.minidns.AndroidDNSClient;
-import de.measite.minidns.AbstractDNSClient;
-import de.measite.minidns.DNSCache;
-import de.measite.minidns.DNSClient;
-import de.measite.minidns.DNSName;
-import de.measite.minidns.Question;
-import de.measite.minidns.Record;
-import de.measite.minidns.cache.LRUCache;
-import de.measite.minidns.dnssec.DNSSECResultNotAuthenticException;
-import de.measite.minidns.dnsserverlookup.AndroidUsingExec;
-import de.measite.minidns.hla.DnssecResolverApi;
-import de.measite.minidns.hla.ResolverApi;
-import de.measite.minidns.hla.ResolverResult;
-import de.measite.minidns.iterative.ReliableDNSClient;
-import de.measite.minidns.record.A;
-import de.measite.minidns.record.AAAA;
-import de.measite.minidns.record.CNAME;
-import de.measite.minidns.record.Data;
-import de.measite.minidns.record.InternetAddressRR;
-import de.measite.minidns.record.SRV;
+//import de.gultsch.minidns.AndroidDNSClient;
+import org.minidns.AbstractDnsClient;
+import org.minidns.DnsCache;
+import org.minidns.DnsClient;
+import org.minidns.dnsname.DnsName;
+import org.minidns.dnsmessage.Question;
+import org.minidns.record.Record;
+import org.minidns.cache.LruCache;
+import org.minidns.dnssec.DnssecResultNotAuthenticException;
+import org.minidns.dnsserverlookup.AndroidUsingExec;
+import org.minidns.hla.DnssecResolverApi;
+import org.minidns.hla.ResolverApi;
+import org.minidns.hla.ResolverResult;
+import org.minidns.iterative.ReliableDnsClient;
+import org.minidns.record.A;
+import org.minidns.record.AAAA;
+import org.minidns.record.CNAME;
+import org.minidns.record.Data;
+import org.minidns.record.InternetAddressRR;
+import org.minidns.record.SRV;
import eu.siacs.conversations.Config;
import eu.siacs.conversations.R;
import eu.siacs.conversations.services.XmppConnectionService;
@@ -57,34 +57,18 @@ public class Resolver {
public static void init(XmppConnectionService service) {
Resolver.SERVICE = service;
- DNSClient.removeDNSServerLookupMechanism(AndroidUsingExec.INSTANCE);
- DNSClient.addDnsServerLookupMechanism(AndroidUsingExecLowPriority.INSTANCE);
- DNSClient.addDnsServerLookupMechanism(new AndroidUsingLinkProperties(service));
- final AbstractDNSClient client = ResolverApi.INSTANCE.getClient();
- if (client instanceof ReliableDNSClient) {
- disableHardcodedDnsServers((ReliableDNSClient) client);
- }
- }
-
- private static void disableHardcodedDnsServers(ReliableDNSClient reliableDNSClient) {
- try {
- final Field dnsClientField = ReliableDNSClient.class.getDeclaredField("dnsClient");
- dnsClientField.setAccessible(true);
- final DNSClient dnsClient = (DNSClient) dnsClientField.get(reliableDNSClient);
- if (dnsClient != null) {
- dnsClient.getDataSource().setTimeout(3000);
- }
- final Field useHardcodedDnsServers = DNSClient.class.getDeclaredField("useHardcodedDnsServers");
- useHardcodedDnsServers.setAccessible(true);
- useHardcodedDnsServers.setBoolean(dnsClient, false);
- } catch (NoSuchFieldException | IllegalAccessException e) {
- Log.e(Config.LOGTAG, "Unable to disable hardcoded DNS servers", e);
+ DnsClient.removeDNSServerLookupMechanism(AndroidUsingExec.INSTANCE);
+ DnsClient.addDnsServerLookupMechanism(AndroidUsingExecLowPriority.INSTANCE);
+ DnsClient.addDnsServerLookupMechanism(new AndroidUsingLinkProperties(service));
+ final AbstractDnsClient client = ResolverApi.INSTANCE.getClient();
+ if (client instanceof ReliableDnsClient) {
+ ((ReliableDnsClient) client).setUseHardcodedDnsServers(false);
}
}
public static List fromHardCoded(final String hostname, final int port) {
final Result result = new Result();
- result.hostname = DNSName.from(hostname);
+ result.hostname = DnsName.from(hostname);
result.port = port;
result.directTls = useDirectTls(port);
result.authenticated = true;
@@ -92,12 +76,12 @@ public class Resolver {
}
public static void checkDomain(final Jid jid) {
- DNSName.from(jid.getDomain());
+ DnsName.from(jid.getDomain());
}
public static boolean invalidHostname(final String hostname) {
try {
- DNSName.from(hostname);
+ DnsName.from(hostname);
return false;
} catch (IllegalArgumentException e) {
return true;
@@ -105,11 +89,11 @@ public class Resolver {
}
public static void clearCache() {
- final AbstractDNSClient client = ResolverApi.INSTANCE.getClient();
- final DNSCache dnsCache = client.getCache();
- if (dnsCache instanceof LRUCache) {
+ final AbstractDnsClient client = ResolverApi.INSTANCE.getClient();
+ final DnsCache dnsCache = client.getCache();
+ if (dnsCache instanceof LruCache) {
Log.d(Config.LOGTAG,"clearing DNS cache");
- ((LRUCache) dnsCache).clear();
+ ((LruCache) dnsCache).clear();
}
}
@@ -151,7 +135,7 @@ public class Resolver {
}
});
threads[2] = new Thread(() -> {
- List list = resolveNoSrvRecords(DNSName.from(domain), true);
+ List list = resolveNoSrvRecords(DnsName.from(domain), true);
synchronized (fallbackResults) {
fallbackResults.addAll(list);
}
@@ -193,6 +177,7 @@ public class Resolver {
Result result = new Result();
result.ip = InetAddress.getByName(domain);
result.port = DEFAULT_PORT_XMPP;
+ result.authenticated = true;
return Collections.singletonList(result);
} catch (UnknownHostException e) {
return Collections.emptyList();
@@ -200,7 +185,7 @@ public class Resolver {
}
private static List resolveSrv(String domain, final boolean directTls) throws IOException {
- DNSName dnsName = DNSName.from((directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERVICE) + "._tcp." + domain);
+ DnsName dnsName = DnsName.from((directTls ? DIRECT_TLS_SERVICE : STARTTLS_SERVICE) + "._tcp." + domain);
ResolverResult result = resolveWithFallback(dnsName, SRV.class);
final List results = new ArrayList<>();
final List threads = new ArrayList<>();
@@ -243,10 +228,10 @@ public class Resolver {
private static List resolveIp(SRV srv, Class type, boolean authenticated, boolean directTls) {
List list = new ArrayList<>();
try {
- ResolverResult results = resolveWithFallback(srv.name, type, authenticated);
+ ResolverResult results = resolveWithFallback(srv.name, type);
for (D record : results.getAnswersOrEmptySet()) {
Result resolverResult = Result.fromRecord(srv, directTls);
- resolverResult.authenticated = results.isAuthenticData() && authenticated; //TODO technically it doesn’t matter if the IP was authenticated
+ resolverResult.authenticated = results.isAuthenticData() && authenticated;
resolverResult.ip = record.getInetAddress();
list.add(resolverResult);
}
@@ -256,18 +241,29 @@ public class Resolver {
return list;
}
- private static List resolveNoSrvRecords(DNSName dnsName, boolean withCnames) {
+ private static List resolveNoSrvRecords(DnsName dnsName, boolean withCnames) {
List results = new ArrayList<>();
try {
- for (A a : resolveWithFallback(dnsName, A.class, false).getAnswersOrEmptySet()) {
- results.add(Result.createDefault(dnsName, a.getInetAddress()));
+ ResolverResult aResult = resolveWithFallback(dnsName, A.class);
+ Log.d("WUTr", "" + aResult.isAuthenticData() + " " + aResult.getAnswersOrEmptySet());
+ for (A a : aResult.getAnswersOrEmptySet()) {
+ Result r = Result.createDefault(dnsName, a.getInetAddress());
+ r.authenticated = aResult.isAuthenticData();
+ results.add(r);
}
- for (AAAA aaaa : resolveWithFallback(dnsName, AAAA.class, false).getAnswersOrEmptySet()) {
- results.add(Result.createDefault(dnsName, aaaa.getInetAddress()));
+ ResolverResult aaaaResult = resolveWithFallback(dnsName, AAAA.class);
+ for (AAAA aaaa : aaaaResult.getAnswersOrEmptySet()) {
+ Result r = Result.createDefault(dnsName, aaaa.getInetAddress());
+ r.authenticated = aaaaResult.isAuthenticData();
+ results.add(r);
}
if (results.size() == 0 && withCnames) {
- for (CNAME cname : resolveWithFallback(dnsName, CNAME.class, false).getAnswersOrEmptySet()) {
- results.addAll(resolveNoSrvRecords(cname.name, false));
+ ResolverResult cnameResult = resolveWithFallback(dnsName, CNAME.class);
+ for (CNAME cname : cnameResult.getAnswersOrEmptySet()) {
+ for (Result r : resolveNoSrvRecords(cname.name, false)) {
+ r.authenticated = r.authenticated && cnameResult.isAuthenticData();
+ results.add(r);
+ }
}
}
} catch (final Throwable throwable) {
@@ -279,21 +275,10 @@ public class Resolver {
return results;
}
- private static ResolverResult resolveWithFallback(DNSName dnsName, Class type) throws IOException {
- return resolveWithFallback(dnsName, type, validateHostname());
- }
-
- private static ResolverResult resolveWithFallback(DNSName dnsName, Class type, boolean validateHostname) throws IOException {
+ private static ResolverResult resolveWithFallback(DnsName dnsName, Class type) throws IOException {
final Question question = new Question(dnsName, Record.TYPE.getType(type));
- if (!validateHostname) {
- final AndroidDNSClient androidDNSClient = new AndroidDNSClient(SERVICE);
- final ResolverApi resolverApi = new ResolverApi(androidDNSClient);
- return resolverApi.resolve(question);
- }
try {
- return DnssecResolverApi.INSTANCE.resolveDnssecReliable(question);
- } catch (DNSSECResultNotAuthenticException e) {
- Log.d(Config.LOGTAG, Resolver.class.getSimpleName() + ": error resolving " + type.getSimpleName() + " with DNSSEC. Trying DNS instead.", e);
+ return DnssecResolverApi.INSTANCE.resolve(question);
} catch (IOException e) {
throw e;
} catch (Throwable throwable) {
@@ -302,10 +287,6 @@ public class Resolver {
return ResolverApi.INSTANCE.resolve(question);
}
- private static boolean validateHostname() {
- return SERVICE != null && SERVICE.getBooleanPreference("validate_hostname", R.bool.validate_hostname);
- }
-
public static class Result implements Comparable {
public static final String DOMAIN = "domain";
public static final String IP = "ip";
@@ -315,7 +296,7 @@ public class Resolver {
public static final String DIRECT_TLS = "directTls";
public static final String AUTHENTICATED = "authenticated";
private InetAddress ip;
- private DNSName hostname;
+ private DnsName hostname;
private int port = DEFAULT_PORT_XMPP;
private boolean directTls = false;
private boolean authenticated = false;
@@ -330,7 +311,7 @@ public class Resolver {
return result;
}
- static Result createDefault(DNSName hostname, InetAddress ip) {
+ static Result createDefault(DnsName hostname, InetAddress ip) {
Result result = new Result();
result.port = DEFAULT_PORT_XMPP;
result.hostname = hostname;
@@ -338,7 +319,7 @@ public class Resolver {
return result;
}
- static Result createDefault(DNSName hostname) {
+ static Result createDefault(DnsName hostname) {
return createDefault(hostname, null);
}
@@ -350,7 +331,7 @@ public class Resolver {
result.ip = null;
}
final String hostname = cursor.getString(cursor.getColumnIndex(HOSTNAME));
- result.hostname = hostname == null ? null : DNSName.from(hostname);
+ result.hostname = hostname == null ? null : DnsName.from(hostname);
result.port = cursor.getInt(cursor.getColumnIndex(PORT));
result.priority = cursor.getInt(cursor.getColumnIndex(PRIORITY));
result.authenticated = cursor.getInt(cursor.getColumnIndex(AUTHENTICATED)) > 0;
@@ -392,7 +373,7 @@ public class Resolver {
return port;
}
- public DNSName getHostname() {
+ public DnsName getHostname() {
return hostname;
}
@@ -482,7 +463,7 @@ public class Resolver {
return null;
}
try {
- result.hostname = DNSName.from(hostPart.trim());
+ result.hostname = DnsName.from(hostPart.trim());
} catch (final Exception e) {
return null;
}
@@ -499,7 +480,7 @@ public class Resolver {
result.ip = inetAddress;
} else {
try {
- result.hostname = DNSName.from(hostname);
+ result.hostname = DnsName.from(hostname);
} catch (final Exception e) {
return null;
}
diff --git a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
index 4a4f9ccce822217b385f84afaab0d0185a497bad..4b8cec64cef1b812119033c9854ecbafa449d721 100644
--- a/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
+++ b/src/main/java/eu/siacs/conversations/xmpp/XmppConnection.java
@@ -225,6 +225,12 @@ public class XmppConnection implements Runnable {
}
}
+
+ public boolean resolverAuthenticated() {
+ if (currentResolverResult == null) return false;
+ return currentResolverResult.isAuthenticated();
+ }
+
private void changeStatus(final Account.State nextStatus) {
synchronized (this) {
if (Thread.currentThread().isInterrupted()) {
diff --git a/src/main/res/layout/account_row.xml b/src/main/res/layout/account_row.xml
index bbc09b028e75ad1f6ec02f287f4e7110e271ff92..98a897f66dca1278b3ae79fa1161d31f1a0ba600 100644
--- a/src/main/res/layout/account_row.xml
+++ b/src/main/res/layout/account_row.xml
@@ -43,12 +43,29 @@
android:singleLine="true"
android:textAppearance="@style/TextAppearance.Conversations.Subhead" />
-
+ android:layout_centerVertical="true"
+ android:orientation="horizontal">
+
+
+
+
+ >
+
+
+
+
+
+
+
+
+
+
-