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.EventLoopGroup;
26  import io.netty.channel.MultiThreadIoEventLoopGroup;
27  import io.netty.channel.SimpleChannelInboundHandler;
28  import io.netty.channel.nio.NioIoHandler;
29  import io.netty.channel.socket.SocketChannel;
30  import io.netty.channel.socket.nio.NioServerSocketChannel;
31  import io.netty.channel.socket.nio.NioSocketChannel;
32  import io.netty.handler.codec.dns.DefaultDnsQuery;
33  import io.netty.handler.codec.dns.DefaultDnsQuestion;
34  import io.netty.handler.codec.dns.DefaultDnsRawRecord;
35  import io.netty.handler.codec.dns.DefaultDnsResponse;
36  import io.netty.handler.codec.dns.DnsOpCode;
37  import io.netty.handler.codec.dns.DnsQuery;
38  import io.netty.handler.codec.dns.DnsQuestion;
39  import io.netty.handler.codec.dns.DnsRawRecord;
40  import io.netty.handler.codec.dns.DnsRecord;
41  import io.netty.handler.codec.dns.DnsRecordType;
42  import io.netty.handler.codec.dns.DnsSection;
43  import io.netty.handler.codec.dns.TcpDnsQueryDecoder;
44  import io.netty.handler.codec.dns.TcpDnsQueryEncoder;
45  import io.netty.handler.codec.dns.TcpDnsResponseDecoder;
46  import io.netty.handler.codec.dns.TcpDnsResponseEncoder;
47  import io.netty.handler.logging.LogLevel;
48  import io.netty.handler.logging.LoggingHandler;
49  import io.netty.util.NetUtil;
50  
51  import java.util.Random;
52  import java.util.concurrent.Executors;
53  import java.util.concurrent.TimeUnit;
54  
55  public final class TcpDnsServer {
56      private static final String QUERY_DOMAIN = "www.example.com";
57      private static final int DNS_SERVER_PORT = 53;
58      private static final String DNS_SERVER_HOST = "127.0.0.1";
59      private static final byte[] QUERY_RESULT = new byte[]{(byte) 192, (byte) 168, 1, 1};
60  
61      public static void main(String[] args) throws Exception {
62          ServerBootstrap bootstrap = new ServerBootstrap().group(
63                  new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory()))
64                  .channel(NioServerSocketChannel.class)
65                  .handler(new LoggingHandler(LogLevel.INFO))
66                  .childHandler(new ChannelInitializer<Channel>() {
67                      @Override
68                      protected void initChannel(Channel ch) throws Exception {
69                          ch.pipeline().addLast(new TcpDnsQueryDecoder(), new TcpDnsResponseEncoder(),
70                                  new SimpleChannelInboundHandler<DnsQuery>() {
71                                      @Override
72                                      protected void channelRead0(ChannelHandlerContext ctx,
73                                                                  DnsQuery msg) throws Exception {
74                                          DnsQuestion question = msg.recordAt(DnsSection.QUESTION);
75                                          System.out.println("Query domain: " + question);
76  
77                                          //always return 192.168.1.1
78                                          ctx.writeAndFlush(newResponse(msg, question, 600, QUERY_RESULT));
79                                      }
80  
81                                      private DefaultDnsResponse newResponse(DnsQuery query,
82                                                                             DnsQuestion question,
83                                                                             long ttl, byte[]... addresses) {
84                                          DefaultDnsResponse response = new DefaultDnsResponse(query.id());
85                                          response.addRecord(DnsSection.QUESTION, question);
86  
87                                          for (byte[] address : addresses) {
88                                              DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
89                                                      question.name(),
90                                                      DnsRecordType.A, ttl, Unpooled.wrappedBuffer(address));
91                                              response.addRecord(DnsSection.ANSWER, queryAnswer);
92                                          }
93                                          return response;
94                                      }
95                                  });
96                      }
97                  });
98          final Channel channel = bootstrap.bind(DNS_SERVER_PORT).channel();
99          Executors.newSingleThreadScheduledExecutor().schedule(new Runnable() {
100             @Override
101             public void run() {
102                 try {
103                     clientQuery();
104                     channel.close();
105                 } catch (Exception e) {
106                     e.printStackTrace();
107                 }
108             }
109         }, 1000, TimeUnit.MILLISECONDS);
110         channel.closeFuture().sync();
111     }
112 
113     // copy from TcpDnsClient.java
114     private static void clientQuery() throws Exception {
115         EventLoopGroup group = new MultiThreadIoEventLoopGroup(NioIoHandler.newFactory());
116         try {
117             Bootstrap b = new Bootstrap();
118             b.group(group)
119                     .channel(NioSocketChannel.class)
120                     .handler(new ChannelInitializer<SocketChannel>() {
121                         @Override
122                         protected void initChannel(SocketChannel ch) {
123                             ch.pipeline().addLast(new TcpDnsQueryEncoder())
124                                     .addLast(new TcpDnsResponseDecoder())
125                                     .addLast(new SimpleChannelInboundHandler<DefaultDnsResponse>() {
126                                         @Override
127                                         protected void channelRead0(ChannelHandlerContext ctx, DefaultDnsResponse msg) {
128                                             try {
129                                                 handleQueryResp(msg);
130                                             } finally {
131                                                 ctx.close();
132                                             }
133                                         }
134                                     });
135                         }
136                     });
137 
138             final Channel ch = b.connect(DNS_SERVER_HOST, DNS_SERVER_PORT).sync().channel();
139 
140             int randomID = new Random().nextInt(60000 - 1000) + 1000;
141             DnsQuery query = new DefaultDnsQuery(randomID, DnsOpCode.QUERY)
142                     .setRecord(DnsSection.QUESTION, new DefaultDnsQuestion(QUERY_DOMAIN, DnsRecordType.A));
143             ch.writeAndFlush(query).sync();
144             boolean success = ch.closeFuture().await(10, TimeUnit.SECONDS);
145             if (!success) {
146                 System.err.println("dns query timeout!");
147                 ch.close().sync();
148             }
149         } finally {
150             group.shutdownGracefully();
151         }
152     }
153 
154     private static void handleQueryResp(DefaultDnsResponse msg) {
155         if (msg.count(DnsSection.QUESTION) > 0) {
156             DnsQuestion question = msg.recordAt(DnsSection.QUESTION, 0);
157             System.out.printf("name: %s%n", question.name());
158         }
159         for (int i = 0, count = msg.count(DnsSection.ANSWER); i < count; i++) {
160             DnsRecord record = msg.recordAt(DnsSection.ANSWER, i);
161             if (record.type() == DnsRecordType.A) {
162                 //just print the IP after query
163                 DnsRawRecord raw = (DnsRawRecord) record;
164                 System.out.println(NetUtil.bytesToIpAddress(ByteBufUtil.getBytes(raw.content())));
165             }
166         }
167     }
168 }