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