
io.jsync.dns.impl.DefaultDnsClient Maven / Gradle / Ivy
Go to download
Show more of this group Show more artifacts with this name
Show all versions of jsync.io Show documentation
Show all versions of jsync.io Show documentation
jsync.io is a non-blocking, event-driven networking framework for Java
/*
* Copyright (c) 2011-2013 The original author or authors
* ------------------------------------------------------
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v1.0
* and Apache License v2.0 which accompanies this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v10.html
*
* The Apache License v2.0 is available at
* http://www.opensource.org/licenses/apache2.0.php
*
* You may elect to redistribute this code under either of these licenses.
*/
package io.jsync.dns.impl;
import io.jsync.AsyncResult;
import io.jsync.Handler;
import io.jsync.dns.*;
import io.jsync.dns.DnsResponseCode;
import io.jsync.dns.impl.netty.*;
import io.jsync.dns.impl.netty.decoder.RecordDecoderFactory;
import io.jsync.dns.impl.netty.decoder.record.MailExchangerRecord;
import io.jsync.dns.impl.netty.decoder.record.ServiceRecord;
import io.jsync.impl.AsyncInternal;
import io.jsync.impl.DefaultContext;
import io.jsync.impl.DefaultFutureResult;
import io.jsync.net.impl.PartialPooledByteBufAllocator;
import io.netty.bootstrap.Bootstrap;
import io.netty.channel.*;
import io.netty.channel.socket.DatagramChannel;
import io.netty.channel.socket.nio.NioDatagramChannel;
import java.net.*;
import java.util.*;
import java.util.concurrent.ThreadLocalRandom;
/**
* @author Norman Maurer
*/
public final class DefaultDnsClient implements DnsClient {
private static final char[] HEX_TABLE = "0123456789abcdef".toCharArray();
private final Bootstrap bootstrap;
private final List dnsServers;
private final DefaultContext actualCtx;
private final AsyncInternal async;
public DefaultDnsClient(AsyncInternal async, InetSocketAddress... dnsServers) {
if (dnsServers == null || dnsServers.length == 0) {
throw new IllegalArgumentException("Need at least one default DNS Server");
}
// use LinkedList as we will traverse it all the time
this.dnsServers = new LinkedList<>(Arrays.asList(dnsServers));
actualCtx = async.getOrCreateContext();
this.async = async;
bootstrap = new Bootstrap();
bootstrap.group(actualCtx.getEventLoop());
bootstrap.channel(NioDatagramChannel.class);
bootstrap.option(ChannelOption.ALLOCATOR, PartialPooledByteBufAllocator.INSTANCE);
bootstrap.handler(new ChannelInitializer() {
@Override
protected void initChannel(DatagramChannel ch) throws Exception {
ChannelPipeline pipeline = ch.pipeline();
pipeline.addLast(new DnsQueryEncoder());
pipeline.addLast(new DnsResponseDecoder());
}
});
}
@Override
public DnsClient lookup4(String name, final Handler> handler) {
lookup(name, new HandlerAdapter(handler), DnsEntry.TYPE_A);
return this;
}
@Override
public DnsClient lookup6(String name, final Handler> handler) {
lookup(name, new HandlerAdapter(handler), DnsEntry.TYPE_AAAA);
return this;
}
@Override
public DnsClient lookup(String name, final Handler> handler) {
lookup(name, new HandlerAdapter(handler), DnsEntry.TYPE_A, DnsEntry.TYPE_AAAA);
return this;
}
@Override
public DnsClient resolveA(String name, final Handler>> handler) {
lookup(name, handler, DnsEntry.TYPE_A);
return this;
}
@Override
public DnsClient resolveCNAME(String name, Handler>> handler) {
lookup(name, handler, DnsEntry.TYPE_CNAME);
return this;
}
@Override
public DnsClient resolveMX(String name, final Handler>> handler) {
lookup(name, new ConvertingHandler(handler, DefaultMxRecordComparator.INSTANCE) {
@Override
protected MxRecord convert(MailExchangerRecord entry) {
return new DefaultMxRecord(entry);
}
}, DnsEntry.TYPE_MX);
return this;
}
@Override
public DnsClient resolveTXT(String name, final Handler>> handler) {
lookup(name, new Handler() {
@SuppressWarnings("unchecked")
@Override
public void handle(AsyncResult event) {
if (event.failed()) {
handler.handle(event);
} else {
List txts = new ArrayList<>();
List> records = (List>) event.result();
for (List txt : records) {
txts.addAll(txt);
}
handler.handle(new DefaultFutureResult(txts));
}
}
}, DnsEntry.TYPE_TXT);
return this;
}
@Override
public DnsClient resolvePTR(String name, final Handler> handler) {
lookup(name, new HandlerAdapter(handler), DnsEntry.TYPE_PTR);
return this;
}
@Override
public DnsClient resolveAAAA(String name, Handler>> handler) {
lookup(name, handler, DnsEntry.TYPE_AAAA);
return this;
}
@Override
public DnsClient resolveNS(String name, Handler>> handler) {
lookup(name, handler, DnsEntry.TYPE_NS);
return this;
}
@Override
public DnsClient resolveSRV(String name, final Handler>> handler) {
lookup(name, new ConvertingHandler(handler, DefaultSrvRecordComparator.INSTANCE) {
@Override
protected SrvRecord convert(ServiceRecord entry) {
return new DefaultSrvRecord(entry);
}
}, DnsEntry.TYPE_SRV);
return this;
}
@Override
public DnsClient reverseLookup(String address, final Handler> handler) {
// TODO: Check if the given address is a valid ip address before pass it to InetAddres.getByName(..)
// This is need as otherwise it may try to perform a DNS lookup.
// An other option would be to change address to be of type InetAddress.
try {
final InetAddress inetAddress = InetAddress.getByName(address);
byte[] addr = inetAddress.getAddress();
StringBuilder reverseName = new StringBuilder(64);
if (inetAddress instanceof Inet4Address) {
// reverse ipv4 address
reverseName.append(addr[3] & 0xff).append(".")
.append(addr[2] & 0xff).append(".")
.append(addr[1] & 0xff).append(".")
.append(addr[0] & 0xff);
} else {
// It is an ipv 6 address time to reverse it
for (int i = 0; i < 16; i++) {
reverseName.append(HEX_TABLE[(addr[15 - i] & 0xf)]);
reverseName.append(".");
reverseName.append(HEX_TABLE[(addr[15 - i] >> 4) & 0xf]);
if (i != 15) {
reverseName.append(".");
}
}
}
reverseName.append(".in-addr.arpa");
return resolvePTR(reverseName.toString(), new Handler>() {
@Override
public void handle(AsyncResult event) {
if (event.failed()) {
handler.handle(event);
} else {
String result = (String) event.result();
try {
handler.handle(new DefaultFutureResult<>(InetAddress.getByAddress(result, inetAddress.getAddress())));
} catch (UnknownHostException e) {
// Should never happen
handler.handle(new DefaultFutureResult(e));
}
}
}
});
} catch (final UnknownHostException e) {
// Should never happen as we work with ip addresses as input
// anyway just in case notify the handler
actualCtx.execute(new Runnable() {
public void run() {
handler.handle(new DefaultFutureResult(e));
}
});
}
return this;
}
@SuppressWarnings("unchecked")
private void lookup(final String name, final Handler handler, final int... types) {
final DefaultFutureResult result = new DefaultFutureResult<>();
result.setHandler(handler);
lookup(dnsServers.iterator(), name, result, types);
}
@SuppressWarnings("unchecked")
private void lookup(final Iterator dns, final String name, final DefaultFutureResult result, final int... types) {
bootstrap.connect(dns.next()).addListener(new RetryChannelFutureListener(dns, name, result, types) {
@Override
public void onSuccess(ChannelFuture future) throws Exception {
DnsQuery query = new DnsQuery(ThreadLocalRandom.current().nextInt());
for (int type : types) {
query.addQuestion(new DnsQuestion(name, type));
}
future.channel().writeAndFlush(query).addListener(new RetryChannelFutureListener(dns, name, result, types) {
@Override
public void onSuccess(ChannelFuture future) throws Exception {
future.channel().pipeline().addLast(new SimpleChannelInboundHandler() {
@Override
protected void channelRead0(ChannelHandlerContext ctx, DnsResponse msg) throws Exception {
DnsResponseCode code = DnsResponseCode.valueOf(msg.getHeader().getResponseCode());
if (code == DnsResponseCode.NOERROR) {
List resources = msg.getAnswers();
List
© 2015 - 2025 Weber Informatics LLC | Privacy Policy