使用Netty

时间:2017-10-07 08:48:50

标签: java dns netty nameservers

我一直在尝试使用netty编写自定义DNS服务器。我使用DatagramDnsQueryDecoder来解析传入的DNSQuery UDP数据包,但我无法弄清楚如何发送响应来解析域名。我收到了处理程序中的DatagramDnsQuery对象,但无法找到初始化DatagramDnsResponse并添加测试DNS记录的方法,并通过DatagramDnsResponseEncoder将其发送回客户端。

这是我到目前为止所做的事情

public class DNSListen {
public static void main(String[] args) {
    final NioEventLoopGroup group = new NioEventLoopGroup();

    try {
        Bootstrap bootstrap = new Bootstrap();
        bootstrap.group(group)
                .channel(NioDatagramChannel.class)
                .handler(new ChannelInitializer<NioDatagramChannel>() {
                    @Override
                    protected void initChannel(NioDatagramChannel nioDatagramChannel) throws Exception {
                        nioDatagramChannel.pipeline().addLast(new DatagramDnsQueryDecoder());
                        nioDatagramChannel.pipeline().addLast(new DatagramDnsResponseEncoder());
                        nioDatagramChannel.pipeline().addLast(new DNSMessageHandler());
                    }
                })
                .option(ChannelOption.SO_BROADCAST, true);

        ChannelFuture future = bootstrap.bind(53).sync();
        future.channel().closeFuture().sync();
    } catch (InterruptedException e) {
        e.printStackTrace();
    } finally {
        group.shutdownGracefully();
    }

}
}

这是DNSMessages的处理程序

public class DNSMessageHandler extends SimpleChannelInboundHandler<DatagramDnsQuery> {

@Override
protected void channelRead0(ChannelHandlerContext ctx, DatagramDnsQuery dnsMessage) throws Exception {
    System.out.println(dnsMessage.content());
}

@Override
public void exceptionCaught(ChannelHandlerContext ctx, Throwable cause) throws Exception {
    cause.printStackTrace();
}
}

运行此命令时,我获取多个DNS请求的输出,并将系统DNS设置为127.0.0.1 但我找不到发送虚拟DNS响应的方法,以便为请求的域解析IP地址。 (我想我应该初始化一个DatagramDnsResponse对象并将其写回给用户,但这是正确的吗?如果是这样的话,我如何用虚拟IP初始化它作为已解析的IP) 我是在错误的道路上,有人可以指引我走正确的道路。感谢。

1 个答案:

答案 0 :(得分:2)

实际上,您只需要DatagramDnsResponse的实例并使用addRecord方法添加dns记录即可。

这是SRV答案的一个例子:

DatagramDnsResponse response = new DatagramDnsResponse(msg.recipient(), msg.sender(), msg.id());
ByteBuf buf = Unpooled.buffer();
buf.writeShort(0); // priority
buf.writeShort(0); // weight
buf.writeShort(993); // port
encodeName("my.domain.tld", buf); // target (special encoding: https://tools.ietf.org/html/rfc1101)
response.addRecord(DnsSection.ANSWER, new DefaultDnsRawRecord("_myprotocol._tcp.domain.tld." /* requested domain */, DnsRecordType.SRV, 30 /* ttl */ , buf));
ctx.writeAndFlush(response);

修改

要对名称进行编码,请使用此函数(我从netty中提取它):

public void encodeName(String name, ByteBuf buf) throws Exception {
    if(".".equals(name)) {
        buf.writeByte(0);
        return;
    }

    final String[] labels = name.split("\\.");
    for (String label : labels) {
        final int labelLen = label.length();
        if (labelLen == 0)
            break;

        buf.writeByte(labelLen);
        ByteBufUtil.writeAscii(buf, label);
    }

    buf.writeByte(0);
}

要解码SRV记录,您可以使用此代码段:

DnsRawRecord record = msg.recordAt(DnsSection.ANSWER, 0); //msg is a DnsResponse
if(record.type() == DnsRecordType.SRV) {
    ByteBuf buf = record.content();
    System.out.println("\tPriority: " + buf.readUnsignedShort());
    System.out.println("\tWeight: " + buf.readUnsignedShort());
    System.out.println("\tPort: " + buf.readUnsignedShort());
    System.out.println("\tTarget:" + DefaultDnsRecordDecoder.decodeName(buf));
}