Add Cache to minidns

This commit is contained in:
Florian Schmaus 2014-06-12 09:29:35 +02:00
parent f66c0db63f
commit 7dd8cfc6e6
4 changed files with 88 additions and 43 deletions

View file

@ -24,6 +24,18 @@ if (isSnapshot) {
version += '-SNAPSHOT' version += '-SNAPSHOT'
} }
repositories {
mavenLocal()
mavenCentral()
maven {
url 'https://oss.sonatype.org/content/repositories/snapshots/'
}
}
dependencies {
compile 'org.igniterealtime.jxmpp:jxmpp-util-cache:0.1.0-alpha1-SNAPSHOT'
}
jar { jar {
manifest { manifest {
instruction 'Implementation-GitRevision:', project.ext.gitCommit instruction 'Implementation-GitRevision:', project.ext.gitCommit

View file

@ -17,6 +17,8 @@ import java.util.Random;
import java.util.logging.Level; import java.util.logging.Level;
import java.util.logging.Logger; import java.util.logging.Logger;
import org.jxmpp.util.cache.ExpirationCache;
import de.measite.minidns.Record.CLASS; import de.measite.minidns.Record.CLASS;
import de.measite.minidns.Record.TYPE; import de.measite.minidns.Record.TYPE;
@ -28,6 +30,9 @@ public class Client {
private static final Logger LOGGER = Logger.getLogger(Client.class.getName()); private static final Logger LOGGER = Logger.getLogger(Client.class.getName());
protected static final ExpirationCache<Question, DNSMessage> cache = new ExpirationCache<Question, DNSMessage>(
10, 1000 * 60 * 60 * 24);
/** /**
* The internal random class for sequence generation. * The internal random class for sequence generation.
*/ */
@ -67,10 +72,7 @@ public class Client {
public DNSMessage query(String name, TYPE type, CLASS clazz, String host, int port) public DNSMessage query(String name, TYPE type, CLASS clazz, String host, int port)
throws IOException throws IOException
{ {
Question q = new Question(); Question q = new Question(name, type, clazz);
q.setClazz(clazz);
q.setType(type);
q.setName(name);
return query(q, host, port); return query(q, host, port);
} }
@ -86,10 +88,7 @@ public class Client {
public DNSMessage query(String name, TYPE type, CLASS clazz, String host) public DNSMessage query(String name, TYPE type, CLASS clazz, String host)
throws IOException throws IOException
{ {
Question q = new Question(); Question q = new Question(name, type, clazz);
q.setClazz(clazz);
q.setType(type);
q.setName(name);
return query(q, host); return query(q, host);
} }
@ -102,10 +101,7 @@ public class Client {
*/ */
public DNSMessage query(String name, TYPE type, CLASS clazz) public DNSMessage query(String name, TYPE type, CLASS clazz)
{ {
Question q = new Question(); Question q = new Question(name, type, clazz);
q.setClazz(clazz);
q.setType(type);
q.setName(name);
return query(q); return query(q);
} }
@ -127,6 +123,10 @@ public class Client {
* @throws IOException On IOErrors. * @throws IOException On IOErrors.
*/ */
public DNSMessage query(Question q, String host, int port) throws IOException { public DNSMessage query(Question q, String host, int port) throws IOException {
DNSMessage dnsMessage = cache.get(q);
if (dnsMessage != null) {
return dnsMessage;
}
DNSMessage message = new DNSMessage(); DNSMessage message = new DNSMessage();
message.setQuestions(new Question[]{q}); message.setQuestions(new Question[]{q});
message.setRecursionDesired(true); message.setRecursionDesired(true);
@ -139,10 +139,16 @@ public class Client {
socket.send(packet); socket.send(packet);
packet = new DatagramPacket(new byte[bufferSize], bufferSize); packet = new DatagramPacket(new byte[bufferSize], bufferSize);
socket.receive(packet); socket.receive(packet);
DNSMessage dnsMessage = DNSMessage.parse(packet.getData()); dnsMessage = DNSMessage.parse(packet.getData());
if (dnsMessage.getId() != message.getId()) { if (dnsMessage.getId() != message.getId()) {
return null; return null;
} }
for (Record record : dnsMessage.getAnswers()) {
if (record.isAnswer(q)) {
cache.put(q, dnsMessage, record.ttl);
break;
}
}
return dnsMessage; return dnsMessage;
} }
} }
@ -152,10 +158,19 @@ public class Client {
* @param q The question section of the DNS query. * @param q The question section of the DNS query.
*/ */
public DNSMessage query(Question q) { public DNSMessage query(Question q) {
// While this query method does in fact re-use query(Question, String)
// we still do a cache lookup here in order to avoid unnecessary
// findDNS()calls, which are expensive on Android. Note that we do not
// put the results back into the Cache, as this is already done by
// query(Question, String).
DNSMessage message = cache.get(q);
if (message != null) {
return message;
}
String dnsServer[] = findDNS(); String dnsServer[] = findDNS();
for (String dns : dnsServer) { for (String dns : dnsServer) {
try { try {
DNSMessage message = query(q, dns); message = query(q, dns);
if (message == null) { if (message == null) {
continue; continue;
} }

View file

@ -416,8 +416,7 @@ public class DNSMessage {
int additionalResourceRecordCount = dis.readUnsignedShort(); int additionalResourceRecordCount = dis.readUnsignedShort();
message.questions = new Question[questionCount]; message.questions = new Question[questionCount];
while (questionCount-- > 0) { while (questionCount-- > 0) {
Question q = new Question(); Question q = Question.parse(dis, data);
q.parse(dis, data);
message.questions[questionCount] = q; message.questions[questionCount] = q;
} }
message.answers = new Record[answerCount]; message.answers = new Record[answerCount];

View file

@ -11,52 +11,71 @@ import de.measite.minidns.util.NameUtil;
public class Question { public class Question {
private String name; private final String name;
private TYPE type; private final TYPE type;
private CLASS clazz = CLASS.IN; private final CLASS clazz;
private byte[] byteArray;
public Question(String name, TYPE type, CLASS clazz) {
this.name = name;
this.type = type;
this.clazz = clazz;
}
public TYPE getType() { public TYPE getType() {
return type; return type;
} }
public void setType(TYPE type) {
this.type = type;
}
public CLASS getClazz() { public CLASS getClazz() {
return clazz; return clazz;
} }
public void setClazz(CLASS clazz) {
this.clazz = clazz;
}
public String getName() { public String getName() {
return name; return name;
} }
public void setName(String name) { public static Question parse(DataInputStream dis, byte[] data) throws IOException {
this.name = name; String name = NameUtil.parse(dis, data);
TYPE type = TYPE.getType(dis.readUnsignedShort());
CLASS clazz = CLASS.getClass(dis.readUnsignedShort());
return new Question (name, type, clazz);
} }
public void parse(DataInputStream dis, byte[] data) throws IOException { public byte[] toByteArray() {
this.name = NameUtil.parse(dis, data); if (byteArray == null) {
this.type = TYPE.getType(dis.readUnsignedShort());
this.clazz = CLASS.getClass(dis.readUnsignedShort());
}
public byte[] toByteArray() throws IOException {
ByteArrayOutputStream baos = new ByteArrayOutputStream(512); ByteArrayOutputStream baos = new ByteArrayOutputStream(512);
DataOutputStream dos = new DataOutputStream(baos); DataOutputStream dos = new DataOutputStream(baos);
try {
dos.write(NameUtil.toByteArray(this.name)); dos.write(NameUtil.toByteArray(this.name));
dos.writeShort(type.getValue()); dos.writeShort(type.getValue());
dos.writeShort(clazz.getValue()); dos.writeShort(clazz.getValue());
dos.flush(); dos.flush();
return baos.toByteArray(); } catch (IOException e) {
// Should never happen
throw new IllegalStateException(e);
}
byteArray = baos.toByteArray();
}
return byteArray;
} }
@Override
public int hashCode() {
return toByteArray().hashCode();
}
@Override
public boolean equals(Object other) {
if (this == other) {
return true;
}
if (!(other instanceof Question)) {
return false;
}
return this.hashCode() == other.hashCode();
}
} }