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
271   1,025   150   3.11
88   613   0.55   10.88
87     1.72  
8    
 
  DNSRecord       Line # 32 30 0% 21 9 82.4% 0.8235294
  DNSRecord.IPv4Address       Line # 173 13 0% 6 5 76.2% 0.7619048
  DNSRecord.IPv6Address       Line # 215 16 0% 8 12 57.1% 0.5714286
  DNSRecord.Address       Line # 263 57 0% 27 58 36.3% 0.36263737
  DNSRecord.Pointer       Line # 425 32 0% 19 18 67.9% 0.6785714
  DNSRecord.Text       Line # 536 25 0% 20 16 68% 0.68
  DNSRecord.Service       Line # 639 72 0% 34 65 39.3% 0.39252338
  DNSRecord.HostInformation       Line # 848 26 0% 15 42 0% 0.0
 
  (20)
 
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.DataOutputStream;
8    import java.io.IOException;
9    import java.io.UnsupportedEncodingException;
10    import java.net.Inet4Address;
11    import java.net.Inet6Address;
12    import java.net.InetAddress;
13    import java.net.UnknownHostException;
14    import java.util.HashMap;
15    import java.util.Map;
16    import java.util.logging.Level;
17    import java.util.logging.Logger;
18   
19    import javax.jmdns.ServiceEvent;
20    import javax.jmdns.ServiceInfo;
21    import javax.jmdns.ServiceInfo.Fields;
22    import javax.jmdns.impl.DNSOutgoing.MessageOutputStream;
23    import javax.jmdns.impl.constants.DNSConstants;
24    import javax.jmdns.impl.constants.DNSRecordClass;
25    import javax.jmdns.impl.constants.DNSRecordType;
26   
27    /**
28    * DNS record
29    *
30    * @author Arthur van Hoff, Rick Blair, Werner Randelshofer, Pierre Frisch
31    */
 
32    public abstract class DNSRecord extends DNSEntry {
33    private static Logger logger = Logger.getLogger(DNSRecord.class.getName());
34    private int _ttl;
35    private long _created;
36   
37    /**
38    * This source is mainly for debugging purposes, should be the address that sent this record.
39    */
40    private InetAddress _source;
41   
42    /**
43    * Create a DNSRecord with a name, type, class, and ttl.
44    */
 
45  2034 toggle DNSRecord(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl) {
46  2026 super(name, type, recordClass, unique);
47  2045 this._ttl = ttl;
48  2046 this._created = System.currentTimeMillis();
49    }
50   
51    /*
52    * (non-Javadoc)
53    * @see javax.jmdns.impl.DNSEntry#equals(java.lang.Object)
54    */
 
55  108 toggle @Override
56    public boolean equals(Object other) {
57  108 return (other instanceof DNSRecord) && super.equals(other) && sameValue((DNSRecord) other);
58    }
59   
60    /**
61    * True if this record has the same value as some other record.
62    */
63    abstract boolean sameValue(DNSRecord other);
64   
65    /**
66    * True if this record has the same type as some other record.
67    */
 
68  359 toggle boolean sameType(DNSRecord other) {
69  359 return this.getRecordType() == other.getRecordType();
70    }
71   
72    /**
73    * Handles a query represented by this record.
74    *
75    * @return Returns true if a conflict with one of the services registered with JmDNS or with the hostname occured.
76    */
77    abstract boolean handleQuery(JmDNSImpl dns, long expirationTime);
78   
79    /**
80    * Handles a response represented by this record.
81    *
82    * @return Returns true if a conflict with one of the services registered with JmDNS or with the hostname occured.
83    */
84    abstract boolean handleResponse(JmDNSImpl dns);
85   
86    /**
87    * Adds this as an answer to the provided outgoing datagram.
88    */
89    abstract DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException;
90   
91    /**
92    * True if this record is suppressed by the answers in a message.
93    */
 
94  123 toggle boolean suppressedBy(DNSIncoming msg) {
95  123 try {
96  123 for (DNSRecord answer : msg.getAllAnswers()) {
97  75 if (suppressedBy(answer)) {
98  21 return true;
99    }
100    }
101  102 return false;
102    } catch (ArrayIndexOutOfBoundsException e) {
103  0 logger.log(Level.WARNING, "suppressedBy() message " + msg + " exception ", e);
104    // msg.print(true);
105  0 return false;
106    }
107    }
108   
109    /**
110    * True if this record would be suppressed by an answer. This is the case if this record would not have a significantly longer TTL.
111    */
 
112  75 toggle boolean suppressedBy(DNSRecord other) {
113  75 if (this.equals(other) && (other._ttl > _ttl / 2)) {
114  21 return true;
115    }
116  54 return false;
117    }
118   
119    /**
120    * Get the expiration time of this record.
121    */
 
122  2531 toggle long getExpirationTime(int percent) {
123    // ttl is in seconds the constant 10 is 1000 ms / 100 %
124  2551 return _created + (percent * _ttl * 10L);
125    }
126   
127    /**
128    * Get the remaining TTL for this record.
129    */
 
130  665 toggle int getRemainingTTL(long now) {
131  665 return (int) Math.max(0, (getExpirationTime(100) - now) / 1000);
132    }
133   
134    /*
135    * (non-Javadoc)
136    * @see javax.jmdns.impl.DNSEntry#isExpired(long)
137    */
 
138  1371 toggle @Override
139    public boolean isExpired(long now) {
140  1376 return getExpirationTime(100) <= now;
141    }
142   
143    /*
144    * (non-Javadoc)
145    * @see javax.jmdns.impl.DNSEntry#isStale(long)
146    */
 
147  503 toggle @Override
148    public boolean isStale(long now) {
149  507 return getExpirationTime(50) <= now;
150    }
151   
152    /**
153    * Reset the TTL of a record. This avoids having to update the entire record in the cache.
154    */
 
155  504 toggle void resetTTL(DNSRecord other) {
156  506 _created = other._created;
157  505 _ttl = other._ttl;
158    }
159   
160    /**
161    * When a record flushed we don't remove it immediately, but mark it for rapid decay.
162    */
 
163  54 toggle void setWillExpireSoon(long now) {
164  54 _created = now;
165  54 _ttl = DNSConstants.RECORD_EXPIRY_DELAY;
166    }
167   
168    /**
169    * Write this record into an outgoing message.
170    */
171    abstract void write(MessageOutputStream out);
172   
 
173    public static class IPv4Address extends Address {
174   
 
175  513 toggle IPv4Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) {
176  513 super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, addr);
177    }
178   
 
