1
2
3
4
5 package javax.jmdns.impl;
6
7 import java.io.ByteArrayInputStream;
8 import java.io.IOException;
9 import java.net.DatagramPacket;
10 import java.net.InetAddress;
11 import java.util.HashMap;
12 import java.util.Map;
13 import java.util.logging.Level;
14 import java.util.logging.Logger;
15
16 import javax.jmdns.impl.constants.DNSConstants;
17 import javax.jmdns.impl.constants.DNSLabel;
18 import javax.jmdns.impl.constants.DNSOptionCode;
19 import javax.jmdns.impl.constants.DNSRecordClass;
20 import javax.jmdns.impl.constants.DNSRecordType;
21 import javax.jmdns.impl.constants.DNSResultCode;
22
23
24
25
26
27
28 public final class DNSIncoming extends DNSMessage {
29 private static Logger logger = Logger.getLogger(DNSIncoming.class.getName());
30
31
32
33 public static boolean USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET = true;
34
35 public static class MessageInputStream extends ByteArrayInputStream {
36 private static Logger logger1 = Logger.getLogger(MessageInputStream.class.getName());
37
38 final Map<Integer, String> _names;
39
40 public MessageInputStream(byte[] buffer, int length) {
41 this(buffer, 0, length);
42 }
43
44
45
46
47
48
49 public MessageInputStream(byte[] buffer, int offset, int length) {
50 super(buffer, offset, length);
51 _names = new HashMap<Integer, String>();
52 }
53
54 public int readByte() {
55 return this.read();
56 }
57
58 public int readUnsignedShort() {
59 return (this.read() << 8) | this.read();
60 }
61
62 public int readInt() {
63 return (this.readUnsignedShort() << 16) | this.readUnsignedShort();
64 }
65
66 public byte[] readBytes(int len) {
67 byte bytes[] = new byte[len];
68 this.read(bytes, 0, len);
69 return bytes;
70 }
71
72 public String readUTF(int len) {
73 StringBuilder buffer = new StringBuilder(len);
74 for (int index = 0; index < len; index++) {
75 int ch = this.read();
76 switch (ch >> 4) {
77 case 0:
78 case 1:
79 case 2:
80 case 3:
81 case 4:
82 case 5:
83 case 6:
84 case 7:
85
86 break;
87 case 12:
88 case 13:
89
90 ch = ((ch & 0x1F) << 6) | (this.read() & 0x3F);
91 index++;
92 break;
93 case 14:
94
95 ch = ((ch & 0x0f) << 12) | ((this.read() & 0x3F) << 6) | (this.read() & 0x3F);
96 index++;
97 index++;
98 break;
99 default:
100
101 ch = ((ch & 0x3F) << 4) | (this.read() & 0x0f);
102 index++;
103 break;
104 }
105 buffer.append((char) ch);
106 }
107 return buffer.toString();
108 }
109
110 protected synchronized int peek() {
111 return (pos < count) ? (buf[pos] & 0xff) : -1;
112 }
113
114 public String readName() {
115 Map<Integer, StringBuilder> names = new HashMap<Integer, StringBuilder>();
116 StringBuilder buffer = new StringBuilder();
117 boolean finished = false;
118 while (!finished) {
119 int len = this.read();
120 if (len == 0) {
121 finished = true;
122 break;
123 }
124 switch (DNSLabel.labelForByte(len)) {
125 case Standard:
126 int offset = pos - 1;
127 String label = this.readUTF(len) + ".";
128 buffer.append(label);
129 for (StringBuilder previousLabel : names.values()) {
130 previousLabel.append(label);
131 }
132 names.put(Integer.valueOf(offset), new StringBuilder(label));
133 break;
134 case Compressed:
135 int index = (DNSLabel.labelValue(len) << 8) | this.read();
136 String compressedLabel = _names.get(Integer.valueOf(index));
137 if (compressedLabel == null) {
138 logger1.severe("bad domain name: possible circular name detected. Bad offset: 0x" + Integer.toHexString(index) + " at 0x" + Integer.toHexString(pos - 2));
139 compressedLabel = "";
140 }
141 buffer.append(compressedLabel);
142 for (StringBuilder previousLabel : names.values()) {
143 previousLabel.append(compressedLabel);
144 }
145 finished = true;
146 break;
147 case Extended:
148
149 logger1.severe("Extended label are not currently supported.");
150 break;
151 case Unknown:
152 default:
153 logger1.severe("unsupported dns label type: '" + Integer.toHexString(len & 0xC0) + "'");
154 }
155 }
156 for (Integer index : names.keySet()) {
157 _names.put(index, names.get(index).toString());
158 }
159 return buffer.toString();
160 }
161
162 public String readNonNameString() {
163 int len = this.read();
164 return this.readUTF(len);
165 }
166
167 }
168
169 private final DatagramPacket _packet;
170
171 private final long _receivedTime;
172
173 private final MessageInputStream _messageInputStream;
174
175 private int _senderUDPPayload;
176
177
178
179
180
181
182
183 public DNSIncoming(DatagramPacket packet) throws IOException {
184 super(0, 0, packet.getPort() == DNSConstants.MDNS_PORT);
185 this._packet = packet;
186 InetAddress source = packet.getAddress();
187 this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
188 this._receivedTime = System.currentTimeMillis();
189 this._senderUDPPayload = DNSConstants.MAX_MSG_TYPICAL;
190
191 try {
192 this.setId(_messageInputStream.readUnsignedShort());
193 this.setFlags(_messageInputStream.readUnsignedShort());
194 int numQuestions = _messageInputStream.readUnsignedShort();
195 int numAnswers = _messageInputStream.readUnsignedShort();
196 int numAuthorities = _messageInputStream.readUnsignedShort();
197 int numAdditionals = _messageInputStream.readUnsignedShort();
198
199
200 if (numQuestions > 0) {
201 for (int i = 0; i < numQuestions; i++) {
202 _questions.add(this.readQuestion());
203 }
204 }
205
206
207 if (numAnswers > 0) {
208 for (int i = 0; i < numAnswers; i++) {
209 DNSRecord rec = this.readAnswer(source);
210 if (rec != null) {
211
212 _answers.add(rec);
213 }
214 }
215 }
216
217 if (numAuthorities > 0) {
218 for (int i = 0; i < numAuthorities; i++) {
219 DNSRecord rec = this.readAnswer(source);
220 if (rec != null) {
221
222 _authoritativeAnswers.add(rec);
223 }
224 }
225 }
226
227 if (numAdditionals > 0) {
228 for (int i = 0; i < numAdditionals; i++) {
229 DNSRecord rec = this.readAnswer(source);
230 if (rec != null) {
231
232 _additionals.add(rec);
233 }
234 }
235 }
236 } catch (Exception e) {
237 logger.log(Level.WARNING, "DNSIncoming() dump " + print(true) + "\n exception ", e);
238
239 IOException ioe = new IOException("DNSIncoming corrupted message");
240 ioe.initCause(e);
241 throw ioe;
242 }
243 }
244
245 private DNSIncoming(int flags, int id, boolean multicast, DatagramPacket packet, long receivedTime) {
246 super(flags, id, multicast);
247 this._packet = packet;
248 this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
249 this._receivedTime = receivedTime;
250 }
251
252
253
254
255
256
257
258 @Override
259 public DNSIncoming clone() {
260 DNSIncoming in = new DNSIncoming(this.getFlags(), this.getId(), this.isMulticast(), this._packet, this._receivedTime);
261 in._senderUDPPayload = this._senderUDPPayload;
262 in._questions.addAll(this._questions);
263 in._answers.addAll(this._answers);
264 in._authoritativeAnswers.addAll(this._authoritativeAnswers);
265 in._additionals.addAll(this._additionals);
266 return in;
267 }
268
269
270 private DNSQuestion readQuestion() {
271 String domain = _messageInputStream.readName();
272 DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
273 if (type == DNSRecordType.TYPE_IGNORE) {
274 logger.log(Level.SEVERE, "Could not find record type: " + this.print(true));
275 }
276 int recordClassIndex = _messageInputStream.readUnsignedShort();
277 DNSRecordClass recordClass = DNSRecordClass.classForIndex(recordClassIndex);
278 boolean unique = recordClass.isUnique(recordClassIndex);
279 return DNSQuestion.newQuestion(domain, type, recordClass, unique);
280 }
281
282 private DNSRecord readAnswer(InetAddress source) {
283 String domain = _messageInputStream.readName();
284 DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
285 if (type == DNSRecordType.TYPE_IGNORE) {
286 logger.log(Level.SEVERE, "Could not find record type. domain: " + domain + "\n" + this.print(true));
287 }
288 int recordClassIndex = _messageInputStream.readUnsignedShort();
289 DNSRecordClass recordClass = (type == DNSRecordType.TYPE_OPT ? DNSRecordClass.CLASS_UNKNOWN : DNSRecordClass.classForIndex(recordClassIndex));
290 if ((recordClass == DNSRecordClass.CLASS_UNKNOWN) && (type != DNSRecordType.TYPE_OPT)) {
291 logger.log(Level.SEVERE, "Could not find record class. domain: " + domain + " type: " + type + "\n" + this.print(true));
292 }
293 boolean unique = recordClass.isUnique(recordClassIndex);
294 int ttl = _messageInputStream.readInt();
295 int len = _messageInputStream.readUnsignedShort();
296 DNSRecord rec = null;
297
298 switch (type) {
299 case TYPE_A:
300 rec = new DNSRecord.IPv4Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
301 break;
302 case TYPE_AAAA:
303 rec = new DNSRecord.IPv6Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
304 break;
305 case TYPE_CNAME:
306 case TYPE_PTR:
307 String service = "";
308 service = _messageInputStream.readName();
309 if (service.length() > 0) {
310 rec = new DNSRecord.Pointer(domain, recordClass, unique, ttl, service);
311 } else {
312 logger.log(Level.WARNING, "PTR record of class: " + recordClass + ", there was a problem reading the service name of the answer for domain:" + domain);
313 }
314 break;
315 case TYPE_TXT:
316 rec = new DNSRecord.Text(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
317 break;
318 case TYPE_SRV:
319 int priority = _messageInputStream.readUnsignedShort();
320 int weight = _messageInputStream.readUnsignedShort();
321 int port = _messageInputStream.readUnsignedShort();
322 String target = "";
323
324
325 if (USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) {
326 target = _messageInputStream.readName();
327 } else {
328
329 target = _messageInputStream.readNonNameString();
330 }
331 rec = new DNSRecord.Service(domain, recordClass, unique, ttl, priority, weight, port, target);
332 break;
333 case TYPE_HINFO:
334 StringBuilder buf = new StringBuilder();
335 buf.append(_messageInputStream.readUTF(len));
336 int index = buf.indexOf(" ");
337 String cpu = (index > 0 ? buf.substring(0, index) : buf.toString()).trim();
338 String os = (index > 0 ? buf.substring(index + 1) : "").trim();
339 rec = new DNSRecord.HostInformation(domain, recordClass, unique, ttl, cpu, os);
340 break;
341 case TYPE_OPT:
342 DNSResultCode extendedResultCode = DNSResultCode.resultCodeForFlags(this.getFlags(), ttl);
343 int version = (ttl & 0x00ff0000) >> 16;
344 if (version == 0) {
345 _senderUDPPayload = recordClassIndex;
346 while (_messageInputStream.available() > 0) {
347
348 int optionCodeInt = 0;
349 DNSOptionCode optionCode = null;
350 if (_messageInputStream.available() >= 2) {
351 optionCodeInt = _messageInputStream.readUnsignedShort();
352 optionCode = DNSOptionCode.resultCodeForFlags(optionCodeInt);
353 } else {
354 logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
355 break;
356 }
357 int optionLength = 0;
358 if (_messageInputStream.available() >= 2) {
359 optionLength = _messageInputStream.readUnsignedShort();
360 } else {
361 logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
362 break;
363 }
364 byte[] optiondata = new byte[0];
365 if (_messageInputStream.available() >= optionLength) {
366 optiondata = _messageInputStream.readBytes(optionLength);
367 }
368
369
370 switch (optionCode) {
371 case Owner:
372
373
374
375
376
377 int ownerVersion = 0;
378 int ownerSequence = 0;
379 byte[] ownerPrimaryMacAddress = null;
380 byte[] ownerWakeupMacAddress = null;
381 byte[] ownerPassword = null;
382 try {
383 ownerVersion = optiondata[0];
384 ownerSequence = optiondata[1];
385 ownerPrimaryMacAddress = new byte[] { optiondata[2], optiondata[3], optiondata[4], optiondata[5], optiondata[6], optiondata[7] };
386 ownerWakeupMacAddress = ownerPrimaryMacAddress;
387 if (optiondata.length > 8) {
388
389 ownerWakeupMacAddress = new byte[] { optiondata[8], optiondata[9], optiondata[10], optiondata[11], optiondata[12], optiondata[13] };
390 }
391 if (optiondata.length == 18) {
392
393 ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17] };
394 }
395 if (optiondata.length == 22) {
396
397 ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17], optiondata[18], optiondata[19], optiondata[20], optiondata[21] };
398 }
399 } catch (Exception exception) {
400 logger.warning("Malformed OPT answer. Option code: Owner data: " + this._hexString(optiondata));
401 }
402 if (logger.isLoggable(Level.FINE)) {
403 logger.fine("Unhandled Owner OPT version: " + ownerVersion + " sequence: " + ownerSequence + " MAC address: " + this._hexString(ownerPrimaryMacAddress)
404 + (ownerWakeupMacAddress != ownerPrimaryMacAddress ? " wakeup MAC address: " + this._hexString(ownerWakeupMacAddress) : "") + (ownerPassword != null ? " password: " + this._hexString(ownerPassword) : ""));
405 }
406 break;
407 case LLQ:
408 case NSID:
409 case UL:
410 if (logger.isLoggable(Level.FINE)) {
411 logger.log(Level.FINE, "There was an OPT answer. Option code: " + optionCode + " data: " + this._hexString(optiondata));
412 }
413 break;
414 case Unknown:
415 logger.log(Level.WARNING, "There was an OPT answer. Not currently handled. Option code: " + optionCodeInt + " data: " + this._hexString(optiondata));
416 break;
417 default:
418
419 break;
420 }
421 }
422 } else {
423 logger.log(Level.WARNING, "There was an OPT answer. Wrong version number: " + version + " result code: " + extendedResultCode);
424 }
425 break;
426 default:
427 if (logger.isLoggable(Level.FINER)) {
428 logger.finer("DNSIncoming() unknown type:" + type);
429 }
430 _messageInputStream.skip(len);
431 break;
432 }
433 if (rec != null) {
434 rec.setRecordSource(source);
435 }
436 return rec;
437 }
438
439
440
441
442 String print(boolean dump) {
443 StringBuilder buf = new StringBuilder();
444 buf.append(this.print());
445 if (dump) {
446 byte[] data = new byte[_packet.getLength()];
447 System.arraycopy(_packet.getData(), 0, data, 0, data.length);
448 buf.append(this.print(data));
449 }
450 return buf.toString();
451 }
452
453 @Override
454 public String toString() {
455 StringBuilder buf = new StringBuilder();
456 buf.append(isQuery() ? "dns[query," : "dns[response,");
457 if (_packet.getAddress() != null) {
458 buf.append(_packet.getAddress().getHostAddress());
459 }
460 buf.append(':');
461 buf.append(_packet.getPort());
462 buf.append(", length=");
463 buf.append(_packet.getLength());
464 buf.append(", id=0x");
465 buf.append(Integer.toHexString(this.getId()));
466 if (this.getFlags() != 0) {
467 buf.append(", flags=0x");
468 buf.append(Integer.toHexString(this.getFlags()));
469 if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) {
470 buf.append(":r");
471 }
472 if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) {
473 buf.append(":aa");
474 }
475 if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) {
476 buf.append(":tc");
477 }
478 }
479 if (this.getNumberOfQuestions() > 0) {
480 buf.append(", questions=");
481 buf.append(this.getNumberOfQuestions());
482 }
483 if (this.getNumberOfAnswers() > 0) {
484 buf.append(", answers=");
485 buf.append(this.getNumberOfAnswers());
486 }
487 if (this.getNumberOfAuthorities() > 0) {
488 buf.append(", authorities=");
489 buf.append(this.getNumberOfAuthorities());
490 }
491 if (this.getNumberOfAdditionals() > 0) {
492 buf.append(", additionals=");
493 buf.append(this.getNumberOfAdditionals());
494 }
495 if (this.getNumberOfQuestions() > 0) {
496 buf.append("\nquestions:");
497 for (DNSQuestion question : _questions) {
498 buf.append("\n\t");
499 buf.append(question);
500 }
501 }
502 if (this.getNumberOfAnswers() > 0) {
503 buf.append("\nanswers:");
504 for (DNSRecord record : _answers) {
505 buf.append("\n\t");
506 buf.append(record);
507 }
508 }
509 if (this.getNumberOfAuthorities() > 0) {
510 buf.append("\nauthorities:");
511 for (DNSRecord record : _authoritativeAnswers) {
512 buf.append("\n\t");
513 buf.append(record);
514 }
515 }
516 if (this.getNumberOfAdditionals() > 0) {
517 buf.append("\nadditionals:");
518 for (DNSRecord record : _additionals) {
519 buf.append("\n\t");
520 buf.append(record);
521 }
522 }
523 buf.append("]");
524 return buf.toString();
525 }
526
527
528
529
530
531
532
533 void append(DNSIncoming that) {
534 if (this.isQuery() && this.isTruncated() && that.isQuery()) {
535 this._questions.addAll(that.getQuestions());
536 this._answers.addAll(that.getAnswers());
537 this._authoritativeAnswers.addAll(that.getAuthorities());
538 this._additionals.addAll(that.getAdditionals());
539 } else {
540 throw new IllegalArgumentException();
541 }
542 }
543
544 public int elapseSinceArrival() {
545 return (int) (System.currentTimeMillis() - _receivedTime);
546 }
547
548
549
550
551
552
553 public int getSenderUDPPayload() {
554 return this._senderUDPPayload;
555 }
556
557 private static final char[] _nibbleToHex = { '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' };
558
559
560
561
562
563
564
565 private String _hexString(byte[] bytes) {
566
567 StringBuilder result = new StringBuilder(2 * bytes.length);
568
569 for (int i = 0; i < bytes.length; i++) {
570 int b = bytes[i] & 0xFF;
571 result.append(_nibbleToHex[b / 16]);
572 result.append(_nibbleToHex[b % 16]);
573 }
574
575 return result.toString();
576 }
577
578 }