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(msg.id())
83 .append(", ")
84 .append(msg.opCode());
85
86 if (msg.isRecursionDesired()) {
87 buf.append(", RD");
88 }
89 if (msg.z() != 0) {
90 buf.append(", Z: ")
91 .append(msg.z());
92 }
93 buf.append(')');
94 }
95
96 private static void appendResponseHeader(StringBuilder buf, DnsResponse msg) {
97 buf.append(StringUtil.simpleClassName(msg))
98 .append('(');
99
100 appendAddresses(buf, msg)
101 .append(msg.id())
102 .append(", ")
103 .append(msg.opCode())
104 .append(", ")
105 .append(msg.code())
106 .append(',');
107
108 boolean hasComma = true;
109 if (msg.isRecursionDesired()) {
110 hasComma = false;
111 buf.append(" RD");
112 }
113 if (msg.isAuthoritativeAnswer()) {
114 hasComma = false;
115 buf.append(" AA");
116 }
117 if (msg.isTruncated()) {
118 hasComma = false;
119 buf.append(" TC");
120 }
121 if (msg.isRecursionAvailable()) {
122 hasComma = false;
123 buf.append(" RA");
124 }
125 if (msg.z() != 0) {
126 if (!hasComma) {
127 buf.append(',');
128 }
129 buf.append(" Z: ")
130 .append(msg.z());
131 }
132
133 if (hasComma) {
134 buf.setCharAt(buf.length() - 1, ')');
135 } else {
136 buf.append(')');
137 }
138 }
139
140 private static StringBuilder appendAddresses(StringBuilder buf, DnsMessage msg) {
141
142 if (!(msg instanceof AddressedEnvelope)) {
143 return buf;
144 }
145
146 @SuppressWarnings("unchecked")
147 AddressedEnvelope<?, SocketAddress> envelope = (AddressedEnvelope<?, SocketAddress>) msg;
148
149 SocketAddress addr = envelope.sender();
150 if (addr != null) {
151 buf.append("from: ")
152 .append(addr)
153 .append(", ");
154 }
155
156 addr = envelope.recipient();
157 if (addr != null) {
158 buf.append("to: ")
159 .append(addr)
160 .append(", ");
161 }
162
163 return buf;
164 }
165
166 private static void appendAllRecords(StringBuilder buf, DnsMessage msg) {
167 appendRecords(buf, msg, DnsSection.QUESTION);
168 appendRecords(buf, msg, DnsSection.ANSWER);
169 appendRecords(buf, msg, DnsSection.AUTHORITY);
170 appendRecords(buf, msg, DnsSection.ADDITIONAL);
171 }
172
173 private static void appendRecords(StringBuilder buf, DnsMessage message, DnsSection section) {
174 final int count = message.count(section);
175 for (int i = 0; i < count; i ++) {
176 buf.append(StringUtil.NEWLINE)
177 .append(StringUtil.TAB)
178 .append(message.<DnsRecord>recordAt(section, i));
179 }
180 }
181
182 static DnsQuery decodeDnsQuery(DnsRecordDecoder decoder, ByteBuf buf, DnsQueryFactory supplier) throws Exception {
183 DnsQuery query = newQuery(buf, supplier);
184 boolean success = false;
185 try {
186 int questionCount = buf.readUnsignedShort();
187 int answerCount = buf.readUnsignedShort();
188 int authorityRecordCount = buf.readUnsignedShort();
189 int additionalRecordCount = buf.readUnsignedShort();
190 decodeQuestions(decoder, query, buf, questionCount);
191 decodeRecords(decoder, query, DnsSection.ANSWER, buf, answerCount);
192 decodeRecords(decoder, query, DnsSection.AUTHORITY, buf, authorityRecordCount);
193 decodeRecords(decoder, query, DnsSection.ADDITIONAL, buf, additionalRecordCount);
194 success = true;
195 return query;
196 } finally {
197 if (!success) {
198 query.release();
199 }
200 }
201 }
202
203 private static DnsQuery newQuery(ByteBuf buf, DnsQueryFactory supplier) {
204 int id = buf.readUnsignedShort();
205 int flags = buf.readUnsignedShort();
206 if (flags >> 15 == 1) {
207 throw new CorruptedFrameException("not a query");
208 }
209
210 DnsQuery query = supplier.newQuery(id, DnsOpCode.valueOf((byte) (flags >> 11 & 0xf)));
211 query.setRecursionDesired((flags >> 8 & 1) == 1);
212 query.setZ(flags >> 4 & 0x7);
213 return query;
214 }
215
216 private static void decodeQuestions(DnsRecordDecoder decoder,
217 DnsQuery query, ByteBuf buf, int questionCount) throws Exception {
218 for (int i = questionCount; i > 0; --i) {
219 query.addRecord(DnsSection.QUESTION, decoder.decodeQuestion(buf));
220 }
221 }
222
223 private static void decodeRecords(DnsRecordDecoder decoder,
224 DnsQuery query, DnsSection section, ByteBuf buf, int count) throws Exception {
225 for (int i = count; i > 0; --i) {
226 DnsRecord r = decoder.decodeRecord(buf);
227 if (r == null) {
228 break;
229 }
230 query.addRecord(section, r);
231 }
232 }
233
234 static void encodeDnsResponse(DnsRecordEncoder encoder, DnsResponse response, ByteBuf buf) throws Exception {
235 boolean success = false;
236 try {
237 encodeHeader(response, buf);
238 encodeQuestions(encoder, response, buf);
239 encodeRecords(encoder, response, DnsSection.ANSWER, buf);
240 encodeRecords(encoder, response, DnsSection.AUTHORITY, buf);
241 encodeRecords(encoder, response, DnsSection.ADDITIONAL, buf);
242 success = true;
243 } finally {
244 if (!success) {
245 buf.release();
246 }
247 }
248 }
249
250
251
252
253
254
255
256 private static void encodeHeader(DnsResponse response, ByteBuf buf) {
257 buf.writeShort(response.id());
258 int flags = 32768;
259 flags |= (response.opCode().byteValue() & 0xFF) << 11;
260 if (response.isAuthoritativeAnswer()) {
261 flags |= 1 << 10;
262 }
263 if (response.isTruncated()) {
264 flags |= 1 << 9;
265 }
266 if (response.isRecursionDesired()) {
267 flags |= 1 << 8;
268 }
269 if (response.isRecursionAvailable()) {
270 flags |= 1 << 7;
271 }
272 flags |= response.z() << 4;
273 flags |= response.code().intValue();
274 buf.writeShort(flags);
275 buf.writeShort(response.count(DnsSection.QUESTION));
276 buf.writeShort(response.count(DnsSection.ANSWER));
277 buf.writeShort(response.count(DnsSection.AUTHORITY));
278 buf.writeShort(response.count(DnsSection.ADDITIONAL));
279 }
280
281 private static void encodeQuestions(DnsRecordEncoder encoder, DnsResponse response, ByteBuf buf) throws Exception {
282 int count = response.count(DnsSection.QUESTION);
283 for (int i = 0; i < count; ++i) {
284 encoder.encodeQuestion(response.<DnsQuestion>recordAt(DnsSection.QUESTION, i), buf);
285 }
286 }
287
288 private static void encodeRecords(DnsRecordEncoder encoder,
289 DnsResponse response, DnsSection section, ByteBuf buf) throws Exception {
290 int count = response.count(section);
291 for (int i = 0; i < count; ++i) {
292 encoder.encodeRecord(response.recordAt(section, i), buf);
293 }
294 }
295
296 interface DnsQueryFactory {
297 DnsQuery newQuery(int id, DnsOpCode dnsOpCode);
298 }
299
300 private DnsMessageUtil() {
301 }
302 }