diff options
Diffstat (limited to 'src/javax/jmdns/impl/DNSRecord.java')
-rw-r--r-- | src/javax/jmdns/impl/DNSRecord.java | 1025 |
1 files changed, 1025 insertions, 0 deletions
diff --git a/src/javax/jmdns/impl/DNSRecord.java b/src/javax/jmdns/impl/DNSRecord.java new file mode 100644 index 0000000..d0ff6f3 --- /dev/null +++ b/src/javax/jmdns/impl/DNSRecord.java @@ -0,0 +1,1025 @@ +// Copyright 2003-2005 Arthur van Hoff, Rick Blair +// Licensed under Apache License version 2.0 +// Original license LGPL + +package javax.jmdns.impl; + +import java.io.DataOutputStream; +import java.io.IOException; +import java.io.UnsupportedEncodingException; +import java.net.Inet4Address; +import java.net.Inet6Address; +import java.net.InetAddress; +import java.net.UnknownHostException; +import java.util.HashMap; +import java.util.Map; +import java.util.logging.Level; +import java.util.logging.Logger; + +import javax.jmdns.ServiceEvent; +import javax.jmdns.ServiceInfo; +import javax.jmdns.ServiceInfo.Fields; +import javax.jmdns.impl.DNSOutgoing.MessageOutputStream; +import javax.jmdns.impl.constants.DNSConstants; +import javax.jmdns.impl.constants.DNSRecordClass; +import javax.jmdns.impl.constants.DNSRecordType; + +/** + * DNS record + * + * @author Arthur van Hoff, Rick Blair, Werner Randelshofer, Pierre Frisch + */ +public abstract class DNSRecord extends DNSEntry { + private static Logger logger = Logger.getLogger(DNSRecord.class.getName()); + private int _ttl; + private long _created; + + /** + * This source is mainly for debugging purposes, should be the address that sent this record. + */ + private InetAddress _source; + + /** + * Create a DNSRecord with a name, type, class, and ttl. + */ + DNSRecord(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl) { + super(name, type, recordClass, unique); + this._ttl = ttl; + this._created = System.currentTimeMillis(); + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSEntry#equals(java.lang.Object) + */ + @Override + public boolean equals(Object other) { + return (other instanceof DNSRecord) && super.equals(other) && sameValue((DNSRecord) other); + } + + /** + * True if this record has the same value as some other record. + */ + abstract boolean sameValue(DNSRecord other); + + /** + * True if this record has the same type as some other record. + */ + boolean sameType(DNSRecord other) { + return this.getRecordType() == other.getRecordType(); + } + + /** + * Handles a query represented by this record. + * + * @return Returns true if a conflict with one of the services registered with JmDNS or with the hostname occured. + */ + abstract boolean handleQuery(JmDNSImpl dns, long expirationTime); + + /** + * Handles a response represented by this record. + * + * @return Returns true if a conflict with one of the services registered with JmDNS or with the hostname occured. + */ + abstract boolean handleResponse(JmDNSImpl dns); + + /** + * Adds this as an answer to the provided outgoing datagram. + */ + abstract DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException; + + /** + * True if this record is suppressed by the answers in a message. + */ + boolean suppressedBy(DNSIncoming msg) { + try { + for (DNSRecord answer : msg.getAllAnswers()) { + if (suppressedBy(answer)) { + return true; + } + } + return false; + } catch (ArrayIndexOutOfBoundsException e) { + logger.log(Level.WARNING, "suppressedBy() message " + msg + " exception ", e); + // msg.print(true); + return false; + } + } + + /** + * True if this record would be suppressed by an answer. This is the case if this record would not have a significantly longer TTL. + */ + boolean suppressedBy(DNSRecord other) { + if (this.equals(other) && (other._ttl > _ttl / 2)) { + return true; + } + return false; + } + + /** + * Get the expiration time of this record. + */ + long getExpirationTime(int percent) { + // ttl is in seconds the constant 10 is 1000 ms / 100 % + return _created + (percent * _ttl * 10L); + } + + /** + * Get the remaining TTL for this record. + */ + int getRemainingTTL(long now) { + return (int) Math.max(0, (getExpirationTime(100) - now) / 1000); + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSEntry#isExpired(long) + */ + @Override + public boolean isExpired(long now) { + return getExpirationTime(100) <= now; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSEntry#isStale(long) + */ + @Override + public boolean isStale(long now) { + return getExpirationTime(50) <= now; + } + + /** + * Reset the TTL of a record. This avoids having to update the entire record in the cache. + */ + void resetTTL(DNSRecord other) { + _created = other._created; + _ttl = other._ttl; + } + + /** + * When a record flushed we don't remove it immediately, but mark it for rapid decay. + */ + void setWillExpireSoon(long now) { + _created = now; + _ttl = DNSConstants.RECORD_EXPIRY_DELAY; + } + + /** + * Write this record into an outgoing message. + */ + abstract void write(MessageOutputStream out); + + public static class IPv4Address extends Address { + + IPv4Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) { + super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, addr); + } + + IPv4Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) { + super(name, DNSRecordType.TYPE_A, recordClass, unique, ttl, rawAddress); + } + + @Override + void write(MessageOutputStream out) { + if (_addr != null) { + byte[] buffer = _addr.getAddress(); + // If we have a type A records we should answer with a IPv4 address + if (_addr instanceof Inet4Address) { + // All is good + } else { + // Get the last four bytes + byte[] tempbuffer = buffer; + buffer = new byte[4]; + System.arraycopy(tempbuffer, 12, buffer, 0, 4); + } + int length = buffer.length; + out.writeBytes(buffer, 0, length); + } + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean) + */ + @Override + public ServiceInfo getServiceInfo(boolean persistent) { + + ServiceInfoImpl info = (ServiceInfoImpl) super.getServiceInfo(persistent); + info.addAddress((Inet4Address) _addr); + return info; + } + + } + + public static class IPv6Address extends Address { + + IPv6Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) { + super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, addr); + } + + IPv6Address(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) { + super(name, DNSRecordType.TYPE_AAAA, recordClass, unique, ttl, rawAddress); + } + + @Override + void write(MessageOutputStream out) { + if (_addr != null) { + byte[] buffer = _addr.getAddress(); + // If we have a type AAAA records we should answer with a IPv6 address + if (_addr instanceof Inet4Address) { + byte[] tempbuffer = buffer; + buffer = new byte[16]; + for (int i = 0; i < 16; i++) { + if (i < 11) { + buffer[i] = tempbuffer[i - 12]; + } else { + buffer[i] = 0; + } + } + } + int length = buffer.length; + out.writeBytes(buffer, 0, length); + } + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean) + */ + @Override + public ServiceInfo getServiceInfo(boolean persistent) { + + ServiceInfoImpl info = (ServiceInfoImpl) super.getServiceInfo(persistent); + info.addAddress((Inet6Address) _addr); + return info; + } + + } + + /** + * Address record. + */ + public static abstract class Address extends DNSRecord { + private static Logger logger1 = Logger.getLogger(Address.class.getName()); + + InetAddress _addr; + + protected Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl, InetAddress addr) { + super(name, type, recordClass, unique, ttl); + this._addr = addr; + } + + protected Address(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique, int ttl, byte[] rawAddress) { + super(name, type, recordClass, unique, ttl); + try { + this._addr = InetAddress.getByAddress(rawAddress); + } catch (UnknownHostException exception) { + logger1.log(Level.WARNING, "Address() exception ", exception); + } + } + + boolean same(DNSRecord other) { + if (! (other instanceof Address) ) { + return false; + } + return ((sameName(other)) && ((sameValue(other)))); + } + + boolean sameName(DNSRecord other) { + return this.getName().equalsIgnoreCase(other.getName()); + } + + @Override + boolean sameValue(DNSRecord other) { + if (! (other instanceof Address) ) { + return false; + } + Address address = (Address) other; + if ((this.getAddress() == null) && (address.getAddress() != null)) { + return false; + } + return this.getAddress().equals(address.getAddress()); + } + + @Override + public boolean isSingleValued() { + return false; + } + + InetAddress getAddress() { + return _addr; + } + + /** + * 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. + */ + @Override + protected void toByteArray(DataOutputStream dout) throws IOException { + super.toByteArray(dout); + byte[] buffer = this.getAddress().getAddress(); + for (int i = 0; i < buffer.length; i++) { + dout.writeByte(buffer[i]); + } + } + + /** + * Does the necessary actions, when this as a query. + */ + @Override + boolean handleQuery(JmDNSImpl dns, long expirationTime) { + if (dns.getLocalHost().conflictWithRecord(this)) { + DNSRecord.Address localAddress = dns.getLocalHost().getDNSAddressRecord(this.getRecordType(), this.isUnique(), DNSConstants.DNS_TTL); + int comparison = this.compareTo(localAddress); + + if (comparison == 0) { + // the 2 records are identical this probably means we are seeing our own record. + // With multiple interfaces on a single computer it is possible to see our + // own records come in on different interfaces than the ones they were sent on. + // see section "10. Conflict Resolution" of mdns draft spec. + logger1.finer("handleQuery() Ignoring an identical address query"); + return false; + } + + logger1.finer("handleQuery() Conflicting query detected."); + // Tie breaker test + if (dns.isProbing() && comparison > 0) { + // We lost the tie-break. We have to choose a different name. + dns.getLocalHost().incrementHostName(); + dns.getCache().clear(); + for (ServiceInfo serviceInfo : dns.getServices().values()) { + ServiceInfoImpl info = (ServiceInfoImpl) serviceInfo; + info.revertState(); + } + } + dns.revertState(); + return true; + } + return false; + } + + /** + * Does the necessary actions, when this as a response. + */ + @Override + boolean handleResponse(JmDNSImpl dns) { + if (dns.getLocalHost().conflictWithRecord(this)) { + logger1.finer("handleResponse() Denial detected"); + + if (dns.isProbing()) { + dns.getLocalHost().incrementHostName(); + dns.getCache().clear(); + for (ServiceInfo serviceInfo : dns.getServices().values()) { + ServiceInfoImpl info = (ServiceInfoImpl) serviceInfo; + info.revertState(); + } + } + dns.revertState(); + return true; + } + return false; + } + + @Override + DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException { + return out; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean) + */ + @Override + public ServiceInfo getServiceInfo(boolean persistent) { + ServiceInfoImpl info = new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, (byte[]) null); + // info.setAddress(_addr); This is done in the sub class so we don't have to test for class type + return info; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl) + */ + @Override + public ServiceEvent getServiceEvent(JmDNSImpl dns) { + ServiceInfo info = this.getServiceInfo(false); + ((ServiceInfoImpl) info).setDns(dns); + return new ServiceEventImpl(dns, info.getType(), info.getName(), info); + } + + /* + * (non-Javadoc) + * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder) + */ + @Override + protected void toString(StringBuilder aLog) { + super.toString(aLog); + aLog.append(" address: '" + (this.getAddress() != null ? this.getAddress().getHostAddress() : "null") + "'"); + } + + } + + /** + * Pointer record. + */ + public static class Pointer extends DNSRecord { + // private static Logger logger = Logger.getLogger(Pointer.class.getName()); + private final String _alias; + + public Pointer(String name, DNSRecordClass recordClass, boolean unique, int ttl, String alias) { + super(name, DNSRecordType.TYPE_PTR, recordClass, unique, ttl); + this._alias = alias; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSEntry#isSameEntry(javax.jmdns.impl.DNSEntry) + */ + @Override + public boolean isSameEntry(DNSEntry entry) { + return super.isSameEntry(entry) && (entry instanceof Pointer) && this.sameValue((Pointer) entry); + } + + @Override + void write(MessageOutputStream out) { + out.writeName(_alias); + } + + @Override + boolean sameValue(DNSRecord other) { + if (! (other instanceof Pointer) ) { + return false; + } + Pointer pointer = (Pointer) other; + if ((_alias == null) && (pointer._alias != null)) { + return false; + } + return _alias.equals(pointer._alias); + } + + @Override + public boolean isSingleValued() { + return false; + } + + @Override + boolean handleQuery(JmDNSImpl dns, long expirationTime) { + // Nothing to do (?) + // I think there is no possibility for conflicts for this record type? + return false; + } + + @Override + boolean handleResponse(JmDNSImpl dns) { + // Nothing to do (?) + // I think there is no possibility for conflicts for this record type? + return false; + } + + String getAlias() { + return _alias; + } + + @Override + DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException { + return out; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean) + */ + @Override + public ServiceInfo getServiceInfo(boolean persistent) { + if (this.isServicesDiscoveryMetaQuery()) { + // The service name is in the alias + Map<Fields, String> map = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getAlias()); + return new ServiceInfoImpl(map, 0, 0, 0, persistent, (byte[]) null); + } else if (this.isReverseLookup()) { + return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, (byte[]) null); + } else if (this.isDomainDiscoveryQuery()) { + // FIXME [PJYF Nov 16 2010] We do not currently support domain discovery + return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, (byte[]) null); + } + Map<Fields, String> map = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getAlias()); + map.put(Fields.Subtype, this.getQualifiedNameMap().get(Fields.Subtype)); + return new ServiceInfoImpl(map, 0, 0, 0, persistent, this.getAlias()); + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl) + */ + @Override + public ServiceEvent getServiceEvent(JmDNSImpl dns) { + ServiceInfo info = this.getServiceInfo(false); + ((ServiceInfoImpl) info).setDns(dns); + String domainName = info.getType(); + String serviceName = JmDNSImpl.toUnqualifiedName(domainName, this.getAlias()); + return new ServiceEventImpl(dns, domainName, serviceName, info); + } + + /* + * (non-Javadoc) + * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder) + */ + @Override + protected void toString(StringBuilder aLog) { + super.toString(aLog); + aLog.append(" alias: '" + (_alias != null ? _alias.toString() : "null") + "'"); + } + + } + + public final static byte[] EMPTY_TXT = new byte[] { 0 }; + + public static class Text extends DNSRecord { + // private static Logger logger = Logger.getLogger(Text.class.getName()); + private final byte[] _text; + + public Text(String name, DNSRecordClass recordClass, boolean unique, int ttl, byte text[]) { + super(name, DNSRecordType.TYPE_TXT, recordClass, unique, ttl); + this._text = (text != null && text.length > 0 ? text : EMPTY_TXT); + } + + /** + * @return the text + */ + byte[] getText() { + return this._text; + } + + @Override + void write(MessageOutputStream out) { + out.writeBytes(_text, 0, _text.length); + } + + @Override + boolean sameValue(DNSRecord other) { + if (! (other instanceof Text) ) { + return false; + } + Text txt = (Text) other; + if ((_text == null) && (txt._text != null)) { + return false; + } + if (txt._text.length != _text.length) { + return false; + } + for (int i = _text.length; i-- > 0;) { + if (txt._text[i] != _text[i]) { + return false; + } + } + return true; + } + + @Override + public boolean isSingleValued() { + return true; + } + + @Override + boolean handleQuery(JmDNSImpl dns, long expirationTime) { + // Nothing to do (?) + // I think there is no possibility for conflicts for this record type? + return false; + } + + @Override + boolean handleResponse(JmDNSImpl dns) { + // Nothing to do (?) + // Shouldn't we care if we get a conflict at this level? + /* + * ServiceInfo info = (ServiceInfo) dns.services.get(name.toLowerCase()); if (info != null) { if (! Arrays.equals(text,info.text)) { info.revertState(); return true; } } + */ + return false; + } + + @Override + DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException { + return out; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean) + */ + @Override + public ServiceInfo getServiceInfo(boolean persistent) { + return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, _text); + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl) + */ + @Override + public ServiceEvent getServiceEvent(JmDNSImpl dns) { + ServiceInfo info = this.getServiceInfo(false); + ((ServiceInfoImpl) info).setDns(dns); + return new ServiceEventImpl(dns, info.getType(), info.getName(), info); + } + + /* + * (non-Javadoc) + * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder) + */ + @Override + protected void toString(StringBuilder aLog) { + super.toString(aLog); + aLog.append(" text: '" + ((_text.length > 20) ? new String(_text, 0, 17) + "..." : new String(_text)) + "'"); + } + + } + + /** + * Service record. + */ + public static class Service extends DNSRecord { + private static Logger logger1 = Logger.getLogger(Service.class.getName()); + private final int _priority; + private final int _weight; + private final int _port; + private final String _server; + + public Service(String name, DNSRecordClass recordClass, boolean unique, int ttl, int priority, int weight, int port, String server) { + super(name, DNSRecordType.TYPE_SRV, recordClass, unique, ttl); + this._priority = priority; + this._weight = weight; + this._port = port; + this._server = server; + } + + @Override + void write(MessageOutputStream out) { + out.writeShort(_priority); + out.writeShort(_weight); + out.writeShort(_port); + if (DNSIncoming.USE_DOMAIN_NAME_FORMAT_FOR_SRV_TARGET) { + out.writeName(_server); + } else { + // [PJYF Nov 13 2010] Do we still need this? This looks really bad. All label are supposed to start by a length. + out.writeUTF(_server, 0, _server.length()); + + // add a zero byte to the end just to be safe, this is the strange form + // used by the BonjourConformanceTest + out.writeByte(0); + } + } + + @Override + protected void toByteArray(DataOutputStream dout) throws IOException { + super.toByteArray(dout); + dout.writeShort(_priority); + dout.writeShort(_weight); + dout.writeShort(_port); + try { + dout.write(_server.getBytes("UTF-8")); + } catch (UnsupportedEncodingException exception) { + /* UTF-8 is always present */ + } + } + + String getServer() { + return _server; + } + + /** + * @return the priority + */ + public int getPriority() { + return this._priority; + } + + /** + * @return the weight + */ + public int getWeight() { + return this._weight; + } + + /** + * @return the port + */ + public int getPort() { + return this._port; + } + + @Override + boolean sameValue(DNSRecord other) { + if (! (other instanceof Service) ) { + return false; + } + Service s = (Service) other; + return (_priority == s._priority) && (_weight == s._weight) && (_port == s._port) && _server.equals(s._server); + } + + @Override + public boolean isSingleValued() { + return true; + } + + @Override + boolean handleQuery(JmDNSImpl dns, long expirationTime) { + ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(this.getKey()); + if (info != null && (info.isAnnouncing() || info.isAnnounced()) && (_port != info.getPort() || !_server.equalsIgnoreCase(dns.getLocalHost().getName()))) { + logger1.finer("handleQuery() Conflicting probe detected from: " + getRecordSource()); + 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()); + + // This block is useful for debugging race conditions when jmdns is responding to itself. + try { + if (dns.getInetAddress().equals(getRecordSource())) { + logger1.warning("Got conflicting probe from ourselves\n" + "incoming: " + this.toString() + "\n" + "local : " + localService.toString()); + } + } catch (IOException e) { + logger1.log(Level.WARNING, "IOException", e); + } + + int comparison = this.compareTo(localService); + + if (comparison == 0) { + // the 2 records are identical this probably means we are seeing our own record. + // With multiple interfaces on a single computer it is possible to see our + // own records come in on different interfaces than the ones they were sent on. + // see section "10. Conflict Resolution" of mdns draft spec. + logger1.finer("handleQuery() Ignoring a identical service query"); + return false; + } + + // Tie breaker test + if (info.isProbing() && comparison > 0) { + // We lost the tie break + String oldName = info.getQualifiedName().toLowerCase(); + info.setName(dns.incrementName(info.getName())); + dns.getServices().remove(oldName); + dns.getServices().put(info.getQualifiedName().toLowerCase(), info); + logger1.finer("handleQuery() Lost tie break: new unique name chosen:" + info.getName()); + + // We revert the state to start probing again with the new name + info.revertState(); + } else { + // We won the tie break, so this conflicting probe should be ignored + // See paragraph 3 of section 9.2 in mdns draft spec + return false; + } + + return true; + + } + return false; + } + + @Override + boolean handleResponse(JmDNSImpl dns) { + ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(this.getKey()); + if (info != null && (_port != info.getPort() || !_server.equalsIgnoreCase(dns.getLocalHost().getName()))) { + logger1.finer("handleResponse() Denial detected"); + + if (info.isProbing()) { + String oldName = info.getQualifiedName().toLowerCase(); + info.setName(dns.incrementName(info.getName())); + dns.getServices().remove(oldName); + dns.getServices().put(info.getQualifiedName().toLowerCase(), info); + logger1.finer("handleResponse() New unique name chose:" + info.getName()); + + } + info.revertState(); + return true; + } + return false; + } + + @Override + DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException { + ServiceInfoImpl info = (ServiceInfoImpl) dns.getServices().get(this.getKey()); + if (info != null) { + if (this._port == info.getPort() != _server.equals(dns.getLocalHost().getName())) { + 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 + .getLocalHost().getName())); + } + } + return out; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean) + */ + @Override + public ServiceInfo getServiceInfo(boolean persistent) { + return new ServiceInfoImpl(this.getQualifiedNameMap(), _port, _weight, _priority, persistent, _server); + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl) + */ + @Override + public ServiceEvent getServiceEvent(JmDNSImpl dns) { + ServiceInfo info = this.getServiceInfo(false); + ((ServiceInfoImpl) info).setDns(dns); + // String domainName = ""; + // String serviceName = this.getServer(); + // int index = serviceName.indexOf('.'); + // if (index > 0) + // { + // serviceName = this.getServer().substring(0, index); + // if (index + 1 < this.getServer().length()) + // domainName = this.getServer().substring(index + 1); + // } + // return new ServiceEventImpl(dns, domainName, serviceName, info); + return new ServiceEventImpl(dns, info.getType(), info.getName(), info); + + } + + /* + * (non-Javadoc) + * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder) + */ + @Override + protected void toString(StringBuilder aLog) { + super.toString(aLog); + aLog.append(" server: '" + _server + ":" + _port + "'"); + } + + } + + public static class HostInformation extends DNSRecord { + String _os; + String _cpu; + + /** + * @param name + * @param recordClass + * @param unique + * @param ttl + * @param cpu + * @param os + */ + public HostInformation(String name, DNSRecordClass recordClass, boolean unique, int ttl, String cpu, String os) { + super(name, DNSRecordType.TYPE_HINFO, recordClass, unique, ttl); + _cpu = cpu; + _os = os; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#addAnswer(javax.jmdns.impl.JmDNSImpl, javax.jmdns.impl.DNSIncoming, java.net.InetAddress, int, javax.jmdns.impl.DNSOutgoing) + */ + @Override + DNSOutgoing addAnswer(JmDNSImpl dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException { + return out; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#handleQuery(javax.jmdns.impl.JmDNSImpl, long) + */ + @Override + boolean handleQuery(JmDNSImpl dns, long expirationTime) { + return false; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#handleResponse(javax.jmdns.impl.JmDNSImpl) + */ + @Override + boolean handleResponse(JmDNSImpl dns) { + return false; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#sameValue(javax.jmdns.impl.DNSRecord) + */ + @Override + boolean sameValue(DNSRecord other) { + if (! (other instanceof HostInformation) ) { + return false; + } + HostInformation hinfo = (HostInformation) other; + if ((_cpu == null) && (hinfo._cpu != null)) { + return false; + } + if ((_os == null) && (hinfo._os != null)) { + return false; + } + return _cpu.equals(hinfo._cpu) && _os.equals(hinfo._os); + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#isSingleValued() + */ + @Override + public boolean isSingleValued() { + return true; + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#write(javax.jmdns.impl.DNSOutgoing) + */ + @Override + void write(MessageOutputStream out) { + String hostInfo = _cpu + " " + _os; + out.writeUTF(hostInfo, 0, hostInfo.length()); + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceInfo(boolean) + */ + @Override + public ServiceInfo getServiceInfo(boolean persistent) { + Map<String, String> hinfo = new HashMap<String, String>(2); + hinfo.put("cpu", _cpu); + hinfo.put("os", _os); + return new ServiceInfoImpl(this.getQualifiedNameMap(), 0, 0, 0, persistent, hinfo); + } + + /* + * (non-Javadoc) + * @see javax.jmdns.impl.DNSRecord#getServiceEvent(javax.jmdns.impl.JmDNSImpl) + */ + @Override + public ServiceEvent getServiceEvent(JmDNSImpl dns) { + ServiceInfo info = this.getServiceInfo(false); + ((ServiceInfoImpl) info).setDns(dns); + return new ServiceEventImpl(dns, info.getType(), info.getName(), info); + } + + /* + * (non-Javadoc) + * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder) + */ + @Override + protected void toString(StringBuilder aLog) { + super.toString(aLog); + aLog.append(" cpu: '" + _cpu + "' os: '" + _os + "'"); + } + + } + + /** + * Determine if a record can have multiple values in the cache. + * + * @return <code>false</code> if this record can have multiple values in the cache, <code>true</code> otherwise. + */ + public abstract boolean isSingleValued(); + + /** + * Return a service information associated with that record if appropriate. + * + * @return service information + */ + public ServiceInfo getServiceInfo() { + return this.getServiceInfo(false); + } + + /** + * Return a service information associated with that record if appropriate. + * + * @param persistent + * if <code>true</code> ServiceListener.resolveService will be called whenever new new information is received. + * @return service information + */ + public abstract ServiceInfo getServiceInfo(boolean persistent); + + /** + * Creates and return a service event for this record. + * + * @param dns + * DNS serviced by this event + * @return service event + */ + public abstract ServiceEvent getServiceEvent(JmDNSImpl dns); + + public void setRecordSource(InetAddress source) { + this._source = source; + } + + public InetAddress getRecordSource() { + return _source; + } + + /* + * (non-Javadoc) + * @see com.webobjects.discoveryservices.DNSRecord#toString(java.lang.StringBuilder) + */ + @Override + protected void toString(StringBuilder aLog) { + super.toString(aLog); + aLog.append(" ttl: '" + getRemainingTTL(System.currentTimeMillis()) + "/" + _ttl + "'"); + } + + public void setTTL(int ttl) { + this._ttl = ttl; + } + + public int getTTL() { + return _ttl; + } +} |