179  349 toggle IPv4Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) {
180  339 super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, rawAddress);
181    }
182   
 
183  314 toggle @Override
184    void write(MessageOutputStream out) {
185  313 if (_addr != null) {
186  314 byte[] buffer = _addr.getAddress();
187    // If we have a type A records we should answer with a IPv4 address
188  313 if (_addr instanceof Inet4Address) {
189    // All is good
190    } else {
191    // Get the last four bytes
192  0 byte[] tempbuffer = buffer;
193  0 buffer = new byte[4];
194  0 System.arraycopy(tempbuffer, 12, buffer, 0, 4);
195    }
196  313 int length = buffer.length;
197  314 out.writeBytes(buffer, 0, length);
198    }
199    }
200   
201    /*
202    * (non-Javadoc)
203    * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
204    */
 
205  39 toggle @Override
206    public ServiceInfo getServiceInfo(boolean persistent) {
207   
208  39 ServiceInfoImpl info = (ServiceInfoImpl) super.getServiceInfo(persistent);
209  39 info.addAddress((Inet4Address) _addr);
210  39 return info;
211    }
212   
213    }
214   
 
215    public static class IPv6Address extends Address {
216   
 
217  16 toggle IPv6Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) {
218  16 super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, addr);
219    }
220   
 
221  105 toggle IPv6Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) {
222  103 super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, rawAddress);
223    }
224   
 
225  16 toggle @Override
226    void write(MessageOutputStream out) {
227  16 if (_addr != null) {
228  16 byte[] buffer = _addr.getAddress();
229    // If we have a type AAAA records we should answer with a IPv6 address
230  16 if (_addr instanceof Inet4Address) {
231  0 byte[] tempbuffer = buffer;
232  0 buffer = new byte[16];
233  0 for (int i = 0; i < 16; i++) {
234  0 if (i < 11) {
235  0 buffer[i] = tempbuffer[i - 12];
236    } else {
237  0 buffer[i] = 0;
238    }
239    }
240    }
241  16 int length = buffer.length;
242  16 out.writeBytes(buffer, 0, length);
243    }
244    }
245   
246    /*
247    * (non-Javadoc)
248    * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
249    */
 
250  50 toggle @Override
251    public ServiceInfo getServiceInfo(boolean persistent) {
252   
253  51 ServiceInfoImpl info = (ServiceInfoImpl) super.getServiceInfo(persistent);
254  51 info.addAddress((Inet6Address) _addr);
255  50 return info;
256    }
257   
258    }
259   
260    /**
261    * Address record.
262    */
 
