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 }