package com.koushikdutta.async.dns;
import com.koushikdutta.async.AsyncDatagramSocket;
import com.koushikdutta.async.AsyncServer;
import com.koushikdutta.async.ByteBufferList;
import com.koushikdutta.async.DataEmitter;
import com.koushikdutta.async.callback.DataCallback;
import com.koushikdutta.async.future.Cancellable;
import com.koushikdutta.async.future.Future;
import com.koushikdutta.async.future.FutureCallback;
import com.koushikdutta.async.future.SimpleFuture;
import java.lang.reflect.Field;
import java.lang.reflect.Method;
import java.net.DatagramSocket;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.util.Random;
/**
* Created by koush on 10/20/13.
*/
public class Dns {
public static Future<DnsResponse> lookup(String host) {
return lookup(AsyncServer.getDefault(), host, false, null);
}
private static int setFlag(int flags, int value, int offset) {
return flags | (value << offset);
}
private static int setQuery(int flags) {
return setFlag(flags, 0, 0);
}
private static int setRecursion(int flags) {
return setFlag(flags, 1, 8);
}
private static void addName(ByteBuffer bb, String name) {
String[] parts = name.split("\\.");
for (String part: parts) {
bb.put((byte)part.length());
bb.put(part.getBytes());
}
bb.put((byte)0);
}
public static Future<DnsResponse> lookup(AsyncServer server, String host) {
return lookup(server, host, false, null);
}
public static Cancellable multicastLookup(AsyncServer server, String host, FutureCallback<DnsResponse> callback) {
return lookup(server, host, true, callback);
}
public static Cancellable multicastLookup(String host, FutureCallback<DnsResponse> callback) {
return multicastLookup(AsyncServer.getDefault(), host, callback);
}
public static Future<DnsResponse> lookup(AsyncServer server, String host, final boolean multicast, final FutureCallback<DnsResponse> callback) {
ByteBuffer packet = ByteBufferList.obtain(1024).order(ByteOrder.BIG_ENDIAN);
short id = (short)new Random().nextInt();
short flags = (short)setQuery(0);
if (!multicast)
flags = (short)setRecursion(flags);
packet.putShort(id);
packet.putShort(flags);
// number questions
packet.putShort(multicast ? (short)1 : (short)2);
// number answer rr
packet.putShort((short)0);
// number authority rr
packet.putShort((short)0);
// number additional rr
packet.putShort((short)0);
addName(packet, host);
// query
packet.putShort(multicast ? (short)12 : (short)1);
// request internet address
packet.putShort((short)1);
if (!multicast) {
addName(packet, host);
// AAAA query
packet.putShort((short) 28);
// request internet address
packet.putShort((short)1);
}
packet.flip();
try {
final AsyncDatagramSocket dgram;
// todo, use the dns server...
if (!multicast) {
dgram = server.connectDatagram(new InetSocketAddress("8.8.8.8", 53));
}
else {
// System.out.println("multicast dns...");
dgram = AsyncServer.getDefault().openDatagram(new InetSocketAddress(0), true);
Field field = DatagramSocket.class.getDeclaredField("impl");
field.setAccessible(true);
Object impl = field.get(dgram.getSocket());
Method method = impl.getClass().getDeclaredMethod("join", InetAddress.class);
method.setAccessible(true);
method.invoke(impl, InetAddress.getByName("224.0.0.251"));
((DatagramSocket)dgram.getSocket()).setBroadcast(true);
}
final SimpleFuture<DnsResponse> ret = new SimpleFuture<DnsResponse>() {
@Override
protected void cleanup() {
super.cleanup();
// System.out.println("multicast dns cleanup...");
dgram.close();
}
};
dgram.setDataCallback(new DataCallback() {
@Override
public void onDataAvailable(DataEmitter emitter, ByteBufferList bb) {
try {
// System.out.println(dgram.getRemoteAddress());
DnsResponse response = DnsResponse.parse(bb);
// System.out.println(response);
response.source = dgram.getRemoteAddress();
if (!multicast) {
dgram.close();
ret.setComplete(response);
}
else {
callback.onCompleted(null, response);
}
}
catch (Exception e) {
}
bb.recycle();
}
});
if (!multicast)
dgram.write(new ByteBufferList(packet));
else
dgram.send(new InetSocketAddress("224.0.0.251", 5353), packet);
return ret;
}
catch (Exception e) {
SimpleFuture<DnsResponse> ret = new SimpleFuture<DnsResponse>();
ret.setComplete(e);
if (multicast)
callback.onCompleted(e, null);
return ret;
}
}
}