Dns server working for single test query issued by command line

This commit is contained in:
nathan 2018-03-03 14:00:29 +01:00
parent 0460c35e2e
commit 3587933022
9 changed files with 237 additions and 76 deletions

View File

@ -0,0 +1,98 @@
/*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.network.dns;
import java.net.InetSocketAddress;
import java.net.SocketAddress;
import dorkbox.network.dns.records.DnsMessage;
import io.netty.channel.AddressedEnvelope;
import io.netty.util.internal.UnstableApi;
/**
* A {@link DnsServerResponse} implementation for UDP/IP.
*/
@UnstableApi
public
class DnsServerResponse extends DnsEnvelope {
/**
* Creates a new instance.
*
* @param localAddress the address of the sender
* @param remoteAddress the address of the recipient
*/
public
DnsServerResponse(final DnsMessage dnsQuestion, InetSocketAddress localAddress, InetSocketAddress remoteAddress) {
super(dnsQuestion.getHeader()
.getID(), localAddress, remoteAddress);
if (remoteAddress == null && localAddress == null) {
throw new NullPointerException("localAddress and remoteAddress");
}
}
@Override
public
int hashCode() {
int hashCode = super.hashCode();
if (sender() != null) {
hashCode = hashCode * 31 + sender().hashCode();
}
if (recipient() != null) {
hashCode = hashCode * 31 + recipient().hashCode();
}
return hashCode;
}
@Override
public
boolean equals(Object obj) {
if (this == obj) {
return true;
}
if (!super.equals(obj)) {
return false;
}
if (!(obj instanceof AddressedEnvelope)) {
return false;
}
@SuppressWarnings("unchecked")
final AddressedEnvelope<?, SocketAddress> that = (AddressedEnvelope<?, SocketAddress>) obj;
if (sender() == null) {
if (that.sender() != null) {
return false;
}
}
else if (!sender().equals(that.sender())) {
return false;
}
if (recipient() == null) {
if (that.recipient() != null) {
return false;
}
}
else if (!recipient().equals(that.recipient())) {
return false;
}
return true;
}
}

View File

