Home | History | Annotate | Download | only in impl
      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.DataOutputStream;
      9 import java.io.IOException;
     10 import java.util.Collections;
     11 import java.util.Map;
     12 
     13 import javax.jmdns.ServiceInfo.Fields;
     14 import javax.jmdns.impl.constants.DNSRecordClass;
     15 import javax.jmdns.impl.constants.DNSRecordType;
     16 
     17 /**
     18  * DNS entry with a name, type, and class. This is the base class for questions and records.
     19  *
     20  * @author Arthur van Hoff, Pierre Frisch, Rick Blair
     21  */
     22 public abstract class DNSEntry {
     23     // private static Logger logger = Logger.getLogger(DNSEntry.class.getName());
     24     private final String         _key;
     25 
     26     private final String         _name;
     27 
     28     private final String         _type;
     29 
     30     private final DNSRecordType  _recordType;
     31 
     32     private final DNSRecordClass _dnsClass;
     33 
     34     private final boolean        _unique;
     35 
     36     final Map<Fields, String>    _qualifiedNameMap;
     37 
     38     /**
     39      * Create an entry.
     40      */
     41     DNSEntry(String name, DNSRecordType type, DNSRecordClass recordClass, boolean unique) {
     42         _name = name;
     43         // _key = (name != null ? name.trim().toLowerCase() : null);
     44         _recordType = type;
     45         _dnsClass = recordClass;
     46         _unique = unique;
     47         _qualifiedNameMap = ServiceInfoImpl.decodeQualifiedNameMapForType(this.getName());
     48         String domain = _qualifiedNameMap.get(Fields.Domain);
     49         String protocol = _qualifiedNameMap.get(Fields.Protocol);
     50         String application = _qualifiedNameMap.get(Fields.Application);
     51         String instance = _qualifiedNameMap.get(Fields.Instance).toLowerCase();
     52         _type = (application.length() > 0 ? "_" + application + "." : "") + (protocol.length() > 0 ? "_" + protocol + "." : "") + domain + ".";
     53         _key = ((instance.length() > 0 ? instance + "." : "") + _type).toLowerCase();
     54     }
     55 
     56     /*
     57      * (non-Javadoc)
     58      * @see java.lang.Object#equals(java.lang.Object)
     59      */
     60     @Override
     61     public boolean equals(Object obj) {
     62         boolean result = false;
     63         if (obj instanceof DNSEntry) {
     64             DNSEntry other = (DNSEntry) obj;
     65             result = this.getKey().equals(other.getKey()) && this.getRecordType().equals(other.getRecordType()) && this.getRecordClass() == other.getRecordClass();
     66         }
     67         return result;
     68     }
     69 
     70     /**
     71      * Check if two entries have exactly the same name, type, and class.
     72      *
     73      * @param entry
     74      * @return <code>true</code> if the two entries have are for the same record, <code>false</code> otherwise
     75      */
     76     public boolean isSameEntry(DNSEntry entry) {
     77         return this.getKey().equals(entry.getKey()) && this.getRecordType().equals(entry.getRecordType()) && ((DNSRecordClass.CLASS_ANY == entry.getRecordClass()) || this.getRecordClass().equals(entry.getRecordClass()));
     78     }
     79 
     80     /**
     81      * Check if two entries have the same subtype.
     82      *
     83      * @param other
     84      * @return <code>true</code> if the two entries have are for the same subtype, <code>false</code> otherwise
     85      */
     86     public boolean sameSubtype(DNSEntry other) {
     87         return this.getSubtype().equals(other.getSubtype());
     88     }
     89 
     90     /**
     91      * Returns the subtype of this entry
     92      *
     93      * @return subtype of this entry
     94      */
     95     public String getSubtype() {
     96         String subtype = this.getQualifiedNameMap().get(Fields.Subtype);
     97         return (subtype != null ? subtype : "");
     98     }
     99 
    100     /**
    101      * Returns the name of this entry
    102      *
    103      * @return name of this entry
    104      */
    105     public String getName() {
    106         return (_name != null ? _name : "");
    107     }
    108 
    109     /**
    110      * @return the type
    111      */
    112     public String getType() {
    113         return (_type != null ? _type : "");
    114     }
    115 
    116     /**
    117      * Returns the key for this entry. The key is the lower case name.
    118      *
    119      * @return key for this entry
    120      */
    121     public String getKey() {
    122         return (_key != null ? _key : "");
    123     }
    124 
    125     /**
    126      * @return record type
    127      */
    128     public DNSRecordType getRecordType() {
    129         return (_recordType != null ? _recordType : DNSRecordType.TYPE_IGNORE);
    130     }
    131 
    132     /**
    133      * @return record class
    134      */
    135     public DNSRecordClass getRecordClass() {
    136         return (_dnsClass != null ? _dnsClass : DNSRecordClass.CLASS_UNKNOWN);
    137     }
    138 
    139     /**
    140      * @return true if unique
    141      */
    142     public boolean isUnique() {
    143         return _unique;
    144     }
    145 
    146     public Map<Fields, String> getQualifiedNameMap() {
    147         return Collections.unmodifiableMap(_qualifiedNameMap);
    148     }
    149 
    150     public boolean isServicesDiscoveryMetaQuery() {
    151         return _qualifiedNameMap.get(Fields.Application).equals("dns-sd") && _qualifiedNameMap.get(Fields.Instance).equals("_services");
    152     }
    153 
    154     public boolean isDomainDiscoveryQuery() {
    155         // b._dns-sd._udp.<domain>.
    156         // db._dns-sd._udp.<domain>.
    157         // r._dns-sd._udp.<domain>.
    158         // dr._dns-sd._udp.<domain>.
    159         // lb._dns-sd._udp.<domain>.
    160 
    161         if (_qualifiedNameMap.get(Fields.Application).equals("dns-sd")) {
    162             String name = _qualifiedNameMap.get(Fields.Instance);
    163             return "b".equals(name) || "db".equals(name) || "r".equals(name) || "dr".equals(name) || "lb".equals(name);
    164         }
    165         return false;
    166     }
    167 
    168     public boolean isReverseLookup() {
    169         return this.isV4ReverseLookup() || this.isV6ReverseLookup();
    170     }
    171 
    172     public boolean isV4ReverseLookup() {
    173         return _qualifiedNameMap.get(Fields.Domain).endsWith("in-addr.arpa");
    174     }
    175 
    176     public boolean isV6ReverseLookup() {
    177         return _qualifiedNameMap.get(Fields.Domain).endsWith("ip6.arpa");
    178     }
    179 
    180     /**
    181      * Check if the record is stale, i.e. it has outlived more than half of its TTL.
    182      *
    183      * @param now
    184      *            update date
    185      * @return <code>true</code> is the record is stale, <code>false</code> otherwise.
    186      */
    187     public abstract boolean isStale(long now);
    188 
    189     /**
    190      * Check if the record is expired.
    191      *
    192      * @param now
    193      *            update date
    194      * @return <code>true</code> is the record is expired, <code>false</code> otherwise.
    195      */
    196     public abstract boolean isExpired(long now);
    197 
    198     /**
    199      * Check that 2 entries are of the same class.
    200      *
    201      * @param entry
    202      * @return <code>true</code> is the two class are the same, <code>false</code> otherwise.
    203      */
    204     public boolean isSameRecordClass(DNSEntry entry) {
    205         return (entry != null) && (entry.getRecordClass() == this.getRecordClass());
    206     }
    207 
    208     /**
    209      * Check that 2 entries are of the same type.
    210      *
    211      * @param entry
    212      * @return <code>true</code> is the two type are the same, <code>false</code> otherwise.
    213      */
    214     public boolean isSameType(DNSEntry entry) {
    215         return (entry != null) && (entry.getRecordType() == this.getRecordType());
    216     }
    217 
    218     /**
    219      * @param dout
    220      * @exception IOException
    221      */
    222     protected void toByteArray(DataOutputStream dout) throws IOException {
    223         dout.write(this.getName().getBytes("UTF8"));
    224         dout.writeShort(this.getRecordType().indexValue());
    225         dout.writeShort(this.getRecordClass().indexValue());
    226     }
    227 
    228     /**
    229      * 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.
    230      *
    231      * @return byte array representation
    232      */
    233     protected byte[] toByteArray() {
    234         try {
    235             ByteArrayOutputStream bout = new ByteArrayOutputStream();
    236             DataOutputStream dout = new DataOutputStream(bout);
    237             this.toByteArray(dout);
    238             dout.close();
    239             return bout.toByteArray();
    240         } catch (IOException e) {
    241             throw new InternalError();
    242         }
    243     }
    244 
    245     /**
    246      * 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.
    247      *
    248      * @param that
    249      * @return a negative integer, zero, or a positive integer as this object is less than, equal to, or greater than the specified object.
    250      */
    251     public int compareTo(DNSEntry that) {
    252         byte[] thisBytes = this.toByteArray();
    253         byte[] thatBytes = that.toByteArray();
    254         for (int i = 0, n = Math.min(thisBytes.length, thatBytes.length); i < n; i++) {
    255             if (thisBytes[i] > thatBytes[i]) {
    256                 return 1;
    257             } else if (thisBytes[i] < thatBytes[i]) {
    258                 return -1;
    259             }
    260         }
    261         return thisBytes.length - thatBytes.length;
    262     }
    263 
    264     /**
    265      * Overriden, to return a value which is consistent with the value returned by equals(Object).
    266      */
    267     @Override
    268     public int hashCode() {
    269         return this.getKey().hashCode() + this.getRecordType().indexValue() + this.getRecordClass().indexValue();
    270     }
    271 
    272     /*
    273      * (non-Javadoc)
    274      * @see java.lang.Object#toString()
    275      */
    276     @Override
    277     public String toString() {
    278         StringBuilder aLog = new StringBuilder(200);
    279         aLog.append("[" + this.getClass().getSimpleName() + "@" + System.identityHashCode(this));
    280         aLog.append(" type: " + this.getRecordType());
    281         aLog.append(", class: " + this.getRecordClass());
    282         aLog.append((_unique ? "-unique," : ","));
    283         aLog.append(" name: " + _name);
    284         this.toString(aLog);
    285         aLog.append("]");
    286         return aLog.toString();
    287     }
    288 
    289     /**
    290      * @param aLog
    291      */
    292     protected void toString(StringBuilder aLog) {
    293         // Stub
    294     }
    295 
    296 }
    297