keep cache of DNS messages
This commit is contained in:
parent
55a77c083f
commit
97acd16422
|
@ -5,18 +5,30 @@ import android.net.ConnectivityManager;
|
||||||
import android.net.LinkProperties;
|
import android.net.LinkProperties;
|
||||||
import android.net.Network;
|
import android.net.Network;
|
||||||
import android.os.Build;
|
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.base.Strings;
|
||||||
|
import com.google.common.collect.Collections2;
|
||||||
import com.google.common.collect.ImmutableList;
|
import com.google.common.collect.ImmutableList;
|
||||||
|
|
||||||
import de.measite.minidns.AbstractDNSClient;
|
import de.measite.minidns.AbstractDNSClient;
|
||||||
import de.measite.minidns.DNSMessage;
|
import de.measite.minidns.DNSMessage;
|
||||||
|
|
||||||
|
import eu.siacs.conversations.Config;
|
||||||
|
|
||||||
import java.io.IOException;
|
import java.io.IOException;
|
||||||
import java.net.InetAddress;
|
import java.net.InetAddress;
|
||||||
|
import java.time.Duration;
|
||||||
|
import java.util.Collections;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
public class AndroidDNSClient extends AbstractDNSClient {
|
public class AndroidDNSClient extends AbstractDNSClient {
|
||||||
|
|
||||||
|
private static final LruCache<QuestionServerTuple, DNSMessage> QUERY_CACHE =
|
||||||
|
new LruCache<>(1024);
|
||||||
private final Context context;
|
private final Context context;
|
||||||
private final NetworkDataSource networkDataSource = new NetworkDataSource();
|
private final NetworkDataSource networkDataSource = new NetworkDataSource();
|
||||||
private boolean askForDnssec = false;
|
private boolean askForDnssec = false;
|
||||||
|
@ -56,6 +68,8 @@ public class AndroidDNSClient extends AbstractDNSClient {
|
||||||
protected DNSMessage query(final DNSMessage.Builder queryBuilder) throws IOException {
|
protected DNSMessage query(final DNSMessage.Builder queryBuilder) throws IOException {
|
||||||
final DNSMessage question = newQuestion(queryBuilder).build();
|
final DNSMessage question = newQuestion(queryBuilder).build();
|
||||||
for (final DNSServer dnsServer : getDNSServers()) {
|
for (final DNSServer dnsServer : getDNSServers()) {
|
||||||
|
final QuestionServerTuple cacheKey = new QuestionServerTuple(dnsServer, question);
|
||||||
|
final DNSMessage cachedResponse = queryCache(cacheKey);
|
||||||
final DNSMessage response = this.networkDataSource.query(question, dnsServer);
|
final DNSMessage response = this.networkDataSource.query(question, dnsServer);
|
||||||
if (response == null) {
|
if (response == null) {
|
||||||
continue;
|
continue;
|
||||||
|
@ -67,7 +81,7 @@ public class AndroidDNSClient extends AbstractDNSClient {
|
||||||
default:
|
default:
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
cacheQuery(cacheKey, response);
|
||||||
return response;
|
return response;
|
||||||
}
|
}
|
||||||
return null;
|
return null;
|
||||||
|
@ -120,4 +134,68 @@ public class AndroidDNSClient extends AbstractDNSClient {
|
||||||
}
|
}
|
||||||
return connectivityManager.getAllNetworks();
|
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 expiresAt(final DNSMessage dnsMessage) {
|
||||||
|
return dnsMessage.receiveTimestamp
|
||||||
|
+ (Collections.min(Collections2.transform(dnsMessage.answerSection, d -> d.ttl))
|
||||||
|
* 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);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in a new issue