| // 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.ByteArrayOutputStream; |
| import java.io.DataOutputStream; |
| import java.io.IOException; |
| import java.util.Collections; |
| import java.util.Map; |
| |
| import javax.jmdns.ServiceInfo.Fields; |
| import javax.jmdns.impl.constants.DNSRecordClass; |
| import javax.jmdns.impl.constants.DNSRecordType; |
| |
| /** |
| * DNS entry with a name, type, and class. This is the base class for questions and records. |
| * |
| * @author Arthur van Hoff, Pierre Frisch, Rick Blair |
| */ |
| public abstract class DNSEntry { |
| // private static Logger logger = Logger.getLogger(DNSEntry.class.getName()); |
| private final String _key; |
| |
| private final String _name; |
| |
| private final String _type; |
| |
| private final DNSRecordType _recordType; |
| |
| private final DNSRecordClass _dnsClass; |
| |
| private final boolean _unique; |
| |
| final Map<Fields, String> _qualifiedNameMap; |
| |
| /** |
| * Create an entry. |
| */ |
| DNSEntry(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) { |
| _name = name; |
| // _key = (name != null ? name.trim().toLowerCase() : null); |
| _recordType = type; |
| _dnsClass = recordClass; |
| _unique = unique; |
| _qualifiedNameMap = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getName()); |
| String domain = _qualifiedNameMap.get(Fields.Domain); |
| String protocol = _qualifiedNameMap.get(Fields.Protocol); |
| String application = _qualifiedNameMap.get(Fields.Application); |
| String instance = _qualifiedNameMap.get(Fields.Instance).toLowerCase(); |
| _type = (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + "."; |
| _key = ((instance.length() > 0 ? instance + "." : "") + _type).toLowerCase(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see java.lang.Object#equals(java.lang.Object) |
| */ |
| @Override |
| public boolean equals(Object obj) { |
| boolean result = false; |
| if (obj instanceof DNSEntry) { |
| DNSEntry other = (DNSEntry) obj; |
| result = this.getKey().equals(other.getKey()) && this.getRecordType().equals(other.getRecordType()) && this.getRecordClass() == other.getRecordClass(); |
| } |
| return result; |
| } |
| |
| /** |
| * Check if two entries have exactly the same name, type, and class. |
| * |
| * @param entry |
| * @return <code>true</code> if the two entries have are for the same record, <code>false</code> otherwise |
| */ |
| public boolean isSameEntry(DNSEntry entry) { |
| return this.getKey().equals(entry.getKey()) && this.getRecordType().equals(entry.getRecordType()) && ((DNSRecordClass.CLASS_ANY == entry.getRecordClass()) || this.getRecordClass().equals(entry.getRecordClass())); |
| } |
| |
| /** |
| * Check if two entries have the same subtype. |
| * |
| * @param other |
| * @return <code>true</code> if the two entries have are for the same subtype, <code>false</code> otherwise |
| */ |
| public boolean sameSubtype(DNSEntry other) { |
| return this.getSubtype().equals(other.getSubtype()); |
| } |
| |
| /** |
| * Returns the subtype of this entry |
| * |
| * @return subtype of this entry |
| */ |
| public String getSubtype() { |
| String subtype = this.getQualifiedNameMap().get(Fields.Subtype); |
| return (subtype != null ? subtype : ""); |
| } |
| |
| /** |
| * Returns the name of this entry |
| * |
| * @return name of this entry |
| */ |
| public String getName() { |
| return (_name != null ? _name : ""); |
| } |
| |
| /** |
| * @return the type |
| */ |
| public String getType() { |
| return (_type != null ? _type : ""); |
| } |
| |
| /** |
| * Returns the key for this entry. The key is the lower case name. |
| * |
| * @return key for this entry |
| */ |
| public String getKey() { |
| return (_key != null ? _key : ""); |
| } |
| |
| /** |
| * @return record type |
| */ |
| public DNSRecordType getRecordType() { |
| return (_recordType != null ? _recordType : DNSRecordType.TYPE_IGNORE); |
| } |
| |
| /** |
| * @return record class |
| */ |
| public DNSRecordClass getRecordClass() { |
| return (_dnsClass != null ? _dnsClass : DNSRecordClass.CLASS_UNKNOWN); |
| } |
| |
| /** |
| * @return true if unique |
| */ |
| public boolean isUnique() { |
| return _unique; |
| } |
| |
| public Map<Fields, String> getQualifiedNameMap() { |
| return Collections.unmodifiableMap(_qualifiedNameMap); |
| } |
| |
| public boolean isServicesDiscoveryMetaQuery() { |
| return _qualifiedNameMap.get(Fields.Application).equals("dns-sd") && _qualifiedNameMap.get(Fields.Instance).equals("_services"); |
| } |
| |
| public boolean isDomainDiscoveryQuery() { |
| // b._dns-sd._udp.<domain>. |
| // db._dns-sd._udp.<domain>. |
| // r._dns-sd._udp.<domain>. |
| // dr._dns-sd._udp.<domain>. |
| // lb._dns-sd._udp.<domain>. |
| |
| if (_qualifiedNameMap.get(Fields.Application).equals("dns-sd")) { |
| String name = _qualifiedNameMap.get(Fields.Instance); |
| return "b".equals(name) || "db".equals(name) || "r".equals(name) || "dr".equals(name) || "lb".equals(name); |
| } |
| return false; |
| } |
| |
| public boolean isReverseLookup() { |
| return this.isV4ReverseLookup() || this.isV6ReverseLookup(); |
| } |
| |
| public boolean isV4ReverseLookup() { |
| return _qualifiedNameMap.get(Fields.Domain).endsWith("in-addr.arpa"); |
| } |
| |
| public boolean isV6ReverseLookup() { |
| return _qualifiedNameMap.get(Fields.Domain).endsWith("ip6.arpa"); |
| } |
| |
| /** |
| * Check if the record is stale, i.e. it has outlived more than half of its TTL. |
| * |
| * @param now |
| * update date |
| * @return <code>true</code> is the record is stale, <code>false</code> otherwise. |
| */ |
| public abstract boolean isStale(long now); |
| |
| /** |
| * Check if the record is expired. |
| * |
| * @param now |
| * update date |
| * @return <code>true</code> is the record is expired, <code>false</code> otherwise. |
| */ |
| public abstract boolean isExpired(long now); |
| |
| /** |
| * Check that 2 entries are of the same class. |
| * |
| * @param entry |
| * @return <code>true</code> is the two class are the same, <code>false</code> otherwise. |
| */ |
| public boolean isSameRecordClass(DNSEntry entry) { |
| return (entry != null) && (entry.getRecordClass() == this.getRecordClass()); |
| } |
| |
| /** |
| * Check that 2 entries are of the same type. |
| * |
| * @param entry |
| * @return <code>true</code> is the two type are the same, <code>false</code> otherwise. |
| */ |
| public boolean isSameType(DNSEntry entry) { |
| return (entry != null) && (entry.getRecordType() == this.getRecordType()); |
| } |
| |
| /** |
| * @param dout |
| * @exception IOException |
| */ |
| protected void toByteArray(DataOutputStream dout) throws IOException { |
| dout.write(this.getName().getBytes("UTF8")); |
| dout.writeShort(this.getRecordType().indexValue()); |
| dout.writeShort(this.getRecordClass().indexValue()); |
| } |
| |
| /** |
| * 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. |
| * |
| * @return byte array representation |
| */ |
| protected byte[] toByteArray() { |
| try { |
| ByteArrayOutputStream bout = new ByteArrayOutputStream(); |
| DataOutputStream dout = new DataOutputStream(bout); |
| this.toByteArray(dout); |
| dout.close(); |
| return bout.toByteArray(); |
| } catch (IOException e) { |
| throw new InternalError(); |
| } |
| } |
| |
| /** |
| * Does a lexicographic comparison of the byte array representation of this record and that record. This is needed for tie-break tests according to draft-cheshire-dnsext-multicastdns-04.txt chapter 9.2. |
| * |
| * @param that |
| * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object. |
| */ |
| public int compareTo(DNSEntry that) { |
| byte[] thisBytes = this.toByteArray(); |
| byte[] thatBytes = that.toByteArray(); |
| for (int i = 0, n = Math.min(thisBytes.length, thatBytes.length); i < n; i++) { |
| if (thisBytes[i] > thatBytes[i]) { |
| return 1; |
| } else if (thisBytes[i] < thatBytes[i]) { |
| return -1; |
| } |
| } |
| return thisBytes.length - thatBytes.length; |
| } |
| |
| /** |
| * Overriden, to return a value which is consistent with the value returned by equals(Object). |
| */ |
| @Override |
| public int hashCode() { |
| return this.getKey().hashCode() + this.getRecordType().indexValue() + this.getRecordClass().indexValue(); |
| } |
| |
| /* |
| * (non-Javadoc) |
| * @see java.lang.Object#toString() |
| */ |
| @Override |
| public String toString() { |
| StringBuilder aLog = new StringBuilder(200); |
| aLog.append("[" + this.getClass().getSimpleName() + "@" + System.identityHashCode(this)); |
| aLog.append(" type: " + this.getRecordType()); |
| aLog.append(", class: " + this.getRecordClass()); |
| aLog.append((_unique ? "-unique," : ",")); |
| aLog.append(" name: " + _name); |
| this.toString(aLog); |
| aLog.append("]"); |
| return aLog.toString(); |
| } |
| |
| /** |
| * @param aLog |
| */ |
| protected void toString(StringBuilder aLog) { |
| // Stub |
| } |
| |
| } |