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:
parent
dea337b4cf
commit
b9ae48589b
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue
Block a user