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.netty5.example.dns.tcp;
17  
18  import io.netty5.bootstrap.Bootstrap;
19  import io.netty5.bootstrap.ServerBootstrap;
20  import io.netty5.buffer.BufferUtil;
21  import io.netty5.channel.Channel;
22  import io.netty5.channel.ChannelHandlerContext;
23  import io.netty5.channel.ChannelInitializer;
24  import io.netty5.channel.IoHandlerFactory;
25  import io.netty5.channel.MultithreadEventLoopGroup;
26  import io.netty5.channel.SimpleChannelInboundHandler;
27  import io.netty5.channel.nio.NioHandler;
28  import io.netty5.channel.socket.SocketChannel;
29  import io.netty5.channel.socket.nio.NioServerSocketChannel;
30  import io.netty5.channel.socket.nio.NioSocketChannel;
31  import io.netty5.handler.codec.dns.DefaultDnsQuery;
32  import io.netty5.handler.codec.dns.DefaultDnsQuestion;
33  import io.netty5.handler.codec.dns.DefaultDnsRawRecord;
34  import io.netty5.handler.codec.dns.DefaultDnsResponse;
35  import io.netty5.handler.codec.dns.DnsOpCode;
36  import io.netty5.handler.codec.dns.DnsQuery;
37  import io.netty5.handler.codec.dns.DnsQuestion;
38  import io.netty5.handler.codec.dns.DnsRawRecord;
39  import io.netty5.handler.codec.dns.DnsRecord;
40  import io.netty5.handler.codec.dns.DnsRecordType;
41  import io.netty5.handler.codec.dns.DnsSection;
42  import io.netty5.handler.codec.dns.TcpDnsQueryDecoder;
43  import io.netty5.handler.codec.dns.TcpDnsQueryEncoder;
44  import io.netty5.handler.codec.dns.TcpDnsResponseDecoder;
45  import io.netty5.handler.codec.dns.TcpDnsResponseEncoder;
46  import io.netty5.handler.logging.LogLevel;
47  import io.netty5.handler.logging.LoggingHandler;
48  import io.netty5.util.NetUtil;
49  
50  import java.util.Random;
51  import java.util.concurrent.Executors;
52  import java.util.concurrent.TimeUnit;
53  
54  public final class TcpDnsServer {
55      private static final String QUERY_DOMAIN = "www.example.com";
56      private static final int DNS_SERVER_PORT = 53;
57      private static final String DNS_SERVER_HOST = "127.0.0.1";
58      private static final byte[] QUERY_RESULT = {(byte) 192, (byte) 168, 1, 1};
59  
60      public static void main(String[] args) throws Exception {
61          IoHandlerFactory ioHandlerFactory = NioHandler.newFactory();
62          ServerBootstrap bootstrap = new ServerBootstrap()
63                  .group(new MultithreadEventLoopGroup(1, ioHandlerFactory),
64                         new MultithreadEventLoopGroup(ioHandlerFactory))
65                  .channel(NioServerSocketChannel.class)
66                  .handler(new LoggingHandler(LogLevel.INFO))
67                  .childHandler(new ChannelInitializer<>() {
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 messageReceived(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(ctx, msg, question, 600, QUERY_RESULT));
80                                      }
81  
82                                      private DefaultDnsResponse newResponse(ChannelHandlerContext ctx,
83                                                                             DnsQuery query,
84                                                                             DnsQuestion question,
85                                                                             long ttl, byte[]... addresses) {
86                                          DefaultDnsResponse response = new DefaultDnsResponse(query.id());
87                                          response.addRecord(DnsSection.QUESTION, question);
88  
89                                          for (byte[] address : addresses) {
90                                              DefaultDnsRawRecord queryAnswer = new DefaultDnsRawRecord(
91                                                      question.name(),
92                                                      DnsRecordType.A, ttl, ctx.bufferAllocator().copyOf(address));
93                                              response.addRecord(DnsSection.ANSWER, queryAnswer);
94                                          }
95                                          return response;
96                                      }
97                                  });
98                      }
99                  });
100         final Channel channel = bootstrap.bind(DNS_SERVER_PORT).asStage().get();
101         Executors.newSingleThreadScheduledExecutor().schedule(() -> {
102             try {
103                 clientQuery();
104                 channel.close();
105             } catch (Exception e) {
106                 e.printStackTrace();
107             }
108         }, 1000, TimeUnit.MILLISECONDS);
109         channel.closeFuture().asStage().sync();
110     }
111 
112     // copy from TcpDnsClient.java
113     private static void clientQuery() throws Exception {
114         MultithreadEventLoopGroup group = new MultithreadEventLoopGroup(NioHandler.newFactory());
115         try {
116             Bootstrap b = new Bootstrap();
117             b.group(group)
118                     .channel(NioSocketChannel.class)
119                     .handler(new ChannelInitializer<SocketChannel>() {
120                         @Override
121                         protected void initChannel(SocketChannel ch) {
122                             ch.pipeline().addLast(new TcpDnsQueryEncoder())
123                                     .addLast(new TcpDnsResponseDecoder())
124                                     .addLast(new SimpleChannelInboundHandler<DefaultDnsResponse>() {
125                                         @Override
126                                         protected void messageReceived(
127                                                 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).asStage().get();
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).asStage().sync();
144             boolean success = ch.closeFuture().asStage().await(10, TimeUnit.SECONDS);
145             if (!success) {
146                 System.err.println("dns query timeout!");
147                 ch.close().asStage().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(BufferUtil.getBytes(raw.content())));
165             }
166         }
167     }
168 }