263    public static abstract class Address extends DNSRecord {
264    private static Logger logger1 = Logger.getLogger(Address.class.getName());
265   
266    InetAddress _addr;
267   
 
268  529 toggle protected Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) {
269  527 super(name, type, recordClass, unique, ttl);
270  529 this._addr = addr;
271    }
272   
 
273  458 toggle protected Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) {
274  454 super(name, type, recordClass, unique, ttl);
275  457 try {
276  456 this._addr = InetAddress.getByAddress(rawAddress);
277    } catch (UnknownHostException exception) {
278  0 logger1.log(Level.WARNING, "Address() exception ", exception);
279    }
280    }
281   
 
282  0 toggle boolean same(DNSRecord other) {
283  0 if (! (other instanceof Address) ) {
284  0 return false;
285    }
286  0 return ((sameName(other)) && ((sameValue(other))));
287    }
288   
 
289  358 toggle boolean sameName(DNSRecord other) {
290  360 return this.getName().equalsIgnoreCase(other.getName());
291    }
292   
 
293  578 toggle @Override
294    boolean sameValue(DNSRecord other) {
295  578 if (! (other instanceof Address) ) {
296  0 return false;
297    }
298  576 Address address = (Address) other;
299  579 if ((this.getAddress() == null) && (address.getAddress() != null)) {
300  0 return false;
301    }
302  578 return this.getAddress().equals(address.getAddress());
303    }
304   
 
305  0 toggle @Override
306    public boolean isSingleValued() {
307  0 return false;
308    }
309   
 
310  1775 toggle InetAddress getAddress() {
311  1796 return _addr;
312    }
313   
314    /**
315    * Creates a byte array representation of this record. This is needed for tie-break tests according to draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2.
316    */
 
317  0 toggle @Override
318    protected void toByteArray(DataOutputStream dout) throws IOException {
319  0 super.toByteArray(dout);
320  0 byte[] buffer = this.getAddress().getAddress();
321  0 for (int i = 0; i < buffer.length; i++) {
322  0 dout.writeByte(buffer[i]);
323    }
324    }
325   
326    /**
327    * Does the necessary actions, when this as a query.
328    */
 
329  143 toggle @Override
330    boolean handleQuery(JmDNSImpl dns, long expirationTime) {
331  142 if (dns.getLocalHost().conflictWithRecord(this)) {
332  0 DNSRecord.Address localAddress = dns.getLocalHost().getDNSAddressRecord(this.getRecordType(), this.isUnique(), DNSConstants.DNS_TTL);
333  0 int comparison = this.compareTo(localAddress);
334   
335  0 if (comparison == 0) {
336    // the 2 records are identical this probably means we are seeing our own record.
337    // With multiple interfaces on a single computer it is possible to see our
338    // own records come in on different interfaces than the ones they were sent on.
339    // see section "10. Conflict Resolution" of mdns draft spec.
340  0 logger1.finer("handleQuery() Ignoring an identical address query");
341  0 return false;
342    }
343   
344  0 logger1.finer("handleQuery() Conflicting query detected.");
345    // Tie breaker test
346  0 if (dns.isProbing() && comparison > 0) {
347    // We lost the tie-break. We have to choose a different name.
348  0 dns.getLocalHost().incrementHostName();
349  0 dns.getCache().clear();
350  0 for (ServiceInfo serviceInfo : dns.getServices().values()) {
351  0 ServiceInfoImpl info = (ServiceInfoImpl) serviceInfo;
352  0 info.revertState();
353    }
354    }
355  0 dns.revertState();
356  0 return true;
357    }
358  141 return false;
359    }
360   
361    /**
362    * Does the necessary actions, when this as a response.
363    */
 
364  305 toggle @Override
365    boolean handleResponse(JmDNSImpl dns) {
366  306 if (dns.getLocalHost().conflictWithRecord(this)) {
367  0 logger1.finer("handleResponse() Denial detected");
368   
369  0 if (dns.isProbing()) {
370  0 dns.getLocalHost().incrementHostName();
371  0 dns.getCache().clear();
372  0 for (ServiceInfo serviceInfo : dns.getServices().values()) {
373  0 ServiceInfoImpl info = (ServiceInfoImpl) serviceInfo;
374  0 info.revertState();
375    }
376    }
377  0 dns.revertState();
378  0 return true;
379    }
380  303 return false;
381    }
382   
 
383  0 toggle @Override
384    DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException {
385  0 return out;
386    }
387   
388    /*
389    * (non-Javadoc)
390    * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
391    */
 
392  90 toggle @Override
393    public ServiceInfo getServiceInfo(boolean persistent) {
394  90 ServiceInfoImpl info = new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, (byte[]) null);
395    // info.setAddress(_addr); This is done in the sub class so we don't have to test for class type
396  90 return info;
397    }
398   
399    /*
400    * (non-Javadoc)
401    * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
402    */
 
403  41 toggle @Override
404    public ServiceEvent getServiceEvent(JmDNSImpl dns) {
405  41 ServiceInfo info = this.getServiceInfo(false);
406  41 ((ServiceInfoImpl) info).setDns(dns);
407  41 return new ServiceEventImpl(dns, info.getType(), info.getName(), info);
408    }
409   
410    /*
411    * (non-Javadoc)
412    * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
413    */
 
414  0 toggle @Override
415    protected void toString(StringBuilder aLog) {
416  0 super.toString(aLog);
417  0 aLog.append(" address: '" + (this.getAddress() != null ? this.getAddress().getHostAddress() : "null") + "'");
418    }
419   
420    }
421   
422    /**
423    * Pointer record.
424    */
 
425    public static class Pointer extends DNSRecord {
426    // private static Logger logger = Logger.getLogger(Pointer.class.getName());
427    private final String _alias;
428   
 
429  383 toggle public Pointer(String name, DNSRecordClass recordClass, boolean unique, int ttl, String alias) {
430  377 super(name, DNSRecordType.TYPE_PTR, recordClass, unique, ttl);
431  383 this._alias = alias;
432    }
433   
434    /*
435    * (non-Javadoc)
436    * @see javax.jmdns.impl.DNSEntry#isSameEntry(javax.jmdns.impl.DNSEntry)
437    */
 
438  173 toggle @Override
439    public boolean isSameEntry(DNSEntry entry) {
440  175 return super.isSameEntry(entry) && (entry instanceof Pointer) && this.sameValue((Pointer) entry);
441    }
442   
 
443  288 toggle @Override
444    void write(MessageOutputStream out) {
445  288 out.writeName(_alias);
446    }
447   
 
448  294 toggle @Override
449    boolean sameValue(DNSRecord other) {
450  294 if (! (other instanceof Pointer) ) {
451  0 return false;
452    }
453  294 Pointer pointer = (Pointer) other;
454  299 if ((_alias == null) && (pointer._alias != null)) {
455  0 return false;
456    }
457  300 return _alias.equals(pointer._alias);
458    }
459   
 
460  0 toggle @Override
461    public boolean isSingleValued() {
462  0 return false;
463    }
464   
 
465  18 toggle @Override
466    boolean handleQuery(JmDNSImpl dns, long expirationTime) {
467    // Nothing to do (?)
468    // I think there is no possibility for conflicts for this record type?
469  18 return false;
470    }
471   
 
472  138 toggle @Override
473    boolean handleResponse(JmDNSImpl dns) {
474    // Nothing to do (?)
475    // I think there is no possibility for conflicts for this record type?
476  139 return false;
477    }
478   
 
479  218 toggle String getAlias() {
480  219 return _alias;
481    }
482   
 
483  0 toggle @Override
484    DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException {
485  0 return out;
486    }
487   
488    /*
489    * (non-Javadoc)
490    * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
491    */
 
492  105 toggle @Override
493    public ServiceInfo getServiceInfo(boolean persistent) {
494  106 if (this.isServicesDiscoveryMetaQuery()) {
495    // The service name is in the alias
496  0 Map<Fields, String> map = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getAlias());
497  0 return new ServiceInfoImpl(map, 0, 0, 0, persistent, (byte[]) null);
498  107 } else if (this.isReverseLookup()) {
499  17 return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, (byte[]) null);
500  88 } else if (this.isDomainDiscoveryQuery()) {
501    // FIXME [PJYF Nov 16 2010] We do not currently support domain discovery
502  0 return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, (byte[]) null);
503    }
504  89 Map<Fields, String> map = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getAlias());
505  89 map.put(Fields.Subtype, this.getQualifiedNameMap().get(Fields.Subtype));
506  89 return new ServiceInfoImpl(map, 0, 0, 0, persistent, this.getAlias());
507    }
508   
509    /*
510    * (non-Javadoc)
511    * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
512    */
 
513  41 toggle @Override
514    public ServiceEvent getServiceEvent(JmDNSImpl dns) {
515  41 ServiceInfo info = this.getServiceInfo(false);
516  41 ((ServiceInfoImpl) info).setDns(dns);
517  41 String domainName = info.getType();
518  41 String serviceName = JmDNSImpl.toUnqualifiedName(domainName, this.getAlias());
519  41 return new ServiceEventImpl(dns, domainName, serviceName, info);
520    }
521   
522    /*
523    * (non-Javadoc)
524    * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
525    */
 
526  0 toggle @Override
527    protected void toString(StringBuilder aLog) {
528  0 super.toString(aLog);
529  0 aLog.append(" alias: '" + (_alias != null ? _alias.toString() : "null") + "'");
530    }
531   
532    }
533   
534    public final static byte[] EMPTY_TXT = new byte[] { 0 };
535   
 
536    public static class Text extends DNSRecord {
537    // private static Logger logger = Logger.getLogger(Text.class.getName());
538    private final byte[] _text;
539   
 
540  267 toggle public Text(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte text[]) {
541  267 super(name, DNSRecordType.TYPE_TXT, recordClass, unique, ttl);
542  266 this._text = (text != null && text.length > 0 ? text : EMPTY_TXT);
543    }
544   
545    /**
546    * @return the text
547    */
 
548  26 toggle byte[] getText() {
549  26 return this._text;
550    }
551   
 
552  276 toggle @Override
553    void write(MessageOutputStream out) {
554  276 out.writeBytes(_text, 0, _text.length);
555    }
556   
 
557  96 toggle @Override
558    boolean sameValue(DNSRecord other) {
559  96 if (! (other instanceof Text) ) {
560  0 return false;
561    }
562  97 Text txt = (Text) other;
563  97 if ((_text == null) && (txt._text != null)) {
564  0 return false;
565    }
566  97 if (txt._text.length != _text.length) {
567  8 return false;
568    }
569  3304 for (int i = _text.length; i-- > 0;) {
570  3215 if (txt._text[i] != _text[i]) {
571  0 return false;
572    }
573    }
574  89 return true;
575    }
576   
 
577  7 toggle @Override
578    public boolean isSingleValued() {
579  8 return true;
580    }
581   
 
582  0 toggle @Override
583    boolean handleQuery(JmDNSImpl dns, long expirationTime) {
584    // Nothing to do (?)
585    // I think there is no possibility for conflicts for this record type?
586  0 return false;
587    }
588   
 
589  130 toggle @Override
590    boolean handleResponse(JmDNSImpl dns) {
591    // Nothing to do (?)
592    // Shouldn't we care if we get a conflict at this level?
593    /*
594    * ServiceInfo info = (ServiceInfo) dns.services.get(name.toLowerCase()); if (info != null) { if (! Arrays.equals(text,info.text)) { info.revertState(); return true; } }
595    */
596  130 return false;
597    }
598   
 
599  0 toggle @Override
600    DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException {
601  0 return out;
602    }
603   
604    /*
605    * (non-Javadoc)
606    * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
607    */
 
608  41 toggle @Override
609    public ServiceInfo getServiceInfo(boolean persistent) {
610  41 return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, _text);
611    }
612   
613    /*
614    * (non-Javadoc)
615    * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
616    */
 
617  26 toggle @Override
618    public ServiceEvent getServiceEvent(JmDNSImpl dns) {
619  26 ServiceInfo info = this.getServiceInfo(false);
620  26 ((ServiceInfoImpl) info).setDns(dns);
621  26 return new ServiceEventImpl(dns, info.getType(), info.getName(), info);
622    }
623   
624    /*
625    * (non-Javadoc)
626    * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
627    */
 
628  0 toggle @Override
629    protected void toString(StringBuilder aLog) {
630  0 super.toString(aLog);
631  0 aLog.append(" text: '" + ((_text.length > 20) ? new String(_text, 0, 17) + "..." : new String(_text)) + "'");
632    }
633   
634    }
635   
636    /**
637    * Service record.
638    */
 
639    public static class Service extends DNSRecord {
640    private static Logger logger1 = Logger.getLogger(Service.class.getName());
641    private final int _priority;
642    private final int _weight;
643    private final int _port;
644    private final String _server;
645   
 
646  409 toggle public Service(String name, DNSRecordClass recordClass, boolean unique, int ttl, int priority, int weight, int port, String server) {
647  406 super(name, DNSRecordType.TYPE_SRV, recordClass, unique, ttl);
648  410 this._priority = priority;
649  411 this._weight = weight;
650  411 this._port = port;
651  411 this._server = server;
652    }
653   
 
654  398 toggle @Override
655    void write(MessageOutputStream out) {
656  398 out.writeShort(_priority);
657  398 out.writeShort(_weight);
658  398 out.writeShort(_port);
659  398 if (DNSIncoming.USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) {
660  398 out.writeName(_server);
661    } else {
662    // [PJYF Nov 13 2010] Do we still need this? This looks really bad. All label are supposed to start by a length.
663  0 out.writeUTF(_server, 0, _server.length());
664   
665    // add a zero byte to the end just to be safe, this is the strange form
666    // used by the BonjourConformanceTest
667  0 out.writeByte(0);
668    }
669    }
670   
 
671  0 toggle @Override
672    protected void toByteArray(DataOutputStream dout) throws IOException {
673  0 super.toByteArray(dout);
674  0 dout.writeShort(_priority);
675  0 dout.writeShort(_weight);
676  0 dout.writeShort(_port);
677  0 try {
678  0 dout.write(_server.getBytes("UTF-8"));
679    } catch (UnsupportedEncodingException exception) {
680    /* UTF-8 is always present */
681    }
682    }
683   
 
684  23 toggle String getServer() {
685  23 return _server;
686    }
687   
688    /**
689    * @return the priority
690    */
 
691  22 toggle public int getPriority() {
692  22 return this._priority;
693    }
694   
695    /**
696    * @return the weight
697    */
 
698  22 toggle public int getWeight() {
699  22 return this._weight;
700    }
701   
702    /**
703    * @return the port
704    */
 
705  22 toggle public int getPort() {
706  22 return this._port;
707    }
708   
 
709  87 toggle @Override
710    boolean sameValue(DNSRecord other) {
711  88 if (! (other instanceof Service) ) {
712  0 return false;
713    }
714  87 Service s = (Service) other;
715  88 return (_priority == s._priority) && (_weight == s._weight) && (_port == s._port) && _server.equals(s._server);
716    }
717   
 
718  0 toggle @Override
719    public boolean isSingleValued() {
720  0 return true;
721    }
722   
 
723  77 toggle @Override
724    boolean handleQuery(JmDNSImpl dns, long expirationTime) {
725  77 ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(this.getKey());
726  81 if (info != null && (info.isAnnouncing() || info.isAnnounced()) && (_port != info.getPort() || !_server.equalsIgnoreCase(dns.getLocalHost().getName()))) {
727  0 logger1.finer("handleQuery() Conflicting probe detected from: " + getRecordSource());
728  0 DNSRecord.Service localService = new DNSRecord.Service(info.getQualifiedName(), DNSRecordClass.CLASS_IN, DNSRecordClass.UNIQUE, DNSConstants.DNS_TTL, info.getPriority(), info.getWeight(), info.getPort(), dns.getLocalHost().getName());
729   
730    // This block is useful for debugging race conditions when jmdns is responding to itself.
731  0 try {
732  0 if (dns.getInterface().equals(getRecordSource())) {
733  0 logger1.warning("Got conflicting probe from ourselves\n" + "incoming: " + this.toString() + "\n" + "local : " + localService.toString());
734    }
735    } catch (IOException e) {
736  0 logger1.log(Level.WARNING, "IOException", e);
737    }
738   
739  0 int comparison = this.compareTo(localService);
740   
741  0 if (comparison == 0) {
742    // the 2 records are identical this probably means we are seeing our own record.
743    // With multiple interfaces on a single computer it is possible to see our
744    // own records come in on different interfaces than the ones they were sent on.
745    // see section "10. Conflict Resolution" of mdns draft spec.
746  0 logger1.finer("handleQuery() Ignoring a identical service query");
747  0 return false;
748    }
749   
750    // Tie breaker test
751  0 if (info.isProbing() && comparison > 0) {
752    // We lost the tie break
753  0 String oldName = info.getQualifiedName().toLowerCase();
754  0 info.setName(dns.incrementName(info.getName()));
755  0 dns.getServices().remove(oldName);
756  0 dns.getServices().put(info.getQualifiedName().toLowerCase(), info);
757  0 logger1.finer("handleQuery() Lost tie break: new unique name chosen:" + info.getName());
758   
759    // We revert the state to start probing again with the new name
760  0 info.revertState();
761    } else {
762    // We won the tie break, so this conflicting probe should be ignored
763    // See paragraph 3 of section 9.2 in mdns draft spec
764  0 return false;
765    }
766   
767  0 return true;
768   
769    }
770  81 return false;
771    }
772   
 
773  128 toggle @Override
774    boolean handleResponse(JmDNSImpl dns) {
775  129 ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(this.getKey());
776  130 if (info != null && (_port != info.getPort() || !_server.equalsIgnoreCase(dns.getLocalHost().getName()))) {
777  0 logger1.finer("handleResponse() Denial detected");
778   
779  0 if (info.isProbing()) {
780  0 String oldName = info.getQualifiedName().toLowerCase();
781  0 info.setName(dns.incrementName(info.getName()));
782  0 dns.getServices().remove(oldName);
783  0 dns.getServices().put(info.getQualifiedName().toLowerCase(), info);
784  0 logger1.finer("handleResponse() New unique name chose:" + info.getName());
785   
786    }
787  0 info.revertState();
788  0 return true;
789    }
790  130 return false;
791    }
792   
 
793  0 toggle @Override
794    DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException {
795  0 ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(this.getKey());
796  0 if (info != null) {
797  0 if (this._port == info.getPort() != _server.equals(dns.getLocalHost().getName())) {
798  0 return dns.addAnswer(in, addr, port, out, new DNSRecord.Service(info.getQualifiedName(), DNSRecordClass.CLASS_IN, DNSRecordClass.UNIQUE, DNSConstants.DNS_TTL, info.getPriority(), info.getWeight(), info.getPort(), dns
799    .getLocalHost().getName()));
800    }
801    }
802  0 return out;
803    }
804   
805    /*
806    * (non-Javadoc)
807    * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
808    */
 
809  24 toggle @Override
810    public ServiceInfo getServiceInfo(boolean persistent) {
811  24 return new ServiceInfoImpl(this.getQualifiedNameMap(), _port, _weight, _priority, persistent, _server);
812    }
813   
814    /*
815    * (non-Javadoc)
816    * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
817    */
 
818  1 toggle @Override
819    public ServiceEvent getServiceEvent(JmDNSImpl dns) {
820  1 ServiceInfo info = this.getServiceInfo(false);
821  1 ((ServiceInfoImpl) info).setDns(dns);
822    // String domainName = "";
823    // String serviceName = this.getServer();
824    // int index = serviceName.indexOf('.');
825    // if (index > 0)
826    // {
827    // serviceName = this.getServer().substring(0, index);
828    // if (index + 1 < this.getServer().length())
829    // domainName = this.getServer().substring(index + 1);
830    // }
831    // return new ServiceEventImpl(dns, domainName, serviceName, info);
832  1 return new ServiceEventImpl(dns, info.getType(), info.getName(), info);
833   
834    }
835   
836    /*
837    * (non-Javadoc)
838    * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
839    */
 
840  0 toggle @Override
841    protected void toString(StringBuilder aLog) {
842  0 super.toString(aLog);
843  0 aLog.append(" server: '" + _server + ":" + _port + "'");
844    }
845   
846    }
847   
 
848    public static class HostInformation extends DNSRecord {
849    String _os;
850    String _cpu;
851   
852    /**
853    * @param name
854    * @param recordClass
855    * @param unique
856    * @param ttl
857    * @param cpu
858    * @param os
859    */
 
860  0 toggle public HostInformation(String name, DNSRecordClass recordClass, boolean unique, int ttl, String cpu, String os) {
861  0 super(name, DNSRecordType.TYPE_HINFO, recordClass, unique, ttl);
862  0 _cpu = cpu;
863  0 _os = os;
864    }
865   
866    /*
867    * (non-Javadoc)
868    * @see javax.jmdns.impl.DNSRecord#addAnswer(javax.jmdns.impl.JmDNSImpl, javax.jmdns.impl.DNSIncoming, java.net.InetAddress, int, javax.jmdns.impl.DNSOutgoing)
869    */
 
870  0 toggle @Override
871    DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException {
872  0 return out;
873    }
874   
875    /*
876    * (non-Javadoc)
877    * @see javax.jmdns.impl.DNSRecord#handleQuery(javax.jmdns.impl.JmDNSImpl, long)
878    */
 
879  0 toggle @Override
880    boolean handleQuery(JmDNSImpl dns, long expirationTime) {
881  0 return false;
882    }
883   
884    /*
885    * (non-Javadoc)
886    * @see javax.jmdns.impl.DNSRecord#handleResponse(javax.jmdns.impl.JmDNSImpl)
887    */
 
888  0 toggle @Override
889    boolean handleResponse(JmDNSImpl dns) {
890  0 return false;
891    }
892   
893    /*
894    * (non-Javadoc)
895    * @see javax.jmdns.impl.DNSRecord#sameValue(javax.jmdns.impl.DNSRecord)
896    */
 
897  0 toggle @Override
898    boolean sameValue(DNSRecord other) {
899  0 if (! (other instanceof HostInformation) ) {
900  0 return false;
901    }
902  0 HostInformation hinfo = (HostInformation) other;
903  0 if ((_cpu == null) && (hinfo._cpu != null)) {
904  0 return false;
905    }
906  0 if ((_os == null) && (hinfo._os != null)) {
907  0 return false;
908    }
909  0 return _cpu.equals(hinfo._cpu) && _os.equals(hinfo._os);
910    }
911   
912    /*
913    * (non-Javadoc)
914    * @see javax.jmdns.impl.DNSRecord#isSingleValued()
915    */
 
916  0 toggle @Override
917    public boolean isSingleValued() {
918  0 return true;
919    }
920   
921    /*
922    * (non-Javadoc)
923    * @see javax.jmdns.impl.DNSRecord#write(javax.jmdns.impl.DNSOutgoing)
924    */
 
925  0 toggle @Override
926    void write(MessageOutputStream out) {
927  0 String hostInfo = _cpu + " " + _os;
928  0 out.writeUTF(hostInfo, 0, hostInfo.length());
929    }
930   
931    /*
932    * (non-Javadoc)
933    * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean)
934    */
 
935  0 toggle @Override
936    public ServiceInfo getServiceInfo(boolean persistent) {
937  0 Map<String, String> hinfo = new HashMap<String, String>(2);
938  0 hinfo.put("cpu", _cpu);
939  0 hinfo.put("os", _os);
940  0 return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, hinfo);
941    }
942   
943    /*
944    * (non-Javadoc)
945    * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl)
946    */
 
947  0 toggle @Override
948    public ServiceEvent getServiceEvent(JmDNSImpl dns) {
949  0 ServiceInfo info = this.getServiceInfo(false);
950  0 ((ServiceInfoImpl) info).setDns(dns);
951  0 return new ServiceEventImpl(dns, info.getType(), info.getName(), info);
952    }
953   
954    /*
955    * (non-Javadoc)
956    * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
957    */
 
958  0 toggle @Override
959    protected void toString(StringBuilder aLog) {
960  0 super.toString(aLog);
961  0 aLog.append(" cpu: '" + _cpu + "' os: '" + _os + "'");
962    }
963   
964    }
965   
966    /**
967    * Determine if a record can have multiple values in the cache.
968    *
969    * @return <code>false</code> if this record can have multiple values in the cache, <code>true</code> otherwise.
970    */
971    public abstract boolean isSingleValued();
972   
973    /**
974    * Return a service information associated with that record if appropriate.
975    *
976    * @return service information
977    */
 
978  48 toggle public ServiceInfo getServiceInfo() {
979  48 return this.getServiceInfo(false);
980    }
981   
982    /**
983    * Return a service information associated with that record if appropriate.
984    *
985    * @param persistent
986    * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received.
987    * @return service information
988    */
989    public abstract ServiceInfo getServiceInfo(boolean persistent);
990   
991    /**
992    * Creates and return a service event for this record.
993    *
994    * @param dns
995    * DNS serviced by this event
996    * @return service event
997    */
998    public abstract ServiceEvent getServiceEvent(JmDNSImpl dns);
999   
 
1000  941 toggle public void setRecordSource(InetAddress source) {
1001  951 this._source = source;
1002    }
1003   
 
1004  0 toggle public InetAddress getRecordSource() {
1005  0 return _source;
1006    }
1007   
1008    /*
1009    * (non-Javadoc)
1010    * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder)
1011    */
 
1012  0 toggle @Override
1013    protected void toString(StringBuilder aLog) {
1014  0 super.toString(aLog);
1015  0 aLog.append(" ttl: '" + getRemainingTTL(System.currentTimeMillis()) + "/" + _ttl + "'");
1016    }
1017   
 
1018  0 toggle public void setTTL(int ttl) {
1019  0 this._ttl = ttl;
1020    }
1021   
 
1022  681 toggle public int getTTL() {
1023  681 return _ttl;
1024    }
1025    }