Clover Coverage Report - JmDNS 3.4.1
Coverage timestamp: Thu Aug 25 2011 13:06:33 CEST
../../../img/srcFileCovDistChart5.png 47% of files have more coverage
318   578   109   15.14
110   460   0.34   10.5
21     5.19  
2    
 
  DNSIncoming       Line # 28 240 0% 79 224 36.2% 0.36182335
  DNSIncoming.MessageInputStream       Line # 35 78 0% 30 34 65.3% 0.6530612
 
  (21)
 
1    // /Copyright 2003-2005 Arthur van Hoff, Rick Blair
2    // Licensed under Apache License version 2.0
3    // Original license LGPL
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    * Parse an incoming DNS message into its components.
25    *
26    * @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch, Daniel Bobbert
27    */
 
28    public final class DNSIncoming extends DNSMessage {
29    private static Logger logger = Logger.getLogger(DNSIncoming.class.getName());
30   
31    // This is a hack to handle a bug in the BonjourConformanceTest
32    // It is sending out target strings that don't follow the "domain name" format.
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  1004 toggle public MessageInputStream(byte[] buffer, int length) {
41  993 this(buffer, 0, length);
42    }
43   
44    /**
45    * @param buffer
46    * @param offset
47    * @param length
48    */
 
49  999 toggle public MessageInputStream(byte[] buffer, int offset, int length) {
50  979 super(buffer, offset, length);
51  1001 _names = new HashMap<Integer, String>();
52    }
53   
 
54  0 toggle public int readByte() {
55  0 return this.read();
56    }
57   
 
58  10592 toggle public int readUnsignedShort() {
59  10655 return (this.read() << 8) | this.read();
60    }
61   
 
62  1058 toggle public int readInt() {
63  1073 return (this.readUnsignedShort() << 16) | this.readUnsignedShort();
64    }
65   
 
66  579 toggle public byte[] readBytes(int len) {
67  587 byte bytes[] = new byte[len];
68  583 this.read(bytes, 0, len);
69  574 return bytes;
70    }
71   
 
72  2508 toggle public String readUTF(int len) {
73  2517 StringBuilder buffer = new StringBuilder(len);
74  18536 for (int index = 0; index < len; index++) {
75  16017 int ch = this.read();
76  16115 switch (ch >> 4) {
77  0 case 0:
78  0 case 1:
79  276 case 2:
80  393 case 3:
81  226 case 4:
82  852 case 5:
83  10836 case 6:
84  3650 case 7:
85    // 0xxxxxxx
86  16136 break;
87  0 case 12:
88  0 case 13:
89    // 110x xxxx 10xx xxxx
90  0 ch = ((ch & 0x1F) << 6) | (this.read() & 0x3F);
91  0 index++;
92  0 break;
93  0 case 14:
94    // 1110 xxxx 10xx xxxx 10xx xxxx
95  0 ch = ((ch & 0x0f) << 12) | ((this.read() & 0x3F) << 6) | (this.read() & 0x3F);
96  0 index++;
97  0 index++;
98  0 break;
99  0 default:
100    // 10xx xxxx, 1111 xxxx
101  0 ch = ((ch & 0x3F) << 4) | (this.read() & 0x0f);
102  0 index++;
103  0 break;
104    }
105  16046 buffer.append((char) ch);
106    }
107  2531 return buffer.toString();
108    }
109   
 
110  0 toggle protected synchronized int peek() {
111  0 return (pos < count) ? (buf[pos] & 0xff) : -1;
112    }
113   
 
114  1761 toggle public String readName() {
115  1780 Map<Integer, StringBuilder> names = new HashMap<Integer, StringBuilder>();
116  1774 StringBuilder buffer = new StringBuilder();
117  1780 boolean finished = false;
118  5398 while (!finished) {
119  4316 int len = this.read();
120  4320 if (len == 0) {
121  688 finished = true;
122  693 break;
123    }
124  3617 switch (DNSLabel.labelForByte(len)) {
125  2530 case Standard:
126  2539 int offset = pos - 1;
127  2528 String label = this.readUTF(len) + ".";
128  2523 buffer.append(label);
129  2536 for (StringBuilder previousLabel : names.values()) {
130  6944 previousLabel.append(label);
131    }
132  2524 names.put(Integer.valueOf(offset), new StringBuilder(label));
133  2514 break;
134  1099 case Compressed:
135  1101 int index = (DNSLabel.labelValue(len) << 8) | this.read();
136  1100 String compressedLabel = _names.get(Integer.valueOf(index));
137  1099 if (compressedLabel == null) {
138  0 logger1.severe("bad domain name: possible circular name detected. Bad offset: 0x" + Integer.toHexString(index) + " at 0x" + Integer.toHexString(pos - 2));
139  0 compressedLabel = "";
140    }
141  1097 buffer.append(compressedLabel);
142  1102 for (StringBuilder previousLabel : names.values()) {
143  433 previousLabel.append(compressedLabel);
144    }
145  1098 finished = true;
146  1100 break;
147  0 case Extended:
148    // int extendedLabelClass = DNSLabel.labelValue(len);
149  0 logger1.severe("Extended label are not currently supported.");
150  0 break;
151  0 case Unknown:
152  0 default:
153  0 logger1.severe("unsupported dns label type: '" + Integer.toHexString(len & 0xC0) + "'");
154    }
155    }
156  1778 for (Integer index : names.keySet()) {
157  2529 _names.put(index, names.get(index).toString());
158    }
159  1803 return buffer.toString();
160    }
161   
 
162  0 toggle public String readNonNameString() {
163  0 int len = this.read();
164  0 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    * Parse a message from a datagram packet.
179    *
180    * @param packet
181    * @exception IOException
182    */
 
183  642 toggle public DNSIncoming(DatagramPacket packet) throws IOException {
184  635 super(0, 0, packet.getPort() == DNSConstants.MDNS_PORT);
185  651 this._packet = packet;
186  651 InetAddress source = packet.getAddress();
187  651 this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
188  649 this._receivedTime = System.currentTimeMillis();
189  649 this._senderUDPPayload = DNSConstants.MAX_MSG_TYPICAL;
190   
191  652 try {
192  653 this.setId(_messageInputStream.readUnsignedShort());
193  656 this.setFlags(_messageInputStream.readUnsignedShort());
194  653 int numQuestions = _messageInputStream.readUnsignedShort();
195  656 int numAnswers = _messageInputStream.readUnsignedShort();
196  654 int numAuthorities = _messageInputStream.readUnsignedShort();
197  663 int numAdditionals = _messageInputStream.readUnsignedShort();
198   
199    // parse questions
200  649 if (numQuestions > 0) {
201  689 for (int i = 0; i < numQuestions; i++) {
202  352 _questions.add(this.readQuestion());
203    }
204    }
205   
206    // parse answers
207  651 if (numAnswers > 0) {
208  1046 for (int i = 0; i < numAnswers; i++) {
209  720 DNSRecord rec = this.readAnswer(source);
210  724 if (rec != null) {
211    // Add a record, if we were able to create one.
212  718 _answers.add(rec);
213    }
214    }
215    }
216   
217  652 if (numAuthorities > 0) {
218  443 for (int i = 0; i < numAuthorities; i++) {
219  228 DNSRecord rec = this.readAnswer(source);
220  228 if (rec != null) {
221    // Add a record, if we were able to create one.
222  223 _authoritativeAnswers.add(rec);
223    }
224    }
225    }
226   
227  653 if (numAdditionals > 0) {
228  214 for (int i = 0; i < numAdditionals; i++) {
229  117 DNSRecord rec = this.readAnswer(source);
230  119 if (rec != null) {
231    // Add a record, if we were able to create one.
232  0 _additionals.add(rec);
233    }
234    }
235    }
236    } catch (Exception e) {
237  0 logger.log(Level.WARNING, "DNSIncoming() dump " + print(true) + "\n exception ", e);
238    // This ugly but some JVM don't implement the cause on IOException
239  0 IOException ioe = new IOException("DNSIncoming corrupted message");
240  0 ioe.initCause(e);
241  0 throw ioe;
242    }
243    }
244   
 
245  344 toggle private DNSIncoming(int flags, int id, boolean multicast, DatagramPacket packet, long receivedTime) {
246  345 super(flags, id, multicast);
247  347 this._packet = packet;
248  349 this._messageInputStream = new MessageInputStream(packet.getData(), packet.getLength());
249  347 this._receivedTime = receivedTime;
250    }
251   
252   
253    /*
254    * (non-Javadoc)
255    *
256    * @see java.lang.Object#clone()
257    */
 
258  340 toggle @Override
259    public DNSIncoming clone() {
260  345 DNSIncoming in = new DNSIncoming(this.getFlags(), this.getId(), this.isMulticast(), this._packet, this._receivedTime);
261  348 in._senderUDPPayload = this._senderUDPPayload;
262  349 in._questions.addAll(this._questions);
263  341 in._answers.addAll(this._answers);
264  341 in._authoritativeAnswers.addAll(this._authoritativeAnswers);
265  347 in._additionals.addAll(this._additionals);
266  350 return in;
267    }
268   
269   
 
270  340 toggle private DNSQuestion readQuestion() {
271  351 String domain = _messageInputStream.readName();
272  354 DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
273  351 if (type == DNSRecordType.TYPE_IGNORE) {
274  0 logger.log(Level.SEVERE, "Could not find record type: " + this.print(true));
275    }
276  347 int recordClassIndex = _messageInputStream.readUnsignedShort();
277  354 DNSRecordClass recordClass = DNSRecordClass.classForIndex(recordClassIndex);
278  354 boolean unique = recordClass.isUnique(recordClassIndex);
279  349 return DNSQuestion.newQuestion(domain, type, recordClass, unique);
280    }
281   
 
282  1061 toggle private DNSRecord readAnswer(InetAddress source) {
283  1073 String domain = _messageInputStream.readName();
284  1068 DNSRecordType type = DNSRecordType.typeForIndex(_messageInputStream.readUnsignedShort());
285  1064 if (type == DNSRecordType.TYPE_IGNORE) {
286  0 logger.log(Level.SEVERE, "Could not find record type. domain: " + domain + "\n" + this.print(true));
287    }
288  1065 int recordClassIndex = _messageInputStream.readUnsignedShort();
289  1066 DNSRecordClass recordClass = (type == DNSRecordType.TYPE_OPT ? DNSRecordClass.CLASS_UNKNOWN : DNSRecordClass.classForIndex(recordClassIndex));
290  1064 if ((recordClass == DNSRecordClass.CLASS_UNKNOWN) && (type != DNSRecordType.TYPE_OPT)) {
291  0 logger.log(Level.SEVERE, "Could not find record class. domain: " + domain + " type: " + type + "\n" + this.print(true));
292    }
293  1062 boolean unique = recordClass.isUnique(recordClassIndex);
294  1061 int ttl = _messageInputStream.readInt();
295  1072 int len = _messageInputStream.readUnsignedShort();
296  1077 DNSRecord rec = null;
297   
298  1077 switch (type) {
299  347 case TYPE_A: // IPv4
300  347 rec = new DNSRecord.IPv4Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
301  346 break;
302  105 case TYPE_AAAA: // IPv6
303  104 rec = new DNSRecord.IPv6Address(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
304  103 break;
305  0 case TYPE_CNAME:
306  148 case TYPE_PTR:
307  150 String service = "";
308  154 service = _messageInputStream.readName();
309  158 if (service.length() > 0) {
310  157 rec = new DNSRecord.Pointer(domain, recordClass, unique, ttl, service);
311    } else {
312  0 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  156 break;
315  127 case TYPE_TXT:
316  129 rec = new DNSRecord.Text(domain, recordClass, unique, ttl, _messageInputStream.readBytes(len));
317  128 break;
318  207 case TYPE_SRV:
319  210 int priority = _messageInputStream.readUnsignedShort();
320  211 int weight = _messageInputStream.readUnsignedShort();
321  211 int port = _messageInputStream.readUnsignedShort();
322  211 String target = "";
323    // This is a hack to handle a bug in the BonjourConformanceTest
324    // It is sending out target strings that don't follow the "domain name" format.
325  208 if (USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) {
326  210 target = _messageInputStream.readName();
327    } else {
328    // [PJYF Nov 13 2010] Do we still need this? This looks really bad. All label are supposed to start by a length.
329  0 target = _messageInputStream.readNonNameString();
330    }
331  210 rec = new DNSRecord.Service(domain, recordClass, unique, ttl, priority, weight, port, target);
332  211 break;
333  0 case TYPE_HINFO:
334  0 StringBuilder buf = new StringBuilder();
335  0 buf.append(_messageInputStream.readUTF(len));
336  0 int index = buf.indexOf(" ");
337  0 String cpu = (index > 0 ? buf.substring(0, index) : buf.toString()).trim();
338  0 String os = (index > 0 ? buf.substring(index + 1) : "").trim();
339  0 rec = new DNSRecord.HostInformation(domain, recordClass, unique, ttl, cpu, os);
340  0 break;
341  0 case TYPE_OPT:
342  0 DNSResultCode extendedResultCode = DNSResultCode.resultCodeForFlags(this.getFlags(), ttl);
343  0 int version = (ttl & 0x00ff0000) >> 16;
344  0 if (version == 0) {
345  0 _senderUDPPayload = recordClassIndex;
346  0 while (_messageInputStream.available() > 0) {
347    // Read RDData
348  0 int optionCodeInt = 0;
349  0 DNSOptionCode optionCode = null;
350  0 if (_messageInputStream.available() >= 2) {
351  0 optionCodeInt = _messageInputStream.readUnsignedShort();
352  0 optionCode = DNSOptionCode.resultCodeForFlags(optionCodeInt);
353    } else {
354  0 logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
355  0 break;
356    }
357  0 int optionLength = 0;
358  0 if (_messageInputStream.available() >= 2) {
359  0 optionLength = _messageInputStream.readUnsignedShort();
360    } else {
361  0 logger.log(Level.WARNING, "There was a problem reading the OPT record. Ignoring.");
362  0 break;
363    }
364  0 byte[] optiondata = new byte[0];
365  0 if (_messageInputStream.available() >= optionLength) {
366  0 optiondata = _messageInputStream.readBytes(optionLength);
367    }
368    //
369    // We should really do something with those options.
370  0 switch (optionCode) {
371  0 case Owner:
372    // Valid length values are 8, 14, 18 and 20
373    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
374    // |Opt|Len|V|S|Primary MAC|Wakeup MAC | Password |
375    // +-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
376    //
377  0 int ownerVersion = 0;
378  0 int ownerSequence = 0;
379  0 byte[] ownerPrimaryMacAddress = null;
380  0 byte[] ownerWakeupMacAddress = null;
381  0 byte[] ownerPassword = null;
382  0 try {
383  0 ownerVersion = optiondata[0];
384  0 ownerSequence = optiondata[1];
385  0 ownerPrimaryMacAddress = new byte[] { optiondata[2], optiondata[3], optiondata[4], optiondata[5], optiondata[6], optiondata[7] };
386  0 ownerWakeupMacAddress = ownerPrimaryMacAddress;
387  0 if (optiondata.length > 8) {
388    // We have a wakeupMacAddress.
389  0 ownerWakeupMacAddress = new byte[] { optiondata[8], optiondata[9], optiondata[10], optiondata[11], optiondata[12], optiondata[13] };
390    }
391  0 if (optiondata.length == 18) {
392    // We have a short password.
393  0 ownerPassword = new byte[] { optiondata[14], optiondata[15], optiondata[16], optiondata[17] };
394    }
395  0 if (optiondata.length == 22) {
396    // We have a long password.
397  0 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  0 logger.warning("Malformed OPT answer. Option code: Owner data: " + this._hexString(optiondata));
401    }
402  0 if (logger.isLoggable(Level.FINE)) {
403  0 logger.fine("Unhandled Owner OPT version: " + ownerVersion + " sequence: " + ownerSequence + " MAC address: " + this._hexString(ownerPrimaryMacAddress)
404  0 + (ownerWakeupMacAddress != ownerPrimaryMacAddress ? " wakeup MAC address: " + this._hexString(ownerWakeupMacAddress) : "") + (ownerPassword != null ? " password: " + this._hexString(ownerPassword) : ""));
405    }
406  0 break;
407  0 case LLQ:
408  0 case NSID:
409  0 case UL:
410  0 if (logger.isLoggable(Level.FINE)) {
411  0 logger.log(Level.FINE, "There was an OPT answer. Option code: " + optionCode + " data: " + this._hexString(optiondata));
412    }
413  0 break;
414  0 case Unknown:
415  0 logger.log(Level.WARNING, "There was an OPT answer. Not currently handled. Option code: " + optionCodeInt + " data: " + this._hexString(optiondata));
416  0 break;
417  0 default:
418    // This is to keep the compiler happy.
419  0 break;
420    }
421    }
422    } else {
423  0 logger.log(Level.WARNING, "There was an OPT answer. Wrong version number: " + version + " result code: " + extendedResultCode);
424    }
425  0 break;
426  118 default:
427  118 if (logger.isLoggable(Level.FINER)) {
428  0 logger.finer("DNSIncoming() unknown type:" + type);
429    }
430  117 _messageInputStream.skip(len);
431  116 break;
432    }
433  1059 if (rec != null) {
434  952 rec.setRecordSource(source);
435    }
436  1050 return rec;
437    }
438   
439    /**
440    * Debugging.
441    */
 
442  0 toggle String print(boolean dump) {
443  0 StringBuilder buf = new StringBuilder();
444  0 buf.append(this.print());
445  0 if (dump) {
446  0 byte[] data = new byte[_packet.getLength()];
447  0 System.arraycopy(_packet.getData(), 0, data, 0, data.length);
448  0 buf.append(this.print(data));
449    }
450  0 return buf.toString();
451    }
452   
 
453  0 toggle @Override
454    public String toString() {
455  0 StringBuilder buf = new StringBuilder();
456  0 buf.append(isQuery() ? "dns[query," : "dns[response,");
457  0 if (_packet.getAddress() != null) {
458  0 buf.append(_packet.getAddress().getHostAddress());
459    }
460  0 buf.append(':');
461  0 buf.append(_packet.getPort());
462  0 buf.append(", length=");
463  0 buf.append(_packet.getLength());
464  0 buf.append(", id=0x");
465  0 buf.append(Integer.toHexString(this.getId()));
466  0 if (this.getFlags() != 0) {
467  0 buf.append(", flags=0x");
468  0 buf.append(Integer.toHexString(this.getFlags()));
469  0 if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) {
470  0 buf.append(":r");
471    }
472  0 if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) {
473  0 buf.append(":aa");
474    }
475  0 if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) {
476  0 buf.append(":tc");
477    }
478    }
479  0 if (this.getNumberOfQuestions() > 0) {
480  0 buf.append(", questions=");
481  0 buf.append(this.getNumberOfQuestions());
482    }
483  0 if (this.getNumberOfAnswers() > 0) {
484  0 buf.append(", answers=");
485  0 buf.append(this.getNumberOfAnswers());
486    }
487  0 if (this.getNumberOfAuthorities() > 0) {
488  0 buf.append(", authorities=");
489  0 buf.append(this.getNumberOfAuthorities());
490    }
491  0 if (this.getNumberOfAdditionals() > 0) {
492  0 buf.append(", additionals=");
493  0 buf.append(this.getNumberOfAdditionals());
494    }
495  0 if (this.getNumberOfQuestions() > 0) {
496  0 buf.append("\nquestions:");
497  0 for (DNSQuestion question : _questions) {
498  0 buf.append("\n\t");
499  0 buf.append(question);
500    }
501    }
502  0 if (this.getNumberOfAnswers() > 0) {
503  0 buf.append("\nanswers:");
504  0 for (DNSRecord record : _answers) {
505  0 buf.append("\n\t");
506  0 buf.append(record);
507    }
508    }
509  0 if (this.getNumberOfAuthorities() > 0) {
510  0 buf.append("\nauthorities:");
511  0 for (DNSRecord record : _authoritativeAnswers) {
512  0 buf.append("\n\t");
513  0 buf.append(record);
514    }
515    }
516  0 if (this.getNumberOfAdditionals() > 0) {
517  0 buf.append("\nadditionals:");
518  0 for (DNSRecord record : _additionals) {
519  0 buf.append("\n\t");
520  0 buf.append(record);
521    }
522    }
523  0 buf.append("]");
524  0 return buf.toString();
525    }
526   
527    /**
528    * Appends answers to this Incoming.
529    *
530    * @exception IllegalArgumentException
531    * If not a query or if Truncated.
532    */
 
533  0 toggle void append(DNSIncoming that) {
534  0 if (this.isQuery() && this.isTruncated() && that.isQuery()) {
535  0 this._questions.addAll(that.getQuestions());
536  0 this._answers.addAll(that.getAnswers());
537  0 this._authoritativeAnswers.addAll(that.getAuthorities());
538  0 this._additionals.addAll(that.getAdditionals());
539    } else {
540  0 throw new IllegalArgumentException();
541    }
542    }
543   
 
544  158 toggle public int elapseSinceArrival() {
545  161 return (int) (System.currentTimeMillis() - _receivedTime);
546    }
547   
548    /**
549    * This will return the default UDP payload except if an OPT record was found with a different size.
550    *
551    * @return the senderUDPPayload
552    */
 
553  33 toggle public int getSenderUDPPayload() {
554  33 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    * Returns a hex-string for printing
561    *
562    * @param bytes
563    * @return Returns a hex-string which can be used within a SQL expression
564    */
 
565  0 toggle private String _hexString(byte[] bytes) {
566   
567  0 StringBuilder result = new StringBuilder(2 * bytes.length);
568   
569  0 for (int i = 0; i < bytes.length; i++) {
570  0 int b = bytes[i] & 0xFF;
571  0 result.append(_nibbleToHex[b / 16]);
572  0 result.append(_nibbleToHex[b % 16]);
573    }
574   
575  0 return result.toString();
576    }
577   
578    }