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 }