aboutsummaryrefslogtreecommitdiff
path: root/src/org/xbill/DNS/SimpleResolver.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/org/xbill/DNS/SimpleResolver.java')
-rw-r--r--src/org/xbill/DNS/SimpleResolver.java351
1 files changed, 351 insertions, 0 deletions
diff --git a/src/org/xbill/DNS/SimpleResolver.java b/src/org/xbill/DNS/SimpleResolver.java
new file mode 100644
index 0000000..7436133
--- /dev/null
+++ b/src/org/xbill/DNS/SimpleResolver.java
@@ -0,0 +1,351 @@
+// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
+
+package org.xbill.DNS;
+
+import java.util.*;
+import java.io.*;
+import java.net.*;
+
+/**
+ * An implementation of Resolver that sends one query to one server.
+ * SimpleResolver handles TCP retries, transaction security (TSIG), and
+ * EDNS 0.
+ * @see Resolver
+ * @see TSIG
+ * @see OPTRecord
+ *
+ * @author Brian Wellington
+ */
+
+
+public class SimpleResolver implements Resolver {
+
+/** The default port to send queries to */
+public static final int DEFAULT_PORT = 53;
+
+/** The default EDNS payload size */
+public static final int DEFAULT_EDNS_PAYLOADSIZE = 1280;
+
+private InetSocketAddress address;
+private InetSocketAddress localAddress;
+private boolean useTCP, ignoreTruncation;
+private OPTRecord queryOPT;
+private TSIG tsig;
+private long timeoutValue = 10 * 1000;
+
+private static final short DEFAULT_UDPSIZE = 512;
+
+private static String defaultResolver = "localhost";
+private static int uniqueID = 0;
+
+/**
+ * Creates a SimpleResolver that will query the specified host
+ * @exception UnknownHostException Failure occurred while finding the host
+ */
+public
+SimpleResolver(String hostname) throws UnknownHostException {
+ if (hostname == null) {
+ hostname = ResolverConfig.getCurrentConfig().server();
+ if (hostname == null)
+ hostname = defaultResolver;
+ }
+ InetAddress addr;
+ if (hostname.equals("0"))
+ addr = InetAddress.getLocalHost();
+ else
+ addr = InetAddress.getByName(hostname);
+ address = new InetSocketAddress(addr, DEFAULT_PORT);
+}
+
+/**
+ * Creates a SimpleResolver. The host to query is either found by using
+ * ResolverConfig, or the default host is used.
+ * @see ResolverConfig
+ * @exception UnknownHostException Failure occurred while finding the host
+ */
+public
+SimpleResolver() throws UnknownHostException {
+ this(null);
+}
+
+/**
+ * Gets the destination address associated with this SimpleResolver.
+ * Messages sent using this SimpleResolver will be sent to this address.
+ * @return The destination address associated with this SimpleResolver.
+ */
+InetSocketAddress
+getAddress() {
+ return address;
+}
+
+/** Sets the default host (initially localhost) to query */
+public static void
+setDefaultResolver(String hostname) {
+ defaultResolver = hostname;
+}
+
+public void
+setPort(int port) {
+ address = new InetSocketAddress(address.getAddress(), port);
+}
+
+/**
+ * Sets the address of the server to communicate with.
+ * @param addr The address of the DNS server
+ */
+public void
+setAddress(InetSocketAddress addr) {
+ address = addr;
+}
+
+/**
+ * Sets the address of the server to communicate with (on the default
+ * DNS port)
+ * @param addr The address of the DNS server
+ */
+public void
+setAddress(InetAddress addr) {
+ address = new InetSocketAddress(addr, address.getPort());
+}
+
+/**
+ * Sets the local address to bind to when sending messages.
+ * @param addr The local address to send messages from.
+ */
+public void
+setLocalAddress(InetSocketAddress addr) {
+ localAddress = addr;
+}
+
+/**
+ * Sets the local address to bind to when sending messages. A random port
+ * will be used.
+ * @param addr The local address to send messages from.
+ */
+public void
+setLocalAddress(InetAddress addr) {
+ localAddress = new InetSocketAddress(addr, 0);
+}
+
+public void
+setTCP(boolean flag) {
+ this.useTCP = flag;
+}
+
+public void
+setIgnoreTruncation(boolean flag) {
+ this.ignoreTruncation = flag;
+}
+
+public void
+setEDNS(int level, int payloadSize, int flags, List options) {
+ if (level != 0 && level != -1)
+ throw new IllegalArgumentException("invalid EDNS level - " +
+ "must be 0 or -1");
+ if (payloadSize == 0)
+ payloadSize = DEFAULT_EDNS_PAYLOADSIZE;
+ queryOPT = new OPTRecord(payloadSize, 0, level, flags, options);
+}
+
+public void
+setEDNS(int level) {
+ setEDNS(level, 0, 0, null);
+}
+
+public void
+setTSIGKey(TSIG key) {
+ tsig = key;
+}
+
+TSIG
+getTSIGKey() {
+ return tsig;
+}
+
+public void
+setTimeout(int secs, int msecs) {
+ timeoutValue = (long)secs * 1000 + msecs;
+}
+
+public void
+setTimeout(int secs) {
+ setTimeout(secs, 0);
+}
+
+long
+getTimeout() {
+ return timeoutValue;
+}
+
+private Message
+parseMessage(byte [] b) throws WireParseException {
+ try {
+ return (new Message(b));
+ }
+ catch (IOException e) {
+ if (Options.check("verbose"))
+ e.printStackTrace();
+ if (!(e instanceof WireParseException))
+ e = new WireParseException("Error parsing message");
+ throw (WireParseException) e;
+ }
+}
+
+private void
+verifyTSIG(Message query, Message response, byte [] b, TSIG tsig) {
+ if (tsig == null)
+ return;
+ int error = tsig.verify(response, b, query.getTSIG());
+ if (Options.check("verbose"))
+ System.err.println("TSIG verify: " + Rcode.TSIGstring(error));
+}
+
+private void
+applyEDNS(Message query) {
+ if (queryOPT == null || query.getOPT() != null)
+ return;
+ query.addRecord(queryOPT, Section.ADDITIONAL);
+}
+
+private int
+maxUDPSize(Message query) {
+ OPTRecord opt = query.getOPT();
+ if (opt == null)
+ return DEFAULT_UDPSIZE;
+ else
+ return opt.getPayloadSize();
+}
+
+/**
+ * Sends a message to a single server and waits for a response. No checking
+ * is done to ensure that the response is associated with the query.
+ * @param query The query to send.
+ * @return The response.
+ * @throws IOException An error occurred while sending or receiving.
+ */
+public Message
+send(Message query) throws IOException {
+ if (Options.check("verbose"))
+ System.err.println("Sending to " +
+ address.getAddress().getHostAddress() +
+ ":" + address.getPort());
+
+ if (query.getHeader().getOpcode() == Opcode.QUERY) {
+ Record question = query.getQuestion();
+ if (question != null && question.getType() == Type.AXFR)
+ return sendAXFR(query);
+ }
+
+ query = (Message) query.clone();
+ applyEDNS(query);
+ if (tsig != null)
+ tsig.apply(query, null);
+
+ byte [] out = query.toWire(Message.MAXLENGTH);
+ int udpSize = maxUDPSize(query);
+ boolean tcp = false;
+ long endTime = System.currentTimeMillis() + timeoutValue;
+ do {
+ byte [] in;
+
+ if (useTCP || out.length > udpSize)
+ tcp = true;
+ if (tcp)
+ in = TCPClient.sendrecv(localAddress, address, out,
+ endTime);
+ else
+ in = UDPClient.sendrecv(localAddress, address, out,
+ udpSize, endTime);
+
+ /*
+ * Check that the response is long enough.
+ */
+ if (in.length < Header.LENGTH) {
+ throw new WireParseException("invalid DNS header - " +
+ "too short");
+ }
+ /*
+ * Check that the response ID matches the query ID. We want
+ * to check this before actually parsing the message, so that
+ * if there's a malformed response that's not ours, it
+ * doesn't confuse us.
+ */
+ int id = ((in[0] & 0xFF) << 8) + (in[1] & 0xFF);
+ int qid = query.getHeader().getID();
+ if (id != qid) {
+ String error = "invalid message id: expected " + qid +
+ "; got id " + id;
+ if (tcp) {
+ throw new WireParseException(error);
+ } else {
+ if (Options.check("verbose")) {
+ System.err.println(error);
+ }
+ continue;
+ }
+ }
+ Message response = parseMessage(in);
+ verifyTSIG(query, response, in, tsig);
+ if (!tcp && !ignoreTruncation &&
+ response.getHeader().getFlag(Flags.TC))
+ {
+ tcp = true;
+ continue;
+ }
+ return response;
+ } while (true);
+}
+
+/**
+ * Asynchronously sends a message to a single server, registering a listener
+ * to receive a callback on success or exception. Multiple asynchronous
+ * lookups can be performed in parallel. Since the callback may be invoked
+ * before the function returns, external synchronization is necessary.
+ * @param query The query to send
+ * @param listener The object containing the callbacks.
+ * @return An identifier, which is also a parameter in the callback
+ */
+public Object
+sendAsync(final Message query, final ResolverListener listener) {
+ final Object id;
+ synchronized (this) {
+ id = new Integer(uniqueID++);
+ }
+ Record question = query.getQuestion();
+ String qname;
+ if (question != null)
+ qname = question.getName().toString();
+ else
+ qname = "(none)";
+ String name = this.getClass() + ": " + qname;
+ Thread thread = new ResolveThread(this, query, id, listener);
+ thread.setName(name);
+ thread.setDaemon(true);
+ thread.start();
+ return id;
+}
+
+private Message
+sendAXFR(Message query) throws IOException {
+ Name qname = query.getQuestion().getName();
+ ZoneTransferIn xfrin = ZoneTransferIn.newAXFR(qname, address, tsig);
+ xfrin.setTimeout((int)(getTimeout() / 1000));
+ xfrin.setLocalAddress(localAddress);
+ try {
+ xfrin.run();
+ }
+ catch (ZoneTransferException e) {
+ throw new WireParseException(e.getMessage());
+ }
+ List records = xfrin.getAXFR();
+ Message response = new Message(query.getHeader().getID());
+ response.getHeader().setFlag(Flags.AA);
+ response.getHeader().setFlag(Flags.QR);
+ response.addRecord(query.getQuestion(), Section.QUESTION);
+ Iterator it = records.iterator();
+ while (it.hasNext())
+ response.addRecord((Record)it.next(), Section.ANSWER);
+ return response;
+}
+
+}