diff --git a/build.gradle b/build.gradle index 7cf46718..01eafc65 100644 --- a/build.gradle +++ b/build.gradle @@ -24,6 +24,18 @@ if (isSnapshot) { 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 { manifest { instruction 'Implementation-GitRevision:', project.ext.gitCommit diff --git a/src/main/java/de/measite/minidns/Client.java b/src/main/java/de/measite/minidns/Client.java index cad3902a..26e3ba01 100644 --- a/src/main/java/de/measite/minidns/Client.java +++ b/src/main/java/de/measite/minidns/Client.java @@ -17,6 +17,8 @@ import java.util.Random; import java.util.logging.Level; import java.util.logging.Logger; +import org.jxmpp.util.cache.ExpirationCache; + import de.measite.minidns.Record.CLASS; import de.measite.minidns.Record.TYPE; @@ -28,6 +30,9 @@ public class Client { private static final Logger LOGGER = Logger.getLogger(Client.class.getName()); + protected static final ExpirationCache cache = new ExpirationCache( + 10, 1000 * 60 * 60 * 24); + /** * 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) throws IOException { - Question q = new Question(); - q.setClazz(clazz); - q.setType(type); - q.setName(name); + Question q = new Question(name, type, clazz); return query(q, host, port); } @@ -86,10 +88,7 @@ public class Client { public DNSMessage query(String name, TYPE type, CLASS clazz, String host) throws IOException { - Question q = new Question(); - q.setClazz(clazz); - q.setType(type); - q.setName(name); + Question q = new Question(name, type, clazz); return query(q, host); } @@ -102,10 +101,7 @@ public class Client { */ public DNSMessage query(String name, TYPE type, CLASS clazz) { - Question q = new Question(); - q.setClazz(clazz); - q.setType(type); - q.setName(name); + Question q = new Question(name, type, clazz); return query(q); } @@ -127,6 +123,10 @@ public class Client { * @throws IOException On IOErrors. */ 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(); message.setQuestions(new Question[]{q}); message.setRecursionDesired(true); @@ -139,10 +139,16 @@ public class Client { socket.send(packet); packet = new DatagramPacket(new byte[bufferSize], bufferSize); socket.receive(packet); - DNSMessage dnsMessage = DNSMessage.parse(packet.getData()); + dnsMessage = DNSMessage.parse(packet.getData()); if (dnsMessage.getId() != message.getId()) { return null; } + for (Record record : dnsMessage.getAnswers()) { + if (record.isAnswer(q)) { + cache.put(q, dnsMessage, record.ttl); + break; + } + } return dnsMessage; } } @@ -152,10 +158,19 @@ public class Client { * @param q The question section of the DNS query. */ 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(); for (String dns : dnsServer) { try { - DNSMessage message = query(q, dns); + message = query(q, dns); if (message == null) { continue; } diff --git a/src/main/java/de/measite/minidns/DNSMessage.java b/src/main/java/de/measite/minidns/DNSMessage.java index 14c8f04b..ea1b25c8 100644 --- a/src/main/java/de/measite/minidns/DNSMessage.java +++ b/src/main/java/de/measite/minidns/DNSMessage.java @@ -416,8 +416,7 @@ public class DNSMessage { int additionalResourceRecordCount = dis.readUnsignedShort(); message.questions = new Question[questionCount]; while (questionCount-- > 0) { - Question q = new Question(); - q.parse(dis, data); + Question q = Question.parse(dis, data); message.questions[questionCount] = q; } message.answers = new Record[answerCount]; diff --git a/src/main/java/de/measite/minidns/Question.java b/src/main/java/de/measite/minidns/Question.java index 9d1e3f56..8efe19f1 100644 --- a/src/main/java/de/measite/minidns/Question.java +++ b/src/main/java/de/measite/minidns/Question.java @@ -11,52 +11,71 @@ import de.measite.minidns.util.NameUtil; 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() { return type; } - public void setType(TYPE type) { - this.type = type; - } - public CLASS getClazz() { return clazz; } - public void setClazz(CLASS clazz) { - this.clazz = clazz; - } - public String getName() { return name; } - public void setName(String name) { - this.name = name; + public static Question parse(DataInputStream dis, byte[] data) throws IOException { + 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 { - this.name = NameUtil.parse(dis, data); - this.type = TYPE.getType(dis.readUnsignedShort()); - this.clazz = CLASS.getClass(dis.readUnsignedShort()); + public byte[] toByteArray() { + if (byteArray == null) { + ByteArrayOutputStream baos = new ByteArrayOutputStream(512); + DataOutputStream dos = new DataOutputStream(baos); + + try { + dos.write(NameUtil.toByteArray(this.name)); + dos.writeShort(type.getValue()); + dos.writeShort(clazz.getValue()); + dos.flush(); + } catch (IOException e) { + // Should never happen + throw new IllegalStateException(e); + } + byteArray = baos.toByteArray(); + } + return byteArray; } - public byte[] toByteArray() throws IOException { - ByteArrayOutputStream baos = new ByteArrayOutputStream(512); - DataOutputStream dos = new DataOutputStream(baos); - - dos.write(NameUtil.toByteArray(this.name)); - dos.writeShort(type.getValue()); - dos.writeShort(clazz.getValue()); - - dos.flush(); - return baos.toByteArray(); + @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(); + } }