View Javadoc
1   /*
2    * Copyright 2015 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.handler.codec.dns;
17  
18  import io.netty.buffer.ByteBuf;
19  
20  /**
21   * The default {@link DnsRecordDecoder} implementation.
22   *
23   * @see DefaultDnsRecordEncoder
24   */
25  public class DefaultDnsRecordDecoder implements DnsRecordDecoder {
26  
27      static final String ROOT = ".";
28  
29      /**
30       * Creates a new instance.
31       */
32      protected DefaultDnsRecordDecoder() { }
33  
34      @Override
35      public final DnsQuestion decodeQuestion(ByteBuf in) throws Exception {
36          String name = decodeName(in);
37          DnsRecordType type = DnsRecordType.valueOf(in.readUnsignedShort());
38          int qClass = in.readUnsignedShort();
39          return new DefaultDnsQuestion(name, type, qClass);
40      }
41  
42      @Override
43      public final <T extends DnsRecord> T decodeRecord(ByteBuf in) throws Exception {
44          final int startOffset = in.readerIndex();
45          final String name = decodeName(in);
46  
47          final int endOffset = in.writerIndex();
48          if (endOffset - in.readerIndex() < 10) {
49              // Not enough data
50              in.readerIndex(startOffset);
51              return null;
52          }
53  
54          final DnsRecordType type = DnsRecordType.valueOf(in.readUnsignedShort());
55          final int aClass = in.readUnsignedShort();
56          final long ttl = in.readUnsignedInt();
57          final int length = in.readUnsignedShort();
58          final int offset = in.readerIndex();
59  
60          if (endOffset - offset < length) {
61              // Not enough data
62              in.readerIndex(startOffset);
63              return null;
64          }
65  
66          @SuppressWarnings("unchecked")
67          T record = (T) decodeRecord(name, type, aClass, ttl, in, offset, length);
68          in.readerIndex(offset + length);
69          return record;
70      }
71  
72      /**
73       * Decodes a record from the information decoded so far by {@link #decodeRecord(ByteBuf)}.
74       *
75       * @param name the domain name of the record
76       * @param type the type of the record
77       * @param dnsClass the class of the record
78       * @param timeToLive the TTL of the record
79       * @param in the {@link ByteBuf} that contains the RDATA
80       * @param offset the start offset of the RDATA in {@code in}
81       * @param length the length of the RDATA
82       *
83       * @return a {@link DnsRawRecord}. Override this method to decode RDATA and return other record implementation.
84       */
85      protected DnsRecord decodeRecord(
86              String name, DnsRecordType type, int dnsClass, long timeToLive,
87              ByteBuf in, int offset, int length) throws Exception {
88  
89          // DNS message compression means that domain names may contain "pointers" to other positions in the packet
90          // to build a full message. This means the indexes are meaningful and we need the ability to reference the
91          // indexes un-obstructed, and thus we cannot use a slice here.
92          // See https://www.ietf.org/rfc/rfc1035 [4.1.4. Message compression]
93          if (type == DnsRecordType.PTR) {
94              return new DefaultDnsPtrRecord(
95                      name, dnsClass, timeToLive, decodeName0(in.duplicate().setIndex(offset, offset + length)));
96          }
97          if (type == DnsRecordType.CNAME || type == DnsRecordType.NS) {
98              return new DefaultDnsRawRecord(name, type, dnsClass, timeToLive,
99                                             DnsCodecUtil.decompressDomainName(
100                                                    in.duplicate().setIndex(offset, offset + length)));
101         }
102         return new DefaultDnsRawRecord(
103                 name, type, dnsClass, timeToLive, in.retainedDuplicate().setIndex(offset, offset + length));
104     }
105 
106     /**
107      * Retrieves a domain name given a buffer containing a DNS packet. If the
108      * name contains a pointer, the position of the buffer will be set to
109      * directly after the pointer's index after the name has been read.
110      *
111      * @param in the byte buffer containing the DNS packet
112      * @return the domain name for an entry
113      */
114     protected String decodeName0(ByteBuf in) {
115         return decodeName(in);
116     }
117 
118     /**
119      * Retrieves a domain name given a buffer containing a DNS packet. If the
120      * name contains a pointer, the position of the buffer will be set to
121      * directly after the pointer's index after the name has been read.
122      *
123      * @param in the byte buffer containing the DNS packet
124      * @return the domain name for an entry
125      */
126     public static String decodeName(ByteBuf in) {
127         return DnsCodecUtil.decodeDomainName(in);
128     }
129 }