Clover Coverage Report - JmDNS 3.4.1
Coverage timestamp: Thu Aug 25 2011 13:06:33 CEST
../../../img/srcFileCovDistChart6.png 31% of files have more coverage
185   451   72   6.85
78   314   0.39   13.5
27     2.67  
2    
 
  DNSOutgoing       Line # 20 116 0% 39 108 38.6% 0.38636363
  DNSOutgoing.MessageOutputStream       Line # 22 69 0% 33 28 75.4% 0.75438595
 
  (23)
 
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.ByteArrayOutputStream;
8    import java.io.IOException;
9    import java.util.HashMap;
10    import java.util.Map;
11   
12    import javax.jmdns.impl.constants.DNSConstants;
13    import javax.jmdns.impl.constants.DNSRecordClass;
14   
15    /**
16    * An outgoing DNS message.
17    *
18    * @author Arthur van Hoff, Rick Blair, Werner Randelshofer
19    */
 
20    public final class DNSOutgoing extends DNSMessage {
21   
 
22    public static class MessageOutputStream extends ByteArrayOutputStream {
23    private final DNSOutgoing _out;
24   
25    private final int _offset;
26   
27    /**
28    * Creates a new message stream, with a buffer capacity of the specified size, in bytes.
29    *
30    * @param size
31    * the initial size.
32    * @exception IllegalArgumentException
33    * if size is negative.
34    */
 
35  3237 toggle MessageOutputStream(int size, DNSOutgoing out) {
36  3237 this(size, out, 0);
37    }
38   
 
39  4530 toggle MessageOutputStream(int size, DNSOutgoing out, int offset) {
40  4530 super(size);
41  4530 _out = out;
42  4529 _offset = offset;
43    }
44   
 
45  64831 toggle void writeByte(int value) {
46  64829 this.write(value & 0xFF);
47    }
48   
 
49  0 toggle void writeBytes(String str, int off, int len) {
50  0 for (int i = 0; i < len; i++) {
51  0 writeByte(str.charAt(off + i));
52    }
53    }
54   
 
55  0 toggle void writeBytes(byte data[]) {
56  0 if (data != null) {
57  0 writeBytes(data, 0, data.length);
58    }
59    }
60   
 
61  606 toggle void writeBytes(byte data[], int off, int len) {
62  12074 for (int i = 0; i < len; i++) {
63  11468 writeByte(data[off + i]);
64    }
65    }
66   
 
67  11046 toggle void writeShort(int value) {
68  11046 writeByte(value >> 8);
69  11045 writeByte(value);
70    }
71   
 
72  1292 toggle void writeInt(int value) {
73  1292 writeShort(value >> 16);
74  1292 writeShort(value);
75    }
76   
 
77  3142 toggle void writeUTF(String str, int off, int len) {
78    // compute utf length
79  3143 int utflen = 0;
80  27315 for (int i = 0; i < len; i++) {
81  24174 int ch = str.charAt(off + i);
82  24175 if ((ch >= 0x0001) && (ch <= 0x007F)) {
83  24176 utflen += 1;
84    } else {
85  0 if (ch > 0x07FF) {
86  0 utflen += 3;
87    } else {
88  0 utflen += 2;
89    }
90    }
91    }
92    // write utf length
93  3143 writeByte(utflen);
94    // write utf data
95  27320 for (int i = 0; i < len; i++) {
96  24176 int ch = str.charAt(off + i);
97  24178 if ((ch >= 0x0001) && (ch <= 0x007F)) {
98  24177 writeByte(ch);
99    } else {
100  0 if (ch > 0x07FF) {
101  0 writeByte(0xE0 | ((ch >> 12) & 0x0F));
102  0 writeByte(0x80 | ((ch >> 6) & 0x3F));
103  0 writeByte(0x80 | ((ch >> 0) & 0x3F));
104    } else {
105  0 writeByte(0xC0 | ((ch >> 6) & 0x1F));
106  0 writeByte(0x80 | ((ch >> 0) & 0x3F));
107    }
108    }
109    }
110    }
111   
 
112  2426 toggle void writeName(String name) {
113  2426 writeName(name, true);
114    }
115   
 
116  2426 toggle void writeName(String name, boolean useCompression) {
117  2426 String aName = name;
118  2425 while (true) {
119  5570 int n = aName.indexOf('.');
120  5570 if (n < 0) {
121  882 n = aName.length();
122    }
123  5568 if (n <= 0) {
124  882 writeByte(0);
125  882 return;
126    }
127  4687 String label = aName.substring(0, n);
128  4688 if (useCompression && USE_DOMAIN_NAME_COMPRESSION) {
129  4688 Integer offset = _out._names.get(aName);
130  4686 if (offset != null) {
131  1544 int val = offset.intValue();
132  1544 writeByte((val >> 8) | 0xC0);
133  1544 writeByte(val & 0xFF);
134  1544 return;
135    }
136  3141 _out._names.put(aName, Integer.valueOf(this.size() + _offset));
137  3144 writeUTF(label, 0, label.length());
138    } else {
139  0 writeUTF(label, 0, label.length());
140    }
141  3144 aName = aName.substring(n);
142  3144 if (aName.startsWith(".")) {
143  3144 aName = aName.substring(1);
144    }
145    }
146    }
147   
 
148  448 toggle void writeQuestion(DNSQuestion question) {
149  448 writeName(question.getName());
150  448 writeShort(question.getRecordType().indexValue());
151  448 writeShort(question.getRecordClass().indexValue());
152    }
153   
 
154  1292 toggle void writeRecord(DNSRecord rec, long now) {
155  1292 writeName(rec.getName());
156  1292 writeShort(rec.getRecordType().indexValue());
157  1292 writeShort(rec.getRecordClass().indexValue() | ((rec.isUnique() && _out.isMulticast()) ? DNSRecordClass.CLASS_UNIQUE : 0));
158  1292 writeInt((now == 0) ? rec.getTTL() : rec.getRemainingTTL(now));
159   
160    // We need to take into account the 2 size bytes
161  1292 MessageOutputStream record = new MessageOutputStream(512, _out, _offset + this.size() + 2);
162  1292 rec.write(record);
163  1292 byte[] byteArray = record.toByteArray();
164   
165  1290 writeShort(byteArray.length);
166  1291 write(byteArray, 0, byteArray.length);
167    }
168   
169    }
170   
171    /**
172    * This can be used to turn off domain name compression. This was helpful for tracking problems interacting with other mdns implementations.
173    */
174    public static boolean USE_DOMAIN_NAME_COMPRESSION = true;
175   
176    Map<String, Integer> _names;
177   
178    private int _maxUDPPayload;
179   
180    private final MessageOutputStream _questionsBytes;
181   
182    private final MessageOutputStream _answersBytes;
183   
184    private final MessageOutputStream _authoritativeAnswersBytes;
185   
186    private final MessageOutputStream _additionalsAnswersBytes;
187   
188    private final static int HEADER_SIZE = 12;
189   
190    /**
191    * Create an outgoing multicast query or response.
192    *
193    * @param flags
194    */
 
195  454 toggle public DNSOutgoing(int flags) {
196  454 this(flags, true, DNSConstants.MAX_MSG_TYPICAL);
197    }
198   
199    /**
200    * Create an outgoing query or response.
201    *
202    * @param flags
203    * @param multicast
204    */
 
205  1 toggle public DNSOutgoing(int flags, boolean multicast) {
206  1 this(flags, multicast, DNSConstants.MAX_MSG_TYPICAL);
207    }
208   
209    /**
210    * Create an outgoing query or response.
211    *
212    * @param flags
213    * @param multicast
214    * @param senderUDPPayload
215    * The sender's UDP payload size is the number of bytes of the largest UDP payload that can be reassembled and delivered in the sender's network stack.
216    */
 
217  488 toggle public DNSOutgoing(int flags, boolean multicast, int senderUDPPayload) {
218  488 super(flags, 0, multicast);
219  488 _names = new HashMap<String, Integer>();
220  486 _maxUDPPayload = (senderUDPPayload > 0 ? senderUDPPayload : DNSConstants.MAX_MSG_TYPICAL);
221  486 _questionsBytes = new MessageOutputStream(senderUDPPayload, this);
222  487 _answersBytes = new MessageOutputStream(senderUDPPayload, this);
223  488 _authoritativeAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
224  488 _additionalsAnswersBytes = new MessageOutputStream(senderUDPPayload, this);
225    }
226   
227    /**
228    * Return the number of byte available in the message.
229    *
230    * @return available space
231    */
 
232  869 toggle public int availableSpace() {
233  869 return _maxUDPPayload - HEADER_SIZE - _questionsBytes.size() - _answersBytes.size() - _authoritativeAnswersBytes.size() - _additionalsAnswersBytes.size();
234    }
235   
236    /**
237    * Add a question to the message.
238    *
239    * @param rec
240    * @exception IOException
241    */
 
242  224 toggle public void addQuestion(DNSQuestion rec) throws IOException {
243  224 MessageOutputStream record = new MessageOutputStream(512, this);
244  224 record.writeQuestion(rec);
245  224 byte[] byteArray = record.toByteArray();
246  224 if (byteArray.length < this.availableSpace()) {
247  224 _questions.add(rec);
248  224 _questionsBytes.write(byteArray, 0, byteArray.length);
249    } else {
250  0 throw new IOException("message full");
251    }
252    }
253   
254    /**
255    * Add an answer if it is not suppressed.
256    *
257    * @param in
258    * @param rec
259    * @exception IOException
260    */
 
261  506 toggle public void addAnswer(DNSIncoming in, DNSRecord rec) throws IOException {
262  505 if ((in == null) || !rec.suppressedBy(in)) {
263  485 this.addAnswer(rec, 0);
264    }
265    }
266   
267    /**
268    * Add an answer to the message.
269    *
270    * @param rec
271    * @param now
272    * @exception IOException
273    */
 
274  504 toggle public void addAnswer(DNSRecord rec, long now) throws IOException {
275  504 if (rec != null) {
276  504 if ((now == 0) || !rec.isExpired(now)) {
277  504 MessageOutputStream record = new MessageOutputStream(512, this);
278  504 record.writeRecord(rec, now);
279  504 byte[] byteArray = record.toByteArray();
280  504 if (byteArray.length < this.availableSpace()) {
281  504 _answers.add(rec);
282  504 _answersBytes.write(byteArray, 0, byteArray.length);
283    } else {
284  0 throw new IOException("message full");
285    }
286    }
287    }
288    }
289   
290    /**
291    * Add an authoritative answer to the message.
292    *
293    * @param rec
294    * @exception IOException
295    */
 
296  141 toggle public void addAuthorativeAnswer(DNSRecord rec) throws IOException {
297  142 MessageOutputStream record = new MessageOutputStream(512, this);
298  142 record.writeRecord(rec, 0);
299  142 byte[] byteArray = record.toByteArray();
300  142 if (byteArray.length < this.availableSpace()) {
301  142 _authoritativeAnswers.add(rec);
302  142 _authoritativeAnswersBytes.write(byteArray, 0, byteArray.length);
303    } else {
304  0 throw new IOException("message full");
305    }
306    }
307   
308    /**
309    * Add an additional answer to the record. Omit if there is no room.
310    *
311    * @param in
312    * @param rec
313    * @exception IOException
314    */
 
315  0 toggle public void addAdditionalAnswer(DNSIncoming in, DNSRecord rec) throws IOException {
316  0 MessageOutputStream record = new MessageOutputStream(512, this);
317  0 record.writeRecord(rec, 0);
318  0 byte[] byteArray = record.toByteArray();
319  0 if (byteArray.length < this.availableSpace()) {
320  0 _additionals.add(rec);
321  0 _additionalsAnswersBytes.write(byteArray, 0, byteArray.length);
322    } else {
323  0 throw new IOException("message full");
324    }
325    }
326   
327    /**
328    * Builds the final message buffer to be send and returns it.
329    *
330    * @return bytes to send.
331    */
 
332  416 toggle public byte[] data() {
333  416 long now = System.currentTimeMillis(); // System.currentTimeMillis()
334  415 _names.clear();
335   
336  415 MessageOutputStream message = new MessageOutputStream(_maxUDPPayload, this);
337  416 message.writeShort(_multicast ? 0 : this.getId());
338  416 message.writeShort(this.getFlags());
339  416 message.writeShort(this.getNumberOfQuestions());
340  416 message.writeShort(this.getNumberOfAnswers());
341  416 message.writeShort(this.getNumberOfAuthorities());
342  416 message.writeShort(this.getNumberOfAdditionals());
343  416 for (DNSQuestion question : _questions) {
344  224 message.writeQuestion(question);
345    }
346  416 for (DNSRecord record : _answers) {
347  504 message.writeRecord(record, now);
348    }
349  416 for (DNSRecord record : _authoritativeAnswers) {
350  142 message.writeRecord(record, now);
351    }
352  416 for (DNSRecord record : _additionals) {
353  0 message.writeRecord(record, now);
354    }
355  416 return message.toByteArray();
356    }
357   
 
358  0 toggle @Override
359    public boolean isQuery() {
360  0 return (this.getFlags() & DNSConstants.FLAGS_QR_MASK) == DNSConstants.FLAGS_QR_QUERY;
361    }
362   
363    /**
364    * Debugging.
365    */
 
366  0 toggle String print(boolean dump) {
367  0 StringBuilder buf = new StringBuilder();
368  0 buf.append(this.print());
369  0 if (dump) {
370  0 buf.append(this.print(this.data()));
371    }
372  0 return buf.toString();
373    }
374   
 
375  0 toggle @Override
376    public String toString() {
377  0 StringBuffer buf = new StringBuffer();
378  0 buf.append(isQuery() ? "dns[query:" : "dns[response:");
379  0 buf.append(" id=0x");
380  0 buf.append(Integer.toHexString(this.getId()));
381  0 if (this.getFlags() != 0) {
382  0 buf.append(", flags=0x");
383  0 buf.append(Integer.toHexString(this.getFlags()));
384  0 if ((this.getFlags() & DNSConstants.FLAGS_QR_RESPONSE) != 0) {
385  0 buf.append(":r");
386    }
387  0 if ((this.getFlags() & DNSConstants.FLAGS_AA) != 0) {
388  0 buf.append(":aa");
389    }
390  0 if ((this.getFlags() & DNSConstants.FLAGS_TC) != 0) {
391  0 buf.append(":tc");
392    }
393    }
394  0 if (this.getNumberOfQuestions() > 0) {
395  0 buf.append(", questions=");
396  0 buf.append(this.getNumberOfQuestions());
397    }
398  0 if (this.getNumberOfAnswers() > 0) {
399  0 buf.append(", answers=");
400  0 buf.append(this.getNumberOfAnswers());
401    }
402  0 if (this.getNumberOfAuthorities() > 0) {
403  0 buf.append(", authorities=");
404  0 buf.append(this.getNumberOfAuthorities());
405    }
406  0 if (this.getNumberOfAdditionals() > 0) {
407  0 buf.append(", additionals=");
408  0 buf.append(this.getNumberOfAdditionals());
409    }
410  0 if (this.getNumberOfQuestions() > 0) {
411  0 buf.append("\nquestions:");
412  0 for (DNSQuestion question : _questions) {
413  0 buf.append("\n\t");
414  0 buf.append(question);
415    }
416    }
417  0 if (this.getNumberOfAnswers() > 0) {
418  0 buf.append("\nanswers:");
419  0 for (DNSRecord record : _answers) {
420  0 buf.append("\n\t");
421  0 buf.append(record);
422    }
423    }
424  0 if (this.getNumberOfAuthorities() > 0) {
425  0 buf.append("\nauthorities:");
426  0 for (DNSRecord record : _authoritativeAnswers) {
427  0 buf.append("\n\t");
428  0 buf.append(record);
429    }
430    }
431  0 if (this.getNumberOfAdditionals() > 0) {
432  0 buf.append("\nadditionals:");
433  0 for (DNSRecord record : _additionals) {
434  0 buf.append("\n\t");
435  0 buf.append(record);
436    }
437    }
438  0 buf.append("\nnames=");
439  0 buf.append(_names);
440  0 buf.append("]");
441  0 return buf.toString();
442    }
443   
444    /**
445    * @return the maxUDPPayload
446    */
 
447  0 toggle public int getMaxUDPPayload() {
448  0 return this._maxUDPPayload;
449    }
450   
451    }