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