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