@ -22,16 +22,20 @@ import java.net.UnknownHostException;
import org.slf4j.Logger; import org.slf4j.Logger;
import dorkbox.network.dns.DnsEnvelope; import dorkbox.network.dns.DnsEnvelope;
import dorkbox.network.dns.DnsServerResponse;
import dorkbox.network.dns.Name; import dorkbox.network.dns.Name;
import dorkbox.network.dns.constants.DnsOpCode; import dorkbox.network.dns.constants.DnsOpCode;
import dorkbox.network.dns.constants.DnsRecordType; import dorkbox.network.dns.constants.DnsRecordType;
import dorkbox.network.dns.constants.DnsResponseCode; import dorkbox.network.dns.constants.DnsResponseCode;
import dorkbox.network.dns.constants.DnsSection; import dorkbox.network.dns.constants.DnsSection;
import dorkbox.network.dns.records.*; import dorkbox.network.dns.records.ARecord;
import dorkbox.network.dns.records.DnsMessage;
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.IntMap;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.util.ReferenceCountUtil;
public class DnsDecisionHandler extends ChannelInboundHandlerAdapter { public class DnsDecisionHandler extends ChannelInboundHandlerAdapter {
@ -62,10 +66,9 @@ public class DnsDecisionHandler extends ChannelInboundHandlerAdapter {
public public
void channelRead(ChannelHandlerContext context, Object message) throws Exception { void channelRead(ChannelHandlerContext context, Object message) throws Exception {
onChannelRead(context, (DnsEnvelope) message); onChannelRead(context, (DnsEnvelope) message);
ReferenceCountUtil.release(message);
} }
public private
void onChannelRead(final ChannelHandlerContext context, final DnsEnvelope dnsMessage) { void onChannelRead(final ChannelHandlerContext context, final DnsEnvelope dnsMessage) {
int opcode = dnsMessage.getHeader() int opcode = dnsMessage.getHeader()
.getOpcode(); .getOpcode();
@ -73,32 +76,26 @@ public class DnsDecisionHandler extends ChannelInboundHandlerAdapter {
switch (opcode) { switch (opcode) {
case DnsOpCode.QUERY: case DnsOpCode.QUERY:
onQuery(context, dnsMessage, dnsMessage.recipient()); onQuery(context, dnsMessage, dnsMessage.recipient());
dnsMessage.release();
return; return;
case DnsOpCode.IQUERY: case DnsOpCode.IQUERY:
onIQuery(context, dnsMessage, dnsMessage.recipient()); onIQuery(context, dnsMessage, dnsMessage.recipient());
dnsMessage.release();
return; return;
case DnsOpCode.NOTIFY: case DnsOpCode.NOTIFY:
onNotify(context, dnsMessage, dnsMessage.recipient()); onNotify(context, dnsMessage, dnsMessage.recipient());
dnsMessage.release();
return; return;
case DnsOpCode.STATUS: case DnsOpCode.STATUS:
onStatus(context, dnsMessage, dnsMessage.recipient()); onStatus(context, dnsMessage, dnsMessage.recipient());
dnsMessage.release();
return; return;
case DnsOpCode.UPDATE: case DnsOpCode.UPDATE:
onUpdate(context, (Update) (DnsMessage) dnsMessage, dnsMessage.recipient()); onUpdate(context, (Update) (DnsMessage) dnsMessage, dnsMessage.recipient());
dnsMessage.release();
return; return;
default: default:
logger.error("Unknown DNS opcode {} from {}", opcode, context.channel().remoteAddress()); logger.error("Unknown DNS opcode {} from {}", opcode, context.channel().remoteAddress());
dnsMessage.release();
} }
} }
@ -111,12 +108,11 @@ public class DnsDecisionHandler extends ChannelInboundHandlerAdapter {
// we don't support more than 1 question at a time. // we don't support more than 1 question at a time.
if (count == 1) { if (count == 1) {
DnsEnvelope dnsEnvelope = new DnsEnvelope(dnsQuestion.getHeader() DnsServerResponse dnsResponse = new DnsServerResponse(dnsQuestion,
.getID(), (InetSocketAddress) context.channel().localAddress(),
(InetSocketAddress) context.channel().localAddress(), recipient);
recipient);
// dnsEnvelope.getHeader().setRcode(DnsResponseCode.NXDOMAIN); // dnsResponse.getHeader().setRcode(DnsResponseCode.NXDOMAIN);
DnsRecord[] sectionArray = dnsQuestion.getSectionArray(DnsSection.QUESTION); DnsRecord[] sectionArray = dnsQuestion.getSectionArray(DnsSection.QUESTION);
DnsRecord dnsRecord = sectionArray[0]; DnsRecord dnsRecord = sectionArray[0];
@ -127,22 +123,16 @@ public class DnsDecisionHandler extends ChannelInboundHandlerAdapter {
// // what type of record? A, AAAA, MX, PTR, etc? // // what type of record? A, AAAA, MX, PTR, etc?
if (DnsRecordType.A == type) { if (DnsRecordType.A == type) {
ARecord answerRecord = new ARecord(name, dnsRecord.getDClass(), 10, localHost); ARecord answerRecord = new ARecord(name, dnsRecord.getDClass(), 10, localHost);
dnsEnvelope.addRecord(dnsRecord, DnsSection.QUESTION); dnsResponse.addRecord(dnsRecord, DnsSection.QUESTION);
dnsEnvelope.addRecord(answerRecord, DnsSection.ANSWER); dnsResponse.addRecord(answerRecord, DnsSection.ANSWER);
dnsEnvelope.getHeader().setRcode(DnsResponseCode.NOERROR); dnsResponse.getHeader().setRcode(DnsResponseCode.NOERROR);
System.err.println("write"); logger.debug("Writing A record response: {}", answerRecord.getAddress());
} }
// dnsEnvelope.retain();
// NOTE: I suspect this must be a "client" that writes back. there are errors if not.
context.channel() context.channel()
.writeAndFlush(dnsEnvelope); .write(dnsResponse);
// out.add(new DatagramPacket(buf, recipient, null));
// ChannelBuffer buffer = (ChannelBuffer) e.getMessage(); // ChannelBuffer buffer = (ChannelBuffer) e.getMessage();
@ -292,7 +282,7 @@ public class DnsDecisionHandler extends ChannelInboundHandlerAdapter {
@Override @Override
public public
void exceptionCaught(final ChannelHandlerContext context, final Throwable cause) throws Exception { void exceptionCaught(final ChannelHandlerContext context, final Throwable cause) throws Exception {
logger.error("ForwardingHandler#exceptionCaught", cause); logger.error("DecisionHandler#exceptionCaught", cause);
super.exceptionCaught(context, cause); super.exceptionCaught(context, cause);
} }

View File

@ -43,10 +43,6 @@ class DnsMessageDecoder extends MessageToMessageDecoder<DatagramPacket> {
InetSocketAddress remoteAddress = packet.sender(); InetSocketAddress remoteAddress = packet.sender();
DnsEnvelope dnsEnvelope = new DnsEnvelope(buf, localAddress, remoteAddress); DnsEnvelope dnsEnvelope = new DnsEnvelope(buf, localAddress, remoteAddress);
dnsEnvelope.retain();
// send down the pipeline
out.add(dnsEnvelope); out.add(dnsEnvelope);
success = true; success = true;
} finally { } finally {

View File

@ -1,25 +1,29 @@
package dorkbox.network.dns.serverHandlers; package dorkbox.network.dns.serverHandlers;
import java.io.IOException;
import org.slf4j.Logger;
import dorkbox.network.dns.DnsOutput; import dorkbox.network.dns.DnsOutput;
import dorkbox.network.dns.records.DnsMessage; import dorkbox.network.dns.DnsServerResponse;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler; import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
/** /**
* An encoder which serializes a Java object into a {@link ByteBuf}. *
* <p>
* Please note that the serialized form this encoder produces is not
* compatible with the standard {@link ObjectInputStream}. Please use
* {@link ObjectDecoder} or {@link ObjectDecoderInputStream} to ensure the
* interoperability with this encoder.
*/ */
@ChannelHandler.Sharable @ChannelHandler.Sharable
public public
class DnsMessageEncoder extends MessageToByteEncoder<DnsMessage> { class DnsMessageEncoder extends MessageToByteEncoder<DnsServerResponse> {
private final Logger logger;
public
DnsMessageEncoder(final Logger logger) {
this.logger = logger;
}
// public // public
@ -54,23 +58,24 @@ class DnsMessageEncoder extends MessageToByteEncoder<DnsMessage> {
@Override @Override
protected protected
void encode(final ChannelHandlerContext ctx, final DnsMessage msg, final ByteBuf out) throws Exception { void encode(final ChannelHandlerContext context, final DnsServerResponse message, final ByteBuf out) throws Exception {
System.err.println("WRITING MESSAGE"); try {
final DnsOutput outd = new DnsOutput(out); DnsOutput dnsOutput = new DnsOutput(out);
msg.toWire(outd); out.retain();
message.toWire(dnsOutput);
DatagramPacket packet = new DatagramPacket(out, message.recipient(), message.sender());
context.channel()
.writeAndFlush(packet);
} catch (Exception e) {
context.fireExceptionCaught(new IOException("Unable to write dns message: " + message, e));
}
} }
@Override @Override
public public
void exceptionCaught(ChannelHandlerContext context, Throwable cause) throws Exception { void exceptionCaught(final ChannelHandlerContext context, final Throwable cause) throws Exception {
// Channel channel = context.channel(); logger.error("DnsMessageEncoder#exceptionCaught", cause);
super.exceptionCaught(context, cause);
System.err.println("POW! ");
cause.printStackTrace();
// this.logger.error("Unexpected exception while trying to send/receive data on Client remote (network) channel. ({})" +
// System.getProperty("line.separator"), channel.remoteAddress(), cause);
// if (channel.isOpen()) {
// channel.close();
// }
} }
} }

View File

@ -15,11 +15,16 @@ public
class DnsServerHandler extends ChannelInboundHandlerAdapter { class DnsServerHandler extends ChannelInboundHandlerAdapter {
protected final DnsMessageDecoder decoder; protected final DnsMessageDecoder decoder;
private final Logger logger; private final Logger logger;
private DnsDecisionHandler decisionHandler;
private DnsMessageEncoder encoder;
public public
DnsServerHandler(final Logger logger) { DnsServerHandler(final Logger logger) {
this.logger = logger; this.logger = logger;
decoder = new DnsMessageDecoder(logger); decoder = new DnsMessageDecoder(logger);
decisionHandler = new DnsDecisionHandler(logger);
encoder = new DnsMessageEncoder(logger);
} }
@Override @Override
@ -49,12 +54,13 @@ class DnsServerHandler extends ChannelInboundHandlerAdapter {
/////////////////////// ///////////////////////
// DECODE (or upstream) // DECODE (or upstream)
/////////////////////// ///////////////////////
pipeline.addLast("decoder", this.decoder); pipeline.addLast("decoder", decoder);
pipeline.addLast("dnsDecision", decisionHandler);
// ENCODE (or downstream) // ENCODE (or downstream)
///////////////////////// /////////////////////////
pipeline.addLast("dnsDecision", new DnsDecisionHandler(logger)); pipeline.addLast("encoder", encoder);
pipeline.addLast("fowarder", new ForwardingHandler(logger)); // pipeline.addLast("fowarder", new ForwardingHandler(logger));
// pipeline.addLast("fowarder", new ForwardingHandler(this.config, this.clientChannelFactory)); // pipeline.addLast("fowarder", new ForwardingHandler(this.config, this.clientChannelFactory));
} }
} }

View File

@ -1,4 +1,19 @@
package dorkbox.network.dns.serverHandlers; /*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.network.dns.serverHandlers.wip;
import dorkbox.network.dns.DnsResponse; import dorkbox.network.dns.DnsResponse;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;

View File

@ -1,15 +1,26 @@
package dorkbox.network.dns.serverHandlers; /*
* Copyright 2018 dorkbox, llc.
import java.io.IOException; *
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.network.dns.serverHandlers.wip;
import org.slf4j.Logger; import org.slf4j.Logger;
import dorkbox.network.dns.DnsEnvelope; import dorkbox.network.dns.DnsEnvelope;
import dorkbox.network.dns.DnsOutput;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandlerContext; import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandlerAdapter; import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToByteEncoder; import io.netty.handler.codec.MessageToByteEncoder;
public class ForwardingHandler extends MessageToByteEncoder<DnsEnvelope> { public class ForwardingHandler extends MessageToByteEncoder<DnsEnvelope> {
@ -39,16 +50,19 @@ public class ForwardingHandler extends MessageToByteEncoder<DnsEnvelope> {
void encode(final ChannelHandlerContext context, final DnsEnvelope message, final ByteBuf out) throws Exception { void encode(final ChannelHandlerContext context, final DnsEnvelope message, final ByteBuf out) throws Exception {
System.err.println("FORWARD HANDLER ENCODE"); System.err.println("FORWARD HANDLER ENCODE");
try { // TODO: forward the message to ANOTHER dns server because we don't know what the awnser is
DnsOutput dnsOutput = new DnsOutput(out);
message.toWire(dnsOutput);
context.channel() // try {
.writeAndFlush(new DatagramPacket(out, message.recipient(), null)); // DnsOutput dnsOutput = new DnsOutput(out);
// .write(new DatagramPacket(out, message.recipient(), message.sender())); // message.toWire(dnsOutput);
} catch (Exception e) { //
context.fireExceptionCaught(new IOException("Unable to write dns message: " + message, e)); // //todo: need void promise
} // context.channel()
// .writeAndFlush(new DatagramPacket(out, message.recipient(), message.sender()));
// // .write(new DatagramPacket(out, message.recipient(), message.sender()));
// } catch (Exception e) {
// context.fireExceptionCaught(new IOException("Unable to write dns message: " + message, e));
// }
} }

View File

@ -1,4 +1,19 @@
package dorkbox.network.dns.serverHandlers; /*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.network.dns.serverHandlers.wip;
import io.netty.channel.ChannelFuture; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelFutureListener; import io.netty.channel.ChannelFutureListener;

View File

@ -1,4 +1,19 @@
package dorkbox.network.dns.serverHandlers; /*
* Copyright 2018 dorkbox, llc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package dorkbox.network.dns.serverHandlers.wip;
import java.net.InetSocketAddress; import java.net.InetSocketAddress;
@ -7,7 +22,14 @@ import dorkbox.util.NamedThreadFactory;
import io.netty.bootstrap.Bootstrap; import io.netty.bootstrap.Bootstrap;
import io.netty.buffer.ByteBuf; import io.netty.buffer.ByteBuf;
import io.netty.buffer.PooledByteBufAllocator; import io.netty.buffer.PooledByteBufAllocator;
import io.netty.channel.*; import io.netty.channel.ChannelFuture;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.ChannelInboundHandler;
import io.netty.channel.ChannelInboundHandlerAdapter;
import io.netty.channel.ChannelOption;
import io.netty.channel.ChannelPipeline;
import io.netty.channel.EventLoopGroup;
import io.netty.channel.nio.NioEventLoopGroup; import io.netty.channel.nio.NioEventLoopGroup;
import io.netty.channel.oio.OioEventLoopGroup; import io.netty.channel.oio.OioEventLoopGroup;
import io.netty.channel.socket.DatagramPacket; import io.netty.channel.socket.DatagramPacket;