1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17 package io.netty.handler.codec.dns;
18
19 import io.netty.buffer.ByteBuf;
20 import io.netty.buffer.ByteBufUtil;
21 import io.netty.handler.codec.CorruptedFrameException;
22 import io.netty.handler.codec.TooLongFrameException;
23 import io.netty.util.CharsetUtil;
24 import io.netty.util.internal.PlatformDependent;
25
26 import static io.netty.handler.codec.dns.DefaultDnsRecordDecoder.*;
27
28 final class DnsCodecUtil {
29 private DnsCodecUtil() {
30
31 }
32
33 static void encodeDomainName(String name, ByteBuf buf) {
34 if (ROOT.equals(name)) {
35
36 buf.writeByte(0);
37 return;
38 }
39
40 int totalLength = 0;
41 final String[] labels = name.split("\\.");
42 for (int i = 0; i < labels.length; i++) {
43 String label = labels[i];
44 final int labelLen = label.length();
45 if (labelLen == 0) {
46 if (i == labels.length - 1) {
47
48 break;
49 } else {
50 throw new IllegalArgumentException("DNS name contains empty label: " + name);
51 }
52 }
53 if (labelLen > 63) {
54 throw new IllegalArgumentException(
55 "DNS label length " + labelLen + " exceeds maximum of 63: " + name);
56 }
57 int idx = label.indexOf('\0');
58 if (idx != -1) {
59 throw new IllegalArgumentException(
60 "DNS label contains null byte at index " + idx);
61 }
62 totalLength += 1 + labelLen;
63 if (totalLength > 255) {
64 throw new IllegalArgumentException(
65 "DNS name exceeds maximum length of 255: " + name);
66 }
67 buf.writeByte(labelLen);
68 ByteBufUtil.writeAscii(buf, label);
69 }
70
71 buf.writeByte(0);
72 }
73
74 static String decodeDomainName(ByteBuf in) {
75 int position = -1;
76 int checked = 0;
77 final int end = in.writerIndex();
78 final int readable = in.readableBytes();
79
80
81
82
83
84
85
86
87 if (readable == 0) {
88 return ROOT;
89 }
90
91 final StringBuilder name = new StringBuilder(readable << 1);
92 while (in.isReadable()) {
93 final int len = in.readUnsignedByte();
94 final boolean pointer = (len & 0xc0) == 0xc0;
95 if (pointer) {
96 if (position == -1) {
97 position = in.readerIndex() + 1;
98 }
99
100 if (!in.isReadable()) {
101 throw new CorruptedFrameException("truncated pointer in a name");
102 }
103
104 final int next = (len & 0x3f) << 8 | in.readUnsignedByte();
105 if (next >= end) {
106 throw new CorruptedFrameException("name has an out-of-range pointer");
107 }
108 in.readerIndex(next);
109
110
111 checked += 2;
112 if (checked >= end) {
113 throw new CorruptedFrameException("name contains a loop.");
114 }
115 } else if (len != 0) {
116 if (!in.isReadable(len)) {
117 throw new CorruptedFrameException("truncated label in a name");
118 }
119
120 if (len > 63) {
121 throw new TooLongFrameException("label must be <= 63 but was " + len);
122 }
123 name.append(in.toString(in.readerIndex(), len, CharsetUtil.UTF_8)).append('.');
124 in.skipBytes(len);
125
126 if (name.length() > 255) {
127 throw new TooLongFrameException("domain name must be <= 255 but was " + name.length());
128 }
129 } else {
130 break;
131 }
132 }
133
134 if (position != -1) {
135 in.readerIndex(position);
136 }
137
138 if (name.length() == 0) {
139 return ROOT;
140 }
141
142 if (name.charAt(name.length() - 1) != '.') {
143 name.append('.');
144 }
145
146 return name.toString();
147 }
148
149
150
151
152
153
154 static ByteBuf decompressDomainName(ByteBuf compression) {
155 String domainName = decodeDomainName(compression);
156 ByteBuf result = compression.alloc().buffer(domainName.length() << 1);
157 try {
158 encodeDomainName(domainName, result);
159 } catch (Throwable cause) {
160 result.release();
161 PlatformDependent.throwException(cause);
162 }
163 return result;
164 }
165 }