Make codec-dns can support build a dns server, reply answer from client.

Motivation:

codec-dns has great function to solve dns packet, but only make a query, not answer query from other client.

i make a change of add two classes to fill last pieces of map, finish the server function.

Modifications:

in this change, add two classes of DatagramDnsQueryDecoder and DatagramDnsResponseEncoder to handle client query, reply answer.

Result:

nothing code change after this commit, except two new classes.
This commit is contained in:
Yeti Sno 2016-01-10 21:55:07 +08:00 committed by Trustin Lee
parent dea337b4cf
commit b9ae48589b
4 changed files with 264 additions and 16 deletions

View File

@ -0,0 +1,114 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.handler.codec.dns;
import io.netty.buffer.ByteBuf;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.CorruptedFrameException;
import io.netty.handler.codec.MessageToMessageDecoder;
import java.net.InetSocketAddress;
import java.util.List;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* Decodes a {@link DatagramPacket} into a {@link DatagramDnsQuery}.
*/
@ChannelHandler.Sharable
public class DatagramDnsQueryDecoder extends MessageToMessageDecoder<DatagramPacket> {
private final DnsRecordDecoder recordDecoder;
/**
* Creates a new decoder with {@linkplain DnsRecordDecoder#DEFAULT the default record decoder}.
*/
public DatagramDnsQueryDecoder() {
this(DnsRecordDecoder.DEFAULT);
}
/**
* Creates a new decoder with the specified {@code recordDecoder}.
*/
public DatagramDnsQueryDecoder(DnsRecordDecoder recordDecoder) {
this.recordDecoder = checkNotNull(recordDecoder, "recordDecoder");
}
@Override
protected void decode(ChannelHandlerContext ctx, DatagramPacket packet, List<Object> out) throws Exception {
final ByteBuf buf = packet.content();
final DnsQuery query = newQuery(packet, buf);
boolean success = false;
try {
final int questionCount = buf.readUnsignedShort();
final int answerCount = buf.readUnsignedShort();
final int authorityRecordCount = buf.readUnsignedShort();
final int additionalRecordCount = buf.readUnsignedShort();
decodeQuestions(query, buf, questionCount);
decodeRecords(query, DnsSection.ANSWER, buf, answerCount);
decodeRecords(query, DnsSection.AUTHORITY, buf, authorityRecordCount);
decodeRecords(query, DnsSection.ADDITIONAL, buf, additionalRecordCount);
out.add(query);
success = true;
} finally {
if (!success) {
query.release();
}
}
}
private static DnsQuery newQuery(DatagramPacket packet, ByteBuf buf) {
final int id = buf.readUnsignedShort();
final int flags = buf.readUnsignedShort();
if (flags >> 15 == 1) {
throw new CorruptedFrameException("not a query");
}
final DnsQuery query =
new DatagramDnsQuery(
packet.sender(),
packet.recipient(),
id,
DnsOpCode.valueOf((byte) (flags >> 11 & 0xf)));
query.setRecursionDesired((flags >> 8 & 1) == 1);
query.setZ(flags >> 4 & 0x7);
return query;
}
private void decodeQuestions(DnsQuery query, ByteBuf buf, int questionCount) throws Exception {
for (int i = questionCount; i > 0; i--) {
query.addRecord(DnsSection.QUESTION, recordDecoder.decodeQuestion(buf));
}
}
private void decodeRecords(
DnsQuery query, DnsSection section, ByteBuf buf, int count) throws Exception {
for (int i = count; i > 0; i--) {
final DnsRecord r = recordDecoder.decodeRecord(buf);
if (r == null) {
// Truncated response
break;
}
query.addRecord(section, r);
}
}
}

View File

@ -52,8 +52,8 @@ public class DatagramDnsQueryEncoder extends MessageToMessageEncoder<AddressedEn
@Override
protected void encode(
ChannelHandlerContext ctx,
AddressedEnvelope<DnsQuery, InetSocketAddress> in, List<Object> out) throws Exception {
ChannelHandlerContext ctx,
AddressedEnvelope<DnsQuery, InetSocketAddress> in, List<Object> out) throws Exception {
final InetSocketAddress recipient = in.recipient();
final DnsQuery query = in.content();
@ -79,24 +79,24 @@ public class DatagramDnsQueryEncoder extends MessageToMessageEncoder<AddressedEn
* Sub-classes may override this method to return a {@link ByteBuf} with a perfect matching initial capacity.
*/
protected ByteBuf allocateBuffer(
ChannelHandlerContext ctx,
@SuppressWarnings("unused") AddressedEnvelope<DnsQuery, InetSocketAddress> msg) throws Exception {
ChannelHandlerContext ctx,
@SuppressWarnings("unused") AddressedEnvelope<DnsQuery, InetSocketAddress> msg) throws Exception {
return ctx.alloc().ioBuffer(1024);
}
/**
* Encodes the header that is always 12 bytes long.
*
* @param query
* the query header being encoded
* @param buf
* the buffer the encoded data should be written to
* @param query the query header being encoded
* @param buf the buffer the encoded data should be written to
*/
private static void encodeHeader(DnsQuery query, ByteBuf buf) {
buf.writeShort(query.id());
int flags = 0;
flags |= (query.opCode().byteValue() & 0xFF) << 14;
flags |= query.isRecursionDesired()? 1 << 8 : 0;
if (query.isRecursionDesired()) {
flags |= 1 << 8;
}
buf.writeShort(flags);
buf.writeShort(query.count(DnsSection.QUESTION));
buf.writeShort(0); // answerCount
@ -106,14 +106,14 @@ public class DatagramDnsQueryEncoder extends MessageToMessageEncoder<AddressedEn
private void encodeQuestions(DnsQuery query, ByteBuf buf) throws Exception {
final int count = query.count(DnsSection.QUESTION);
for (int i = 0; i < count; i ++) {
for (int i = 0; i < count; i++) {
recordEncoder.encodeQuestion((DnsQuestion) query.recordAt(DnsSection.QUESTION, i), buf);
}
}
private void encodeRecords(DnsQuery query, DnsSection section, ByteBuf buf) throws Exception {
final int count = query.count(section);
for (int i = 0; i < count; i ++) {
for (int i = 0; i < count; i++) {
recordEncoder.encodeRecord(query.recordAt(section, i), buf);
}
}

View File

@ -51,10 +51,9 @@ public class DatagramDnsResponseDecoder extends MessageToMessageDecoder<Datagram
@Override
protected void decode(ChannelHandlerContext ctx, DatagramPacket packet, List<Object> out) throws Exception {
final InetSocketAddress sender = packet.sender();
final ByteBuf buf = packet.content();
final DnsResponse response = newResponse(sender, buf);
final DnsResponse response = newResponse(packet, buf);
boolean success = false;
try {
final int questionCount = buf.readUnsignedShort();
@ -76,7 +75,7 @@ public class DatagramDnsResponseDecoder extends MessageToMessageDecoder<Datagram
}
}
private static DnsResponse newResponse(InetSocketAddress sender, ByteBuf buf) {
private static DnsResponse newResponse(DatagramPacket packet, ByteBuf buf) {
final int id = buf.readUnsignedShort();
final int flags = buf.readUnsignedShort();
@ -85,8 +84,10 @@ public class DatagramDnsResponseDecoder extends MessageToMessageDecoder<Datagram
}
final DnsResponse response = new DatagramDnsResponse(
sender, null,
id, DnsOpCode.valueOf((byte) (flags >> 11 & 0xf)), DnsResponseCode.valueOf((byte) (flags & 0xf)));
packet.sender(),
packet.recipient(),
id,
DnsOpCode.valueOf((byte) (flags >> 11 & 0xf)), DnsResponseCode.valueOf((byte) (flags & 0xf)));
response.setRecursionDesired((flags >> 8 & 1) == 1);
response.setAuthoritativeAnswer((flags >> 10 & 1) == 1);

View File

@ -0,0 +1,133 @@
/*
* Copyright 2015 The Netty Project
*
* The Netty Project licenses this file to you 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 io.netty.handler.codec.dns;
import io.netty.buffer.ByteBuf;
import io.netty.channel.AddressedEnvelope;
import io.netty.channel.ChannelHandler;
import io.netty.channel.ChannelHandlerContext;
import io.netty.channel.socket.DatagramPacket;
import io.netty.handler.codec.MessageToMessageEncoder;
import java.net.InetSocketAddress;
import java.util.List;
import static io.netty.util.internal.ObjectUtil.checkNotNull;
/**
* Encodes a {@link DatagramDnsResponse} (or an {@link AddressedEnvelope} of {@link DnsResponse}} into a
* {@link DatagramPacket}.
*/
@ChannelHandler.Sharable
public class DatagramDnsResponseEncoder
extends MessageToMessageEncoder<AddressedEnvelope<DnsResponse, InetSocketAddress>> {
private final DnsRecordEncoder recordEncoder;
/**
* Creates a new encoder with {@linkplain DnsRecordEncoder#DEFAULT the default record encoder}.
*/
public DatagramDnsResponseEncoder() {
this(DnsRecordEncoder.DEFAULT);
}
/**
* Creates a new encoder with the specified {@code recordEncoder}.
*/
public DatagramDnsResponseEncoder(DnsRecordEncoder recordEncoder) {
this.recordEncoder = checkNotNull(recordEncoder, "recordEncoder");
}
@Override
protected void encode(ChannelHandlerContext ctx,
AddressedEnvelope<DnsResponse, InetSocketAddress> in, List<Object> out) throws Exception {
final InetSocketAddress recipient = in.recipient();
final DnsResponse response = in.content();
final ByteBuf buf = allocateBuffer(ctx, in);
boolean success = false;
try {
encodeHeader(response, buf);
encodeQuestions(response, buf);
encodeRecords(response, DnsSection.ANSWER, buf);
encodeRecords(response, DnsSection.AUTHORITY, buf);
encodeRecords(response, DnsSection.ADDITIONAL, buf);
success = true;
} finally {
if (!success) {
buf.release();
}
}
out.add(new DatagramPacket(buf, recipient, null));
}
/**
* Allocate a {@link ByteBuf} which will be used for constructing a datagram packet.
* Sub-classes may override this method to return a {@link ByteBuf} with a perfect matching initial capacity.
*/
protected ByteBuf allocateBuffer(
ChannelHandlerContext ctx,
@SuppressWarnings("unused") AddressedEnvelope<DnsResponse, InetSocketAddress> msg) throws Exception {
return ctx.alloc().ioBuffer(1024);
}
/**
* Encodes the header that is always 12 bytes long.
*
* @param response the response header being encoded
* @param buf the buffer the encoded data should be written to
*/
private static void encodeHeader(DnsResponse response, ByteBuf buf) {
buf.writeShort(response.id());
int flags = 32768;
flags |= (response.opCode().byteValue() & 0xFF) << 11;
if (response.isAuthoritativeAnswer()) {
flags |= 1 << 10;
}
if (response.isTruncated()) {
flags |= 1 << 9;
}
if (response.isRecursionDesired()) {
flags |= 1 << 8;
}
if (response.isRecursionAvailable()) {
flags |= 1 << 7;
}
flags |= response.z() << 4;
flags |= response.code().intValue();
buf.writeShort(flags);
buf.writeShort(response.count(DnsSection.QUESTION));
buf.writeShort(response.count(DnsSection.ANSWER));
buf.writeShort(response.count(DnsSection.AUTHORITY));
buf.writeShort(response.count(DnsSection.ADDITIONAL));
}
private void encodeQuestions(DnsResponse response, ByteBuf buf) throws Exception {
final int count = response.count(DnsSection.QUESTION);
for (int i = 0; i < count; i++) {
recordEncoder.encodeQuestion((DnsQuestion) response.recordAt(DnsSection.QUESTION, i), buf);
}
}
private void encodeRecords(DnsResponse response, DnsSection section, ByteBuf buf) throws Exception {
final int count = response.count(section);
for (int i = 0; i < count; i++) {
recordEncoder.encodeRecord(response.recordAt(section, i), buf);
}
}
}