View Javadoc
1   /*
2    * Copyright 2021 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   https://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package io.netty.example.dns.tcp;
17  
18  import io.netty.bootstrap.Bootstrap;
19  import io.netty.bootstrap.ServerBootstrap;
20  import io.netty.buffer.ByteBufUtil;
21  import io.netty.buffer.Unpooled;
22  import io.netty.channel.Channel;
23  import io.netty.channel.ChannelHandlerContext;
24  import io.netty.channel.ChannelInitializer;
25  import io.netty.channel.SimpleChannelInboundHandler;
26  import io.netty.channel.nio.NioEventLoopGroup;
27  import io.netty.channel.socket.SocketChannel;
28  import io.netty.channel.socket.nio.NioServerSocketChannel;
29  import io.netty.channel.socket.nio.NioSocketChannel;
30  import io.netty.handler.codec.dns.DefaultDnsQuery;
31  import io.netty.handler.codec.dns.DefaultDnsQuestion;
32  import io.netty.handler.codec.dns.DefaultDnsRawRecord;
33  import io.netty.handler.codec.dns.DefaultDnsResponse;
34  import io.netty.handler.codec.dns.DnsOpCode;
35  import io.netty.handler.codec.dns.DnsQuery;
36  import io.netty.handler.codec.dns.DnsQuestion;
37  import io.netty.handler.codec.dns.DnsRawRecord;
38  import io.netty.handler.codec.dns.DnsRecord;
39  import io.netty.handler.codec.dns.DnsRecordType;
40  import io.netty.handler.codec.dns.DnsSection;
41  import io.netty.handler.codec.dns.TcpDnsQueryDecoder;
42  import io.netty.handler.codec.dns.TcpDnsQueryEncoder;
43  import io.netty.handler.codec.dns.TcpDnsResponseDecoder;
44  import io.netty.handler.codec.dns.TcpDnsResponseEncoder;
45  import io.netty.handler.logging.LogLevel;
46  import io.netty.handler.logging.LoggingHandler;
47  import io.netty.util.NetUtil;
48  
49  import java.util.Random;
50  import java.util.concurrent.Executors;
51  import java.util.concurrent.TimeUnit;
52  
53  public final class TcpDnsServer {
54      private static final String QUERY_DOMAIN = "www.example.com";
55      private static final int DNS_SERVER_PORT = 53;
56      private static final String DNS_SERVER_HOST = "127.0.0.1";
57      private static final byte[] QUERY_RESULT = new byte[]{(byte) 192, (byte) 168, 1, 1};
58  
59      public static void main(String[] args) throws Exception {
60          ServerBootstrap bootstrap = new ServerBootstrap().group(new NioEventLoopGroup())
61                  .channel(NioServerSocketChannel.class)
62                  .handler(new LoggingHandler(LogLevel.INFO))
63                  .childHandler(new ChannelInitializer<Channel>() {
64                      @Override
65                      protected void initChannel(Channel ch) throws Exception {
66                          ch.pipeline().addLast(new TcpDnsQueryDecoder(), new TcpDnsResponseEncoder(),
67                                  new SimpleChannelInboundHandler<DnsQuery>() {
68                                      @Override
69                                      protected void channelRead0(ChannelHandlerContext ctx,
70                                                                  DnsQuery msg) throws Exception {
71                                          DnsQuestion question = msg.recordAt(DnsSection.QUESTION);
72                                          System.out.println("Query domain: " + question);
73  
74                                          //always return 192.168.1.1
75                                          ctx.writeAndFlush(newResponse(msg, question, 600, QUERY_RESULT));
76                                      }
77  
78                                      private DefaultDnsResponse newResponse(DnsQuery query,
79                                                                             DnsQuestion question,
80                                                                             long ttl, byte[]... addresses) {
81                                          DefaultDnsResponse response = new DefaultDnsResponse(query.id());
82                                          response.addRecord(DnsSection.QUESTION, question);
83  
84                                          for (byte[] address : addresses) {
85                                              DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
86                                                      question.name(),
87                                                      DnsRecordType.A, ttl, Unpooled.wrappedBuffer(address));
88                                              response.addRecord(DnsSection.ANSWER, queryAnswer);
89                                          }
90                                          return response;
91                                      }
92                                  });
93                      }
94                  });
95          final Channel channel = bootstrap.bind(DNS_SERVER_PORT).channel();
96          Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
97              @Override
98              public void run() {
99                  try {
100                     clientQuery();
101                     channel.close();
102                 } catch (Exception e) {
103                     e.printStackTrace();
104                 }
105             }
106         }, 1000, TimeUnit.MILLISECONDS);
107         channel.closeFuture().sync();
108     }
109 
110     // copy from TcpDnsClient.java
111     private static void clientQuery() throws Exception {
112         NioEventLoopGroup group = new NioEventLoopGroup();
113         try {
114             Bootstrap b = new Bootstrap();
115             b.group(group)
116                     .channel(NioSocketChannel.class)
117                     .handler(new ChannelInitializer<SocketChannel>() {
118                         @Override
119                         protected void initChannel(SocketChannel ch) {
120                             ch.pipeline().addLast(new TcpDnsQueryEncoder())
121                                     .addLast(new TcpDnsResponseDecoder())
122                                     .addLast(new SimpleChannelInboundHandler<DefaultDnsResponse>() {
123                                         @Override
124                                         protected void channelRead0(ChannelHandlerContext ctx, DefaultDnsResponse msg) {
125                                             try {
126                                                 handleQueryResp(msg);
127                                             } finally {
128                                                 ctx.close();
129                                             }
130                                         }
131                                     });
132                         }
133                     });
134 
135             final Channel ch = b.connect(DNS_SERVER_HOST, DNS_SERVER_PORT).sync().channel();
136 
137             int randomID = new Random().nextInt(60000 - 1000) + 1000;
138             DnsQuery query = new DefaultDnsQuery(randomID, DnsOpCode.QUERY)
139                     .setRecord(DnsSection.QUESTION, new DefaultDnsQuestion(QUERY_DOMAIN, DnsRecordType.A));
140             ch.writeAndFlush(query).sync();
141             boolean success = ch.closeFuture().await(10, TimeUnit.SECONDS);
142             if (!success) {
143                 System.err.println("dns query timeout!");
144                 ch.close().sync();
145             }
146         } finally {
147             group.shutdownGracefully();
148         }
149     }
150 
151     private static void handleQueryResp(DefaultDnsResponse msg) {
152         if (msg.count(DnsSection.QUESTION) > 0) {
153             DnsQuestion question = msg.recordAt(DnsSection.QUESTION, 0);
154             System.out.printf("name: %s%n", question.name());
155         }
156         for (int i = 0, count = msg.count(DnsSection.ANSWER); i < count; i++) {
157             DnsRecord record = msg.recordAt(DnsSection.ANSWER, i);
158             if (record.type() == DnsRecordType.A) {
159                 //just print the IP after query
160                 DnsRawRecord raw = (DnsRawRecord) record;
161                 System.out.println(NetUtil.bytesToIpAddress(ByteBufUtil.getBytes(raw.content())));
162             }
163         }
164     }
165 }