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