View Javadoc
1   /*
2    * Copyright 2013 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    *   http://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.handler.codec.dns;
17  
18  import io.netty.buffer.ByteBuf;
19  import io.netty.channel.ChannelHandler;
20  import io.netty.channel.ChannelHandlerContext;
21  import io.netty.channel.socket.DatagramPacket;
22  import io.netty.handler.codec.MessageToMessageEncoder;
23  import io.netty.util.CharsetUtil;
24  import io.netty.util.internal.StringUtil;
25  
26  import java.nio.charset.Charset;
27  import java.util.List;
28  
29  /**
30   * DnsQueryEncoder accepts {@link DnsQuery} and encodes to {@link ByteBuf}. This
31   * class also contains methods for encoding parts of DnsQuery's such as the
32   * header and questions.
33   */
34  @ChannelHandler.Sharable
35  public class DnsQueryEncoder extends MessageToMessageEncoder<DnsQuery> {
36  
37      @Override
38      protected void encode(ChannelHandlerContext ctx, DnsQuery query, List<Object> out) throws Exception {
39          ByteBuf buf = ctx.alloc().buffer();
40          encodeHeader(query.header(), buf);
41          List<DnsQuestion> questions = query.questions();
42          for (DnsQuestion question : questions) {
43              encodeQuestion(question, CharsetUtil.US_ASCII, buf);
44          }
45          for (DnsResource resource: query.additionalResources()) {
46              encodeResource(resource, CharsetUtil.US_ASCII, buf);
47          }
48          out.add(new DatagramPacket(buf, query.recipient(), null));
49      }
50  
51      /**
52       * Encodes the information in a {@link DnsHeader} and writes it to the
53       * specified {@link ByteBuf}. The header is always 12 bytes long.
54       *
55       * @param header
56       *            the query header being encoded
57       * @param buf
58       *            the buffer the encoded data should be written to
59       */
60      private static void encodeHeader(DnsHeader header, ByteBuf buf) {
61          buf.writeShort(header.id());
62          int flags = 0;
63          flags |= header.type() << 15;
64          flags |= header.opcode() << 14;
65          flags |= header.isRecursionDesired() ? 1 << 8 : 0;
66          buf.writeShort(flags);
67          buf.writeShort(header.questionCount());
68          buf.writeShort(0); // answerCount
69          buf.writeShort(0); // authorityResourceCount
70          buf.writeShort(header.additionalResourceCount());
71      }
72  
73      /**
74       * Encodes the information in a {@link DnsQuestion} and writes it to the
75       * specified {@link ByteBuf}.
76       *
77       * @param question
78       *            the question being encoded
79       * @param charset
80       *            charset names are encoded in
81       * @param buf
82       *            the buffer the encoded data should be written to
83       */
84      private static void encodeQuestion(DnsQuestion question, Charset charset, ByteBuf buf) {
85          encodeName(question.name(), charset, buf);
86          buf.writeShort(question.type().intValue());
87          buf.writeShort(question.dnsClass().intValue());
88      }
89  
90      private static void encodeResource(DnsResource resource, Charset charset, ByteBuf buf) {
91          encodeName(resource.name(), charset, buf);
92  
93          buf.writeShort(resource.type().intValue());
94          buf.writeShort(resource.dnsClass().intValue());
95          buf.writeInt((int) resource.timeToLive());
96  
97          ByteBuf content = resource.content();
98          int contentLen = content.readableBytes();
99  
100         buf.writeShort(contentLen);
101         buf.writeBytes(content, content.readerIndex(), contentLen);
102     }
103 
104     private static void encodeName(String name, Charset charset, ByteBuf buf) {
105         String[] parts = StringUtil.split(name, '.');
106         for (String part: parts) {
107             final int partLen = part.length();
108             if (partLen == 0) {
109                 continue;
110             }
111             buf.writeByte(partLen);
112             buf.writeBytes(part.getBytes(charset));
113         }
114         buf.writeByte(0); // marks end of name field
115     }
116 }