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 }