Added support to specify A Records for DNS lookups.
This commit is contained in:
parent
db4fffc893
commit
a553404284
|
@ -19,6 +19,11 @@ import org.slf4j.Logger;
|
|||
|
||||
import dorkbox.network.connection.EndPoint;
|
||||
import dorkbox.network.connection.Shutdownable;
|
||||
import dorkbox.network.dns.DnsQuestion;
|
||||
import dorkbox.network.dns.Name;
|
||||
import dorkbox.network.dns.constants.DnsClass;
|
||||
import dorkbox.network.dns.constants.DnsRecordType;
|
||||
import dorkbox.network.dns.records.ARecord;
|
||||
import dorkbox.network.dns.serverHandlers.DnsServerHandler;
|
||||
import dorkbox.util.NamedThreadFactory;
|
||||
import dorkbox.util.OS;
|
||||
|
@ -42,6 +47,7 @@ import io.netty.channel.socket.nio.NioDatagramChannel;
|
|||
import io.netty.channel.socket.nio.NioServerSocketChannel;
|
||||
import io.netty.channel.socket.oio.OioDatagramChannel;
|
||||
import io.netty.channel.socket.oio.OioServerSocketChannel;
|
||||
import io.netty.util.NetUtil;
|
||||
|
||||
/**
|
||||
* from: https://blog.cloudflare.com/how-the-consumer-product-safety-commission-is-inadvertently-behind-the-internets-largest-ddos-attacks/
|
||||
|
@ -76,6 +82,9 @@ class DnsServer extends Shutdownable {
|
|||
public static
|
||||
void main(String[] args) {
|
||||
DnsServer server = new DnsServer("localhost", 2053);
|
||||
|
||||
server.aRecord("google.com", DnsClass.IN, 10, "127.0.0.1");
|
||||
|
||||
// server.bind(false);
|
||||
server.bind();
|
||||
|
||||
|
@ -92,6 +101,7 @@ class DnsServer extends Shutdownable {
|
|||
// server.stop();
|
||||
}
|
||||
|
||||
private final DnsServerHandler dnsServerHandler;
|
||||
|
||||
public
|
||||
DnsServer(String host, int port) {
|
||||
|
@ -107,6 +117,7 @@ class DnsServer extends Shutdownable {
|
|||
hostName = host;
|
||||
}
|
||||
|
||||
dnsServerHandler = new DnsServerHandler(logger);
|
||||
String threadName = DnsServer.class.getSimpleName();
|
||||
|
||||
|
||||
|
@ -167,7 +178,7 @@ class DnsServer extends Shutdownable {
|
|||
.childOption(ChannelOption.ALLOCATOR, PooledByteBufAllocator.DEFAULT)
|
||||
.childOption(ChannelOption.SO_KEEPALIVE, true)
|
||||
.option(ChannelOption.WRITE_BUFFER_WATER_MARK, new WriteBufferWaterMark(EndPoint.WRITE_BUFF_LOW, EndPoint.WRITE_BUFF_HIGH))
|
||||
.childHandler(new DnsServerHandler(logger));
|
||||
.childHandler(dnsServerHandler);
|
||||
|
||||
// have to check options.host for "0.0.0.0". we don't bind to "0.0.0.0", we bind to "null" to get the "any" address!
|
||||
if (hostName.equals("0.0.0.0")) {
|
||||
|
@ -205,7 +216,7 @@ class DnsServer extends Shutdownable {
|
|||
|
||||
// not binding to specific address, since it's driven by TCP, and that can be bound to a specific address
|
||||
.localAddress(udpPort) // if you bind to a specific interface, Linux will be unable to receive broadcast packets!
|
||||
.handler(new DnsServerHandler(logger));
|
||||
.handler(dnsServerHandler);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -300,4 +311,27 @@ class DnsServer extends Shutdownable {
|
|||
waitForShutdown();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Adds a domain name query result, so clients that request the domain name will get the ipAddress
|
||||
*
|
||||
* @param domainName the domain name to have results for
|
||||
* @param ipAddresses the ip addresses (can be multiple) to return for the requested domain name
|
||||
*/
|
||||
public
|
||||
void aRecord(final String domainName, final int dClass, final int ttl, final String... ipAddresses) {
|
||||
Name name = DnsQuestion.createName(domainName, DnsRecordType.A);
|
||||
|
||||
int length = ipAddresses.length;
|
||||
ARecord[] records = new ARecord[length];
|
||||
|
||||
for (int i = 0; i < length; i++) {
|
||||
byte[] address = NetUtil.createByteArrayFromIpAddressString(ipAddresses[i]);
|
||||
|
||||
records[i] = new ARecord(name, dClass, ttl, address);
|
||||
}
|
||||
|
||||
dnsServerHandler.addARecord(name, records);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,7 +6,11 @@ import java.net.InetSocketAddress;
|
|||
import java.net.SocketAddress;
|
||||
import java.util.Locale;
|
||||
|
||||
import dorkbox.network.dns.constants.*;
|
||||
import dorkbox.network.dns.constants.DnsClass;
|
||||
import dorkbox.network.dns.constants.DnsOpCode;
|
||||
import dorkbox.network.dns.constants.DnsRecordType;
|
||||
import dorkbox.network.dns.constants.DnsSection;
|
||||
import dorkbox.network.dns.constants.Flags;
|
||||
import dorkbox.network.dns.records.DnsRecord;
|
||||
import io.netty.channel.AddressedEnvelope;
|
||||
import io.netty.util.internal.StringUtil;
|
||||
|
@ -26,14 +30,13 @@ class DnsQuestion extends DnsEnvelope {
|
|||
return newQuestion(inetHost, type, isRecursionDesired, false);
|
||||
}
|
||||
|
||||
private static
|
||||
DnsQuestion newQuestion(final String inetHost, final int type, final boolean isRecursionDesired, boolean isResolveQuestion) {
|
||||
|
||||
public static
|
||||
Name createName(String hostName, final int type) {
|
||||
// Convert to ASCII which will also check that the length is not too big. Throws null pointer if null.
|
||||
// See:
|
||||
// - https://github.com/netty/netty/issues/4937
|
||||
// - https://github.com/netty/netty/issues/4935
|
||||
String hostName = hostNameAsciiFix(checkNotNull(inetHost, "hostname"));
|
||||
hostName = hostNameAsciiFix(checkNotNull(hostName, "hostname"));
|
||||
|
||||
if (hostName == null) {
|
||||
// hostNameAsciiFix can throw a TextParseException if it fails to parse
|
||||
|
@ -46,13 +49,19 @@ class DnsQuestion extends DnsEnvelope {
|
|||
// NOTE: have to make sure that the hostname is a FQDN name
|
||||
hostName = DnsRecordType.ensureFQDN(type, hostName);
|
||||
|
||||
Name name;
|
||||
try {
|
||||
name = Name.fromString(hostName);
|
||||
return Name.fromString(hostName);
|
||||
} catch (Exception e) {
|
||||
// Name.fromString may throw a TextParseException if it fails to parse
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private static
|
||||
DnsQuestion newQuestion(final String inetHost, final int type, final boolean isRecursionDesired, boolean isResolveQuestion) {
|
||||
|
||||
Name name = createName(inetHost, type);
|
||||
|
||||
try {
|
||||
DnsRecord questionRecord = DnsRecord.newRecord(name, type, DnsClass.IN);
|
||||
|
|
|
@ -565,6 +565,15 @@ class DnsRecordType {
|
|||
}
|
||||
|
||||
private static final String ptrSuffix = ".in-addr.arpa";
|
||||
|
||||
/**
|
||||
* Guarantees that the specified host name is a FQND. This depends on it's type, which must also be specified.
|
||||
*
|
||||
* @param type the resource record type
|
||||
* @param hostName the hostname
|
||||
*
|
||||
* @return the Fully Qualified Domain Name for this hostname, depending on it's type
|
||||
*/
|
||||
public static
|
||||
String ensureFQDN(int type, String hostName) {
|
||||
// list of RecordTypes from: https://docs.aws.amazon.com/Route53/latest/DeveloperGuide/ResourceRecordTypes.html
|
||||
|
|
|
@ -15,9 +15,7 @@
|
|||
*/
|
||||
package dorkbox.network.dns.serverHandlers;
|
||||
|
||||
import java.net.InetAddress;
|
||||
import java.net.InetSocketAddress;
|
||||
import java.net.UnknownHostException;
|
||||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
|
@ -34,32 +32,37 @@ import dorkbox.network.dns.records.DnsRecord;
|
|||
import dorkbox.network.dns.records.Header;
|
||||
import dorkbox.network.dns.records.Update;
|
||||
import dorkbox.util.collections.IntMap;
|
||||
import dorkbox.util.collections.LockFreeHashMap;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
||||
public class DnsDecisionHandler extends ChannelInboundHandlerAdapter {
|
||||
|
||||
|
||||
private final Logger logger;
|
||||
private final LockFreeHashMap<Name, ARecord[]> aRecordMap;
|
||||
|
||||
|
||||
|
||||
private IntMap responses = new IntMap();
|
||||
// private final DnsClient dnsClient;
|
||||
private final InetAddress localHost;
|
||||
|
||||
public
|
||||
DnsDecisionHandler(final Logger logger) {
|
||||
this.logger = logger;
|
||||
|
||||
// dnsClient = new DnsClient();
|
||||
aRecordMap = new LockFreeHashMap<Name, ARecord[]>();
|
||||
}
|
||||
|
||||
InetAddress local;
|
||||
try {
|
||||
local = InetAddress.getLocalHost();
|
||||
} catch (UnknownHostException e) {
|
||||
e.printStackTrace();
|
||||
local = null;
|
||||
}
|
||||
|
||||
localHost = local;
|
||||
/**
|
||||
* Adds a domain name query result, so clients that request the domain name will get the ipAddress
|
||||
*
|
||||
* @param domainName the domain name to have results for
|
||||
* @param aRecords the A records (can be multiple) to return for the requested domain name
|
||||
*/
|
||||
public
|
||||
void addARecord(final Name domainName, final ARecord[] aRecords) {
|
||||
aRecordMap.put(domainName, aRecords);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
@ -120,19 +123,35 @@ public class DnsDecisionHandler extends ChannelInboundHandlerAdapter {
|
|||
long ttl = dnsRecord.getTTL();
|
||||
int type = dnsRecord.getType();
|
||||
|
||||
// // what type of record? A, AAAA, MX, PTR, etc?
|
||||
|
||||
// what type of record? A, AAAA, MX, PTR, etc?
|
||||
if (DnsRecordType.A == type) {
|
||||
ARecord answerRecord = new ARecord(name, dnsRecord.getDClass(), 10, localHost);
|
||||
dnsResponse.addRecord(dnsRecord, DnsSection.QUESTION);
|
||||
dnsResponse.addRecord(answerRecord, DnsSection.ANSWER);
|
||||
ARecord[] records = aRecordMap.get(name);
|
||||
|
||||
dnsResponse.getHeader().setRcode(DnsResponseCode.NOERROR);
|
||||
if (records != null) {
|
||||
dnsResponse.addRecord(dnsRecord, DnsSection.QUESTION);
|
||||
dnsResponse.getHeader()
|
||||
.setRcode(DnsResponseCode.NOERROR);
|
||||
|
||||
logger.debug("Writing A record response: {}", answerRecord.getAddress());
|
||||
|
||||
for (int i = 0; i < records.length; i++) {
|
||||
ARecord record = records[i];
|
||||
|
||||
dnsResponse.addRecord(record, DnsSection.ANSWER);
|
||||
logger.debug("Writing A record response: {}", record.getAddress());
|
||||
}
|
||||
|
||||
context.channel()
|
||||
.write(dnsResponse);
|
||||
|
||||
return;
|
||||
} else {
|
||||
logger.debug("Sending DNS query to the forwarder...");
|
||||
// have to send this on to the forwarder
|
||||
}
|
||||
}
|
||||
|
||||
context.channel()
|
||||
.write(dnsResponse);
|
||||
|
||||
|
||||
|
||||
// ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
|
||||
|
|
|
@ -3,6 +3,8 @@ package dorkbox.network.dns.serverHandlers;
|
|||
|
||||
import org.slf4j.Logger;
|
||||
|
||||
import dorkbox.network.dns.Name;
|
||||
import dorkbox.network.dns.records.ARecord;
|
||||
import io.netty.channel.Channel;
|
||||
import io.netty.channel.ChannelHandlerContext;
|
||||
import io.netty.channel.ChannelInboundHandlerAdapter;
|
||||
|
@ -27,6 +29,17 @@ class DnsServerHandler extends ChannelInboundHandlerAdapter {
|
|||
encoder = new DnsMessageEncoder(logger);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds a domain name query result, so clients that request the domain name will get the ipAddress
|
||||
*
|
||||
* @param domainName the domain name to have results for
|
||||
* @param @param aRecords the A records (can be multiple) to return for the requested domain name
|
||||
*/
|
||||
public
|
||||
void addARecord(final Name domainName, final ARecord[] aRecords) {
|
||||
decisionHandler.addARecord(domainName, aRecords);
|
||||
}
|
||||
|
||||
@Override
|
||||
public final
|
||||
void channelRegistered(final ChannelHandlerContext context) {
|
||||
|
|
|
@ -46,7 +46,7 @@ class BroadcastServer {
|
|||
// absolutely MUST send packet > 0 across, otherwise netty will think it failed to write to the socket, and keep trying.
|
||||
// (this bug was fixed by netty, however we are keeping this code)
|
||||
ByteBuf directBuffer = channel.alloc()
|
||||
.directBuffer(1);
|
||||
.ioBuffer(1);
|
||||
directBuffer.writeByte(Broadcast.broadcastResponseID);
|
||||
|
||||
channel.writeAndFlush(new DatagramPacket(directBuffer, remoteAddress, localAddress));
|
||||
|
|
|
@ -49,7 +49,7 @@ class KryoEncoderUdp extends MessageToMessageEncoder<Object> {
|
|||
if (message != null) {
|
||||
try {
|
||||
ByteBuf outBuffer = context.alloc()
|
||||
.buffer(maxSize);
|
||||
.ioBuffer(maxSize);
|
||||
|
||||
// no size info, since this is UDP, it is not segmented
|
||||
writeObject(this.serializationManager, context, message, outBuffer);
|
||||
|
|
Loading…
Reference in New Issue
Block a user