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