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