1
2
3
4
5
6
7
8
9
10
11
12
13
14
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.channel.AddressedEnvelope;
21 import io.netty5.handler.codec.CorruptedFrameException;
22 import io.netty5.util.internal.StringUtil;
23
24 import java.net.SocketAddress;
25
26
27
28
29 final class DnsMessageUtil {
30
31 static StringBuilder appendQuery(StringBuilder buf, DnsQuery query) {
32 appendQueryHeader(buf, query);
33 appendAllRecords(buf, query);
34 return buf;
35 }
36
37 static StringBuilder appendResponse(StringBuilder buf, DnsResponse response) {
38 appendResponseHeader(buf, response);
39 appendAllRecords(buf, response);
40 return buf;
41 }
42
43 static StringBuilder appendRecordClass(StringBuilder buf, int dnsClass) {
44 final String name;
45 switch (dnsClass &= 0xFFFF) {
46 case DnsRecord.CLASS_IN:
47 name = "IN";
48 break;
49 case DnsRecord.CLASS_CSNET:
50 name = "CSNET";
51 break;
52 case DnsRecord.CLASS_CHAOS:
53 name = "CHAOS";
54 break;
55 case DnsRecord.CLASS_HESIOD:
56 name = "HESIOD";
57 break;
58 case DnsRecord.CLASS_NONE:
59 name = "NONE";
60 break;
61 case DnsRecord.CLASS_ANY:
62 name = "ANY";
63 break;
64 default:
65 name = null;
66 break;
67 }
68
69 if (name != null) {
70 buf.append(name);
71 } else {
72 buf.append("UNKNOWN(").append(dnsClass).append(')');
73 }
74
75 return buf;
76 }
77
78 private static void appendQueryHeader(StringBuilder buf, DnsQuery msg) {
79 buf.append(StringUtil.simpleClassName(msg))
80 .append('(');
81
82 appendAddresses(buf, msg)
83 .append(msg.id())
84 .append(", ")
85 .append(msg.opCode());
86
87 if (msg.isRecursionDesired()) {
88 buf.append(", RD");
89 }
90 if (msg.z() != 0) {
91 buf.append(", Z: ")
92 .append(msg.z());
93 }
94 buf.append(')');
95 }
96
97 private static void appendResponseHeader(StringBuilder buf, DnsResponse msg) {
98 buf.append(StringUtil.simpleClassName(msg))
99 .append('(');
100
101 appendAddresses(buf, msg)
102 .append(msg.id())
103 .append(", ")
104 .append(msg.opCode())
105 .append(", ")
106 .append(msg.code())
107 .append(',');
108
109 boolean hasComma = true;
110 if (msg.isRecursionDesired()) {
111 hasComma = false;
112 buf.append(" RD");
113 }
114 if (msg.isAuthoritativeAnswer()) {
115 hasComma = false;
116 buf.append(" AA");
117 }
118 if (msg.isTruncated()) {
119 hasComma = false;
120 buf.append(" TC");
121 }
122 if (msg.isRecursionAvailable()) {
123 hasComma = false;
124 buf.append(" RA");
125 }
126 if (msg.z() != 0) {
127 if (!hasComma) {
128 buf.append(',');
129 }
130 buf.append(" Z: ")
131 .append(msg.z());
132 }
133
134 if (hasComma) {
135 buf.setCharAt(buf.length() - 1, ')');
136 } else {
137 buf.append(')');
138 }
139 }
140
141 private static StringBuilder appendAddresses(StringBuilder buf, DnsMessage msg) {
142
143 if (!(msg instanceof AddressedEnvelope)) {
144 return buf;
145 }
146
147 @SuppressWarnings("unchecked")
148 AddressedEnvelope<?, SocketAddress> envelope = (AddressedEnvelope<?, SocketAddress>) msg;
149
150 SocketAddress addr = envelope.sender();
151 if (addr != null) {
152 buf.append("from: ")
153 .append(addr)
154 .append(", ");
155 }
156
157 addr = envelope.recipient();
158 if (addr != null) {
159 buf.append("to: ")
160 .append(addr)
161 .append(", ");
162 }
163
164 return buf;
165 }
166
167 private static void appendAllRecords(StringBuilder buf, DnsMessage msg) {
168 appendRecords(buf, msg, DnsSection.QUESTION);
169 appendRecords(buf, msg, DnsSection.ANSWER);
170 appendRecords(buf, msg, DnsSection.AUTHORITY);
171 appendRecords(buf, msg, DnsSection.ADDITIONAL);
172 }
173
174 private static void appendRecords(StringBuilder buf, DnsMessage message, DnsSection section) {
175 final int count = message.count(section);
176 for (int i = 0; i < count; i ++) {
177 buf.append(StringUtil.NEWLINE)
178 .append(StringUtil.TAB)
179 .append(message.<DnsRecord>recordAt(section, i));
180 }
181 }
182
183 static DnsQuery decodeDnsQuery(DnsRecordDecoder decoder,
184 BufferAllocator allocator, Buffer buf,
185 DnsQueryFactory supplier) throws Exception {
186 DnsQuery query = newQuery(buf, supplier);
187 boolean success = false;
188 try {
189 int questionCount = buf.readUnsignedShort();
190 int answerCount = buf.readUnsignedShort();
191 int authorityRecordCount = buf.readUnsignedShort();
192 int additionalRecordCount = buf.readUnsignedShort();
193 decodeQuestions(decoder, query, buf, questionCount);
194 decodeRecords(decoder, query, DnsSection.ANSWER, allocator, buf, answerCount);
195 decodeRecords(decoder, query, DnsSection.AUTHORITY, allocator, buf, authorityRecordCount);
196 decodeRecords(decoder, query, DnsSection.ADDITIONAL, allocator, buf, additionalRecordCount);
197 success = true;
198 return query;
199 } finally {
200 if (!success) {
201 query.release();
202 }
203 }
204 }
205
206 private static DnsQuery newQuery(Buffer buf, DnsQueryFactory supplier) {
207 int id = buf.readUnsignedShort();
208 int flags = buf.readUnsignedShort();
209 if (flags >> 15 == 1) {
210 throw new CorruptedFrameException("not a query");
211 }
212
213 DnsQuery query = supplier.newQuery(id, DnsOpCode.valueOf((byte) (flags >> 11 & 0xf)));
214 query.setRecursionDesired((flags >> 8 & 1) == 1);
215 query.setZ(flags >> 4 & 0x7);
216 return query;
217 }
218
219 private static void decodeQuestions(DnsRecordDecoder decoder,
220 DnsQuery query, Buffer buf, int questionCount) throws Exception {
221 for (int i = questionCount; i > 0; --i) {
222 query.addRecord(DnsSection.QUESTION, decoder.decodeQuestion(buf));
223 }
224 }
225
226 private static void decodeRecords(DnsRecordDecoder decoder,
227 DnsQuery query, DnsSection section,
228 BufferAllocator allocator, Buffer buf,
229 int count) throws Exception {
230 for (int i = count; i > 0; --i) {
231 DnsRecord r = decoder.decodeRecord(allocator, buf);
232 if (r == null) {
233 break;
234 }
235 query.addRecord(section, r);
236 }
237 }
238
239 static void encodeDnsResponse(DnsRecordEncoder encoder, DnsResponse response, Buffer buf) throws Exception {
240 boolean success = false;
241 try {
242 encodeHeader(response, buf);
243 encodeQuestions(encoder, response, buf);
244 encodeRecords(encoder, response, DnsSection.ANSWER, buf);
245 encodeRecords(encoder, response, DnsSection.AUTHORITY, buf);
246 encodeRecords(encoder, response, DnsSection.ADDITIONAL, buf);
247 success = true;
248 } finally {
249 if (!success) {
250 buf.close();
251 }
252 }
253 }
254
255
256
257
258
259
260
261 private static void encodeHeader(DnsResponse response, Buffer buf) {
262 buf.writeShort((short) response.id());
263 int flags = 32768;
264 flags |= (response.opCode().byteValue() & 0xFF) << 11;
265 if (response.isAuthoritativeAnswer()) {
266 flags |= 1 << 10;
267 }
268 if (response.isTruncated()) {
269 flags |= 1 << 9;
270 }
271 if (response.isRecursionDesired()) {
272 flags |= 1 << 8;
273 }
274 if (response.isRecursionAvailable()) {
275 flags |= 1 << 7;
276 }
277 flags |= response.z() << 4;
278 flags |= response.code().intValue();
279 buf.writeShort((short) flags);
280 buf.writeShort((short) response.count(DnsSection.QUESTION));
281 buf.writeShort((short) response.count(DnsSection.ANSWER));
282 buf.writeShort((short) response.count(DnsSection.AUTHORITY));
283 buf.writeShort((short) response.count(DnsSection.ADDITIONAL));
284 }
285
286 private static void encodeQuestions(DnsRecordEncoder encoder, DnsResponse response, Buffer buf) throws Exception {
287 int count = response.count(DnsSection.QUESTION);
288 for (int i = 0; i < count; ++i) {
289 encoder.encodeQuestion(response.recordAt(DnsSection.QUESTION, i), buf);
290 }
291 }
292
293 private static void encodeRecords(DnsRecordEncoder encoder,
294 DnsResponse response, DnsSection section, Buffer buf) throws Exception {
295 int count = response.count(section);
296 for (int i = 0; i < count; ++i) {
297 encoder.encodeRecord(response.recordAt(section, i), buf);
298 }
299 }
300
301 interface DnsQueryFactory {
302 DnsQuery newQuery(int id, DnsOpCode dnsOpCode);
303 }
304
305 private DnsMessageUtil() {
306 }
307 }