diff options
Diffstat (limited to 'src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns')
15 files changed, 0 insertions, 6832 deletions
diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java deleted file mode 100644 index 0baaff4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSCache.java +++ /dev/null @@ -1,294 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; - -import net.java.sip.communicator.util.*; - -/** - * A table of DNS entries. This is a hash table which - * can handle multiple entries with the same name. - * <p/> - * Storing multiple entries with the same name is implemented using a - * linked list of <code>CacheNode</code>'s. - * <p/> - * The current implementation of the API of DNSCache does expose the - * cache nodes to clients. Clients must explicitly deal with the nodes - * when iterating over entries in the cache. Here's how to iterate over - * all entries in the cache: - * <pre> - * for (Iterator i=dnscache.iterator(); i.hasNext(); ) - * { - * for ( DNSCache.CacheNode n = (DNSCache.CacheNode) i.next(); - * n != null; - * n.next()) - * { - * DNSEntry entry = n.getValue(); - * ...do something with entry... - * } - * } - * </pre> - * <p/> - * And here's how to iterate over all entries having a given name: - * <pre> - * for ( DNSCache.CacheNode n = (DNSCache.CacheNode) dnscache.find(name); - * n != null; - * n.next()) - * { - * DNSEntry entry = n.getValue(); - * ...do something with entry... - * } - * </pre> - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer, Rick Blair - */ -class DNSCache -{ - private static Logger logger = Logger.getLogger(DNSCache.class.toString()); - // Implementation note: - // We might completely hide the existence of CacheNode's in a future version - // of DNSCache. But this will require to implement two (inner) classes for - // the iterators that will be returned by method <code>iterator()</code> and - // method <code>find(name)</code>. - // Since DNSCache is not a public class, it does not seem worth the effort - // to clean its API up that much. - - // [PJYF Oct 15 2004] This should implements Collections - // that would be amuch cleaner implementation - - /** - * The number of DNSEntry's in the cache. - */ - private int size; - - /** - * The hashtable used internally to store the entries of the cache. - * Keys are instances of String. The String contains an unqualified service - * name. - * Values are linked lists of CacheNode instances. - */ - private HashMap<String, CacheNode> hashtable; - - /** - * Cache nodes are used to implement storage of multiple DNSEntry's of the - * same name in the cache. - */ - public static class CacheNode - { - private DNSEntry value; - private CacheNode next; - - public CacheNode(DNSEntry value) - { - this.value = value; -// String SLevel = System.getProperty("jmdns.debug"); -// if (SLevel == null) -// SLevel = "INFO"; -// logger.setLevel(Level.parse(SLevel)); - } - - public CacheNode next() - { - return next; - } - - public DNSEntry getValue() - { - return value; - } - } - - - /** - * Create a table with a given initial size. - * @param size initial size. - */ - public DNSCache(final int size) - { - hashtable = new HashMap<String, CacheNode>(size); - -// String SLevel = System.getProperty("jmdns.debug"); -// if (SLevel == null) SLevel = "INFO"; -// logger.setLevel(Level.parse(SLevel)); - } - - /** - * Clears the cache. - */ - public synchronized void clear() - { - hashtable.clear(); - size = 0; - } - - /** - * Adds an entry to the table. - * @param entry added to the table. - */ - public synchronized void add(final DNSEntry entry) - { - //logger.log("DNSCache.add("+entry.getName()+")"); - CacheNode newValue = new CacheNode(entry); - CacheNode node = hashtable.get(entry.getName()); - if (node == null) - { - hashtable.put(entry.getName(), newValue); - } - else - { - newValue.next = node.next; - node.next = newValue; - } - size++; - } - - /** - * Remove a specific entry from the table. - * @param entry removed from table. - * @return Returns true if the entry was found. - */ - public synchronized boolean remove(DNSEntry entry) - { - CacheNode node = hashtable.get(entry.getName()); - if (node != null) - { - if (node.value == entry) - { - if (node.next == null) - { - hashtable.remove(entry.getName()); - } - else - { - hashtable.put(entry.getName(), node.next); - } - size--; - return true; - } - - CacheNode previous = node; - node = node.next; - while (node != null) - { - if (node.value == entry) - { - previous.next = node.next; - size--; - return true; - } - previous = node; - node = node.next; - } - ; - } - return false; - } - - /** - * Get a matching DNS entry from the table (using equals). - * @param entry to be found in table. - * @return Returns the entry that was found. - */ - public synchronized DNSEntry get(DNSEntry entry) - { - for (CacheNode node = find(entry.getName()); node != null; node = node.next) - { - if (node.value.equals(entry)) - { - return node.value; - } - } - return null; - } - - /** - * Get a matching DNS entry from the table. - * @param name - * @param type - * @param clazz - * @return Return the entry if found, null otherwise. - */ - public synchronized DNSEntry get(String name, int type, int clazz) - { - for (CacheNode node = find(name); node != null; node = node.next) - { - if (node.value.type == type && node.value.clazz == clazz) - { - return node.value; - } - } - return null; - } - - /** - * Iterates over all cache nodes. - * The iterator returns instances of DNSCache.CacheNode. - * Each instance returned is the first node of a linked list. - * To retrieve all entries, one must iterate over this linked list. See - * code snippets in the header of the class. - * @return Returns iterator with instances of DNSCache.CacheNode. - */ - public Iterator<DNSCache.CacheNode> iterator() - { - return Collections.unmodifiableCollection(hashtable.values()).iterator(); - } - - /** - * Iterate only over items with matching name. - * If an instance is returned, it is the first node of a linked list. - * To retrieve all entries, one must iterate over this linked list. - * @param name to be found. - * @return Returns an instance of DNSCache.CacheNode or null. - */ - public synchronized CacheNode find(String name) - { - return hashtable.get(name); - } - - /** - * List all entries for debugging. - */ - public synchronized void print() - { - for (Iterator<CacheNode> i = iterator(); i.hasNext();) - { - for (CacheNode n = i.next(); n != null; n = n.next) - { - if (logger.isInfoEnabled()) - logger.info(n.value.toString()); - } - } - } - - @Override - public synchronized String toString() - { - StringBuffer aLog = new StringBuffer(); - aLog.append("\t---- cache ----"); - for (Iterator<CacheNode> i = iterator(); i.hasNext();) - { - for (CacheNode n = i.next(); n != null; n = n.next) - { - aLog.append("\n\t\t" + n.value); - } - } - return aLog.toString(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java deleted file mode 100644 index 8792c8d..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSConstants.java +++ /dev/null @@ -1,160 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff, Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -/** - * DNS constants. - * - * @version %I%, %G% - * @author Arthur van Hoff, Jeff Sonstein, - * Werner Randelshofer, Pierre Frisch, Rick Blair - */ -public final class DNSConstants -{ - - // changed to final class - jeffs - final static String MDNS_GROUP = "224.0.0.251"; - final static String MDNS_GROUP_IPV6 = "FF02::FB"; - final static int MDNS_PORT = 5353; - final static int DNS_PORT = 53; - // default one hour TTL - final static int DNS_TTL = 60 * 60; - // two hour TTL (draft-cheshire-dnsext-multicastdns.txt ch 13) - // final static int DNS_TTL = 120 * 60; - - final static int MAX_MSG_TYPICAL = 1460; - final static int MAX_MSG_ABSOLUTE = 8972; - - final static int FLAGS_QR_MASK = 0x8000; // Query response mask - final static int FLAGS_QR_QUERY = 0x0000; // Query - final static int FLAGS_QR_RESPONSE = 0x8000;// Response - - public final static int FLAGS_AA = 0x0400; // Authorative answer - final static int FLAGS_TC = 0x0200; // Truncated - final static int FLAGS_RD = 0x0100; // Recursion desired - public final static int FLAGS_RA = 0x8000; // Recursion available - - final static int FLAGS_Z = 0x0040; // Zero - final static int FLAGS_AD = 0x0020; // Authentic data - final static int FLAGS_CD = 0x0010; // Checking disabled - - // Final Static Internet - public final static int CLASS_IN = 1; - // CSNET - final static int CLASS_CS = 2; - // CHAOS - final static int CLASS_CH = 3; - // Hesiod - final static int CLASS_HS = 4; - // Used in DNS UPDATE [RFC 2136] - final static int CLASS_NONE = 254; - // Not a DNS class, but a DNS query class, meaning "all classes" - final static int CLASS_ANY = 255; - // Multicast DNS uses the bottom 15 bits to identify the record class... - final static int CLASS_MASK = 0x7FFF; - // ... and the top bit indicates that all other cached records are now invalid - public final static int CLASS_UNIQUE = 0x8000; - - final static int TYPE_IGNORE = 0; // This is a hack to stop further processing - public final static int TYPE_A = 1; // Address - final static int TYPE_NS = 2; // Name Server - final static int TYPE_MD = 3; // Mail Destination - final static int TYPE_MF = 4; // Mail Forwarder - final static int TYPE_CNAME = 5; // Canonical Name - final static int TYPE_SOA = 6; // Start of Authority - final static int TYPE_MB = 7; // Mailbox - final static int TYPE_MG = 8; // Mail Group - final static int TYPE_MR = 9; // Mail Rename - final static int TYPE_NULL = 10; // NULL RR - final static int TYPE_WKS = 11; // Well-known-service - final static int TYPE_PTR = 12; // Domain Name pofinal static inter - final static int TYPE_HINFO = 13; // Host information - final static int TYPE_MINFO = 14; // Mailbox information - final static int TYPE_MX = 15; // Mail exchanger - public final static int TYPE_TXT = 16;// Arbitrary text string - final static int TYPE_RP = 17; // for Responsible Person [RFC1183] - final static int TYPE_AFSDB = 18; // for AFS Data Base location [RFC1183] - final static int TYPE_X25 = 19; // for X.25 PSDN address [RFC1183] - final static int TYPE_ISDN = 20; // for ISDN address [RFC1183] - final static int TYPE_RT = 21; // for Route Through [RFC1183] - final static int TYPE_NSAP = 22; // for NSAP address, NSAP style A record [RFC1706] - final static int TYPE_NSAP_PTR = 23;// - final static int TYPE_SIG = 24; // for security signature [RFC2931] - final static int TYPE_KEY = 25; // for security key [RFC2535] - final static int TYPE_PX = 26; // X.400 mail mapping information [RFC2163] - final static int TYPE_GPOS = 27; // Geographical Position [RFC1712] - final static int TYPE_AAAA = 28; // IP6 Address [Thomson] - final static int TYPE_LOC = 29; // Location Information [Vixie] - final static int TYPE_NXT = 30; // Next Domain - OBSOLETE [RFC2535, RFC3755] - final static int TYPE_EID = 31; // Endpoint Identifier [Patton] - final static int TYPE_NIMLOC = 32; // Nimrod Locator [Patton] - public final static int TYPE_SRV = 33;// Server Selection [RFC2782] - final static int TYPE_ATMA = 34; // ATM Address [Dobrowski] - final static int TYPE_NAPTR = 35; // Naming Authority Pointer [RFC2168, RFC2915] - final static int TYPE_KX = 36; // Key Exchanger [RFC2230] - final static int TYPE_CERT = 37; // CERT [RFC2538] - final static int TYPE_A6 = 38; // A6 [RFC2874] - final static int TYPE_DNAME = 39; // DNAME [RFC2672] - final static int TYPE_SINK = 40; // SINK [Eastlake] - final static int TYPE_OPT = 41; // OPT [RFC2671] - final static int TYPE_APL = 42; // APL [RFC3123] - final static int TYPE_DS = 43; // Delegation Signer [RFC3658] - final static int TYPE_SSHFP = 44; // SSH Key Fingerprint [RFC-ietf-secsh-dns-05.txt] - final static int TYPE_RRSIG = 46; // RRSIG [RFC3755] - final static int TYPE_NSEC = 47; // NSEC [RFC3755] - final static int TYPE_DNSKEY = 48; // DNSKEY [RFC3755] - final static int TYPE_UINFO = 100; // [IANA-Reserved] - final static int TYPE_UID = 101; // [IANA-Reserved] - final static int TYPE_GID = 102; // [IANA-Reserved] - final static int TYPE_UNSPEC = 103; // [IANA-Reserved] - final static int TYPE_TKEY = 249; // Transaction Key [RFC2930] - final static int TYPE_TSIG = 250; // Transaction Signature [RFC2845] - final static int TYPE_IXFR = 251; // Incremental transfer [RFC1995] - final static int TYPE_AXFR = 252; // Transfer of an entire zone [RFC1035] - final static int TYPE_MAILA = 253; // Mailbox-related records (MB, MG or MR) [RFC1035] - final static int TYPE_MAILB = 254; // Mail agent RRs (Obsolete - see MX) [RFC1035] - final static int TYPE_ANY = 255; // Request for all records [RFC1035] - - //Time Intervals for various functions - - //milliseconds before send shared query - final static int SHARED_QUERY_TIME = 20; - //milliseconds between query loops. - final static int QUERY_WAIT_INTERVAL = 225; - //milliseconds between probe loops. - final static int PROBE_WAIT_INTERVAL = 250; - //minimal wait interval for response. - final static int RESPONSE_MIN_WAIT_INTERVAL = 20; - //maximal wait interval for response - final static int RESPONSE_MAX_WAIT_INTERVAL = 115; - //milliseconds to wait after conflict. - final static int PROBE_CONFLICT_INTERVAL = 1000; - //After x tries go 1 time a sec. on probes. - final static int PROBE_THROTTLE_COUNT = 10; - //We only increment the throttle count, if - // the previous increment is inside this interval. - final static int PROBE_THROTTLE_COUNT_INTERVAL = 5000; - //milliseconds between Announce loops. - final static int ANNOUNCE_WAIT_INTERVAL = 1000; - //milliseconds between cache cleanups. - final static int RECORD_REAPER_INTERVAL = 10000; - - final static int KNOWN_ANSWER_TTL = 120; - // 50% of the TTL in milliseconds - final static int ANNOUNCED_RENEWAL_TTL_INTERVAL = DNS_TTL * 500; -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java deleted file mode 100644 index 7132756..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSEntry.java +++ /dev/null @@ -1,183 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.logging.*; - -/** - * DNS entry with a name, type, and class. This is the base - * class for questions and records. - * - * @version %I%, %G% - * @author Arthur van Hoff, Pierre Frisch, Rick Blair - * @author Christian Vincenot - */ -public class DNSEntry -{ - private static Logger logger = Logger.getLogger(DNSEntry.class.toString()); - String key; - String name; - int type; - int clazz; - boolean unique; - - /** - * Create an entry. - */ - DNSEntry(String name, int type, int clazz) - { - this.key = name.toLowerCase(); - this.name = name; - this.type = type; - this.clazz = clazz & DNSConstants.CLASS_MASK; - this.unique = (clazz & DNSConstants.CLASS_UNIQUE) != 0; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Check if two entries have exactly the same name, type, and class. - */ - @Override - public boolean equals(Object obj) - { - if (obj instanceof DNSEntry) - { - DNSEntry other = (DNSEntry) obj; - return name.equals(other.name) && - type == other.type && - clazz == other.clazz; - } - return false; - } - - public String getName() - { - return name; - } - - public int getType() - { - return type; - } - - public int getClazz() - { - return clazz; - } - - - public boolean isUnique() - { - return unique; - } - - /** - * Overriden, to return a value which is consistent with the value returned - * by equals(Object). - */ - @Override - public int hashCode() - { - return name.hashCode() + type + clazz; - } - - /** - * Get a string given a clazz. - */ - static String getClazz(int clazz) - { - switch (clazz & DNSConstants.CLASS_MASK) - { - case DNSConstants.CLASS_IN: - return "in"; - case DNSConstants.CLASS_CS: - return "cs"; - case DNSConstants.CLASS_CH: - return "ch"; - case DNSConstants.CLASS_HS: - return "hs"; - case DNSConstants.CLASS_NONE: - return "none"; - case DNSConstants.CLASS_ANY: - return "any"; - default: - return "?"; - } - } - - /** - * Get a string given a type. - */ - static String getType(int type) - { - switch (type) - { - case DNSConstants.TYPE_A: - return "a"; - case DNSConstants.TYPE_AAAA: - return "aaaa"; - case DNSConstants.TYPE_NS: - return "ns"; - case DNSConstants.TYPE_MD: - return "md"; - case DNSConstants.TYPE_MF: - return "mf"; - case DNSConstants.TYPE_CNAME: - return "cname"; - case DNSConstants.TYPE_SOA: - return "soa"; - case DNSConstants.TYPE_MB: - return "mb"; - case DNSConstants.TYPE_MG: - return "mg"; - case DNSConstants.TYPE_MR: - return "mr"; - case DNSConstants.TYPE_NULL: - return "null"; - case DNSConstants.TYPE_WKS: - return "wks"; - case DNSConstants.TYPE_PTR: - return "ptr"; - case DNSConstants.TYPE_HINFO: - return "hinfo"; - case DNSConstants.TYPE_MINFO: - return "minfo"; - case DNSConstants.TYPE_MX: - return "mx"; - case DNSConstants.TYPE_TXT: - return "txt"; - case DNSConstants.TYPE_SRV: - return "srv"; - case DNSConstants.TYPE_ANY: - return "any"; - default: - return "?"; - } - } - - public String toString(String hdr, String other) - { - return hdr + "[" + getType(type) + "," + - getClazz(clazz) + (unique ? "-unique," : ",") + - name + ((other != null) ? "," + - other + "]" : "]"); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java deleted file mode 100644 index 3dcedc4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSIncoming.java +++ /dev/null @@ -1,524 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.*; - -/** - * Parse an incoming DNS message into its components. - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch, Daniel Bobbert - */ -final class DNSIncoming -{ - private static Logger logger = Logger.getLogger(DNSIncoming.class.toString()); - // Implementation note: This vector should be immutable. - // If a client of DNSIncoming changes the contents of this vector, - // we get undesired results. To fix this, we have to migrate to - // the Collections API of Java 1.2. i.e we replace Vector by List. - // final static Vector EMPTY = new Vector(); - - private DatagramPacket packet; - private int off; - private int len; - private byte data[]; - - int id; - private int flags; - private int numQuestions; - int numAnswers; - private int numAuthorities; - private int numAdditionals; - private long receivedTime; - - List<DNSEntry> questions; - List<DNSRecord> answers; - - /** - * Parse a message from a datagram packet. - */ - DNSIncoming(DatagramPacket packet) throws IOException - { - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - - this.packet = packet; - this.data = packet.getData(); - this.len = packet.getLength(); - this.off = packet.getOffset(); - this.questions = new LinkedList<DNSEntry>(); - this.answers = new LinkedList<DNSRecord>(); - this.receivedTime = System.currentTimeMillis(); - - try - { - id = readUnsignedShort(); - flags = readUnsignedShort(); - numQuestions = readUnsignedShort(); - numAnswers = readUnsignedShort(); - numAuthorities = readUnsignedShort(); - numAdditionals = readUnsignedShort(); - - // parse questions - if (numQuestions > 0) - { - questions = - Collections.synchronizedList( - new ArrayList<DNSEntry>(numQuestions)); - for (int i = 0; i < numQuestions; i++) - { - DNSQuestion question = - new DNSQuestion( - readName(), - readUnsignedShort(), - readUnsignedShort()); - - questions.add(question); - } - } - - // parse answers - int n = numAnswers + numAuthorities + numAdditionals; - if (n > 0) - { - //System.out.println("JMDNS received "+n+" answers!"); - answers = Collections.synchronizedList( - new ArrayList<DNSRecord>(n)); - for (int i = 0; i < n; i++) - { - String domain = readName(); - int type = readUnsignedShort(); - int clazz = readUnsignedShort(); - int ttl = readInt(); - int len = readUnsignedShort(); - int end = off + len; - DNSRecord rec = null; - - switch (type) - { - case DNSConstants.TYPE_A: // IPv4 - case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested - rec = new DNSRecord.Address( - domain, type, clazz, ttl, readBytes(off, len)); - break; - case DNSConstants.TYPE_CNAME: - case DNSConstants.TYPE_PTR: - rec = new DNSRecord.Pointer( - domain, type, clazz, ttl, readName()); - break; - case DNSConstants.TYPE_TXT: - rec = new DNSRecord.Text( - domain, type, clazz, ttl, readBytes(off, len)); - break; - case DNSConstants.TYPE_SRV: - //System.out.println("JMDNS: One is a SRV field!!"); - rec = new DNSRecord.Service( domain, - type, - clazz, - ttl, - readUnsignedShort(), - readUnsignedShort(), - readUnsignedShort(), - readName()); - break; - case DNSConstants.TYPE_HINFO: - // Maybe we should do something with those - break; - default : - logger.finer("DNSIncoming() unknown type:" + type); - break; - } - - if (rec != null) - { - // Add a record, if we were able to create one. - answers.add(rec); - } - else - { - // Addjust the numbers for the skipped record - if (answers.size() < numAnswers) - { - numAnswers--; - } - else - { - if (answers.size() < numAnswers + numAuthorities) - { - numAuthorities--; - } - else - { - if (answers.size() < numAnswers + - numAuthorities + - numAdditionals) - { - numAdditionals--; - } - } - } - } - off = end; - } - } - } - catch (IOException e) - { - logger.log(Level.WARNING, - "DNSIncoming() dump " + print(true) + "\n exception ", e); - throw e; - } - } - - /** - * Check if the message is a query. - */ - boolean isQuery() - { - return (flags & DNSConstants.FLAGS_QR_MASK) == - DNSConstants.FLAGS_QR_QUERY; - } - - /** - * Check if the message is truncated. - */ - boolean isTruncated() - { - return (flags & DNSConstants.FLAGS_TC) != 0; - } - - /** - * Check if the message is a response. - */ - boolean isResponse() - { - return (flags & DNSConstants.FLAGS_QR_MASK) == - DNSConstants.FLAGS_QR_RESPONSE; - } - - private int get(int off) throws IOException - { - if ((off < 0) || (off >= len)) - { - throw new IOException("parser error: offset=" + off); - } - return data[off] & 0xFF; - } - - private int readUnsignedShort() throws IOException - { - return (get(off++) << 8) + get(off++); - } - - private int readInt() throws IOException - { - return (readUnsignedShort() << 16) + readUnsignedShort(); - } - - private byte[] readBytes(int off, int len) throws IOException - { - byte bytes[] = new byte[len]; - System.arraycopy(data, off, bytes, 0, len); - return bytes; - } - - private void readUTF(StringBuffer buf, int off, int len) throws IOException - { - for (int end = off + len; off < end;) - { - int ch = get(off++); - switch (ch >> 4) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - break; - case 12: - case 13: - // 110x xxxx 10xx xxxx - ch = ((ch & 0x1F) << 6) | (get(off++) & 0x3F); - break; - case 14: - // 1110 xxxx 10xx xxxx 10xx xxxx - ch = ((ch & 0x0f) << 12) | - ((get(off++) & 0x3F) << 6) | - (get(off++) & 0x3F); - break; - default: - // 10xx xxxx, 1111 xxxx - ch = ((ch & 0x3F) << 4) | (get(off++) & 0x0f); - break; - } - buf.append((char) ch); - } - } - - private String readName() throws IOException - { - StringBuffer buf = new StringBuffer(); - int off = this.off; - int next = -1; - int first = off; - - while (true) - { - int len = get(off++); - if (len == 0) - { - break; - } - switch (len & 0xC0) - { - case 0x00: - //buf.append("[" + off + "]"); - readUTF(buf, off, len); - off += len; - buf.append('.'); - break; - case 0xC0: - //buf.append("<" + (off - 1) + ">"); - if (next < 0) - { - next = off + 1; - } - off = ((len & 0x3F) << 8) | get(off++); - if (off >= first) - { - throw new IOException( - "bad domain name: possible circular name detected"); - } - first = off; - break; - default: - throw new IOException( - "bad domain name: '" + buf + "' at " + off); - } - } - this.off = (next >= 0) ? next : off; - return buf.toString(); - } - - /** - * Debugging. - */ - String print(boolean dump) - { - StringBuffer buf = new StringBuffer(); - buf.append(toString() + "\n"); - for (Iterator<DNSEntry> iterator = questions.iterator(); - iterator.hasNext();) - { - buf.append(" ques:" + iterator.next() + "\n"); - } - int count = 0; - for (Iterator<DNSRecord> iterator = answers.iterator(); - iterator.hasNext(); - count++) - { - if (count < numAnswers) - { - buf.append(" answ:"); - } - else - { - if (count < numAnswers + numAuthorities) - { - buf.append(" auth:"); - } - else - { - buf.append(" addi:"); - } - } - buf.append(iterator.next() + "\n"); - } - if (dump) - { - for (int off = 0, len = packet.getLength(); off < len; off += 32) - { - int n = Math.min(32, len - off); - if (off < 10) - { - buf.append(' '); - } - if (off < 100) - { - buf.append(' '); - } - buf.append(off); - buf.append(':'); - for (int i = 0; i < n; i++) - { - if ((i % 8) == 0) - { - buf.append(' '); - } - buf.append(Integer.toHexString((data[off + i] & 0xF0) >> 4)); - buf.append(Integer.toHexString((data[off + i] & 0x0F) >> 0)); - } - buf.append("\n"); - buf.append(" "); - for (int i = 0; i < n; i++) - { - if ((i % 8) == 0) - { - buf.append(' '); - } - buf.append(' '); - int ch = data[off + i] & 0xFF; - buf.append(((ch > ' ') && (ch < 127)) ? (char) ch : '.'); - } - buf.append("\n"); - - // limit message size - if (off + 32 >= 256) - { - buf.append("....\n"); - break; - } - } - } - return buf.toString(); - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append(isQuery() ? "dns[query," : "dns[response,"); - if (packet.getAddress() != null) - { - buf.append(packet.getAddress().getHostAddress()); - } - buf.append(':'); - buf.append(packet.getPort()); - buf.append(",len="); - buf.append(packet.getLength()); - buf.append(",id=0x"); - buf.append(Integer.toHexString(id)); - if (flags != 0) - { - buf.append(",flags=0x"); - buf.append(Integer.toHexString(flags)); - if ((flags & DNSConstants.FLAGS_QR_RESPONSE) != 0) - { - buf.append(":r"); - } - if ((flags & DNSConstants.FLAGS_AA) != 0) - { - buf.append(":aa"); - } - if ((flags & DNSConstants.FLAGS_TC) != 0) - { - buf.append(":tc"); - } - } - if (numQuestions > 0) - { - buf.append(",questions="); - buf.append(numQuestions); - } - if (numAnswers > 0) - { - buf.append(",answers="); - buf.append(numAnswers); - } - if (numAuthorities > 0) - { - buf.append(",authorities="); - buf.append(numAuthorities); - } - if (numAdditionals > 0) - { - buf.append(",additionals="); - buf.append(numAdditionals); - } - buf.append("]"); - return buf.toString(); - } - - /** - * Appends answers to this Incoming. - * - * @throws IllegalArgumentException If not a query or if Truncated. - */ - void append(DNSIncoming that) - { - if (this.isQuery() && this.isTruncated() && that.isQuery()) - { - if (that.numQuestions > 0) { - if (Collections.EMPTY_LIST.equals(this.questions)) - this.questions = - Collections.synchronizedList( - new ArrayList<DNSEntry>(that.numQuestions)); - - this.questions.addAll(that.questions); - this.numQuestions += that.numQuestions; - } - - if (Collections.EMPTY_LIST.equals(answers)) - { - answers = Collections.synchronizedList( - new ArrayList<DNSRecord>()); - } - - if (that.numAnswers > 0) - { - this.answers.addAll(this.numAnswers, - that.answers.subList(0, that.numAnswers)); - this.numAnswers += that.numAnswers; - } - if (that.numAuthorities > 0) - { - this.answers.addAll(this.numAnswers + this.numAuthorities, - that.answers.subList( - that.numAnswers, - that.numAnswers + that.numAuthorities)); - this.numAuthorities += that.numAuthorities; - } - if (that.numAdditionals > 0) - { - this.answers.addAll( - that.answers.subList( - that.numAnswers + that.numAuthorities, - that.numAnswers + that.numAuthorities + that.numAdditionals)); - this.numAdditionals += that.numAdditionals; - } - } - else - { - throw new IllegalArgumentException(); - } - } - - int elapseSinceArrival() - { - return (int) (System.currentTimeMillis() - receivedTime); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java deleted file mode 100644 index d317953..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSListener.java +++ /dev/null @@ -1,39 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -// REMIND: Listener should follow Java idiom for listener or have a different -// name. - -/** - * DNSListener. - * Listener for record updates. - * - * @author Werner Randelshofer, Rick Blair - * @version 1.0 May 22, 2004 Created. - */ -public interface DNSListener -{ - /** - * Update a DNS record. - * @param jmdns - * @param now - * @param record - */ - public void updateRecord(JmDNS jmdns, long now, DNSRecord record); -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java deleted file mode 100644 index 4d099b8..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSOutgoing.java +++ /dev/null @@ -1,405 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.util.*; -import java.util.logging.*; - -/** - * An outgoing DNS message. - * - * @version %I%, %G% - * @author Arthur van Hoff, Rick Blair, Werner Randelshofer - */ -final class DNSOutgoing -{ - private static Logger logger = - Logger.getLogger(DNSOutgoing.class.toString()); - - int id; - int flags; - private boolean multicast; - private int numQuestions; - private int numAnswers; - private int numAuthorities; - private int numAdditionals; - private Hashtable<String, Integer> names; - - byte data[]; - int off; - int len; - - /** - * Create an outgoing multicast query or response. - */ - DNSOutgoing(int flags) - { - this(flags, true); - - } - - /** - * Create an outgoing query or response. - */ - DNSOutgoing(int flags, boolean multicast) - { - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - - this.flags = flags; - this.multicast = multicast; - names = new Hashtable<String, Integer>(); - data = new byte[DNSConstants.MAX_MSG_TYPICAL]; - off = 12; - } - - /** - * Add a question to the message. - */ - void addQuestion(DNSQuestion rec) throws IOException - { - if (numAnswers > 0 || numAuthorities > 0 || numAdditionals > 0) - { - throw new IllegalStateException("Questions must be added before answers"); - } - numQuestions++; - writeQuestion(rec); - } - - /** - * Add an answer if it is not suppressed. - */ - void addAnswer(DNSIncoming in, DNSRecord rec) throws IOException - { - if (numAuthorities > 0 || numAdditionals > 0) - { - throw new IllegalStateException( - "Answers must be added before authorities and additionals"); - } - if (!rec.suppressedBy(in)) - { - addAnswer(rec, 0); - } - } - - /** - * Add an additional answer to the record. Omit if there is no room. - */ - void addAdditionalAnswer(DNSIncoming in, DNSRecord rec) throws IOException - { - if ((off < DNSConstants.MAX_MSG_TYPICAL - 200) && !rec.suppressedBy(in)) - { - writeRecord(rec, 0); - numAdditionals++; - } - } - - /** - * Add an answer to the message. - */ - void addAnswer(DNSRecord rec, long now) throws IOException - { - if (numAuthorities > 0 || numAdditionals > 0) - { - throw new IllegalStateException( - "Questions must be added before answers"); - } - if (rec != null) - { - if ((now == 0) || !rec.isExpired(now)) - { - writeRecord(rec, now); - numAnswers++; - } - } - } - - private LinkedList<DNSRecord> authorativeAnswers = new LinkedList<DNSRecord>(); - - /** - * Add an authorative answer to the message. - */ - void addAuthorativeAnswer(DNSRecord rec) throws IOException - { - if (numAdditionals > 0) - { - throw new IllegalStateException( - "Authorative answers must be added before additional answers"); - } - authorativeAnswers.add(rec); - writeRecord(rec, 0); - numAuthorities++; - - // VERIFY: - - } - - void writeByte(int value) throws IOException - { - if (off >= data.length) - { - throw new IOException("buffer full"); - } - data[off++] = (byte) value; - } - - void writeBytes(String str, int off, int len) throws IOException - { - for (int i = 0; i < len; i++) - { - writeByte(str.charAt(off + i)); - } - } - - void writeBytes(byte data[]) throws IOException - { - if (data != null) - { - writeBytes(data, 0, data.length); - } - } - - void writeBytes(byte data[], int off, int len) throws IOException - { - for (int i = 0; i < len; i++) - { - writeByte(data[off + i]); - } - } - - void writeShort(int value) throws IOException - { - writeByte(value >> 8); - writeByte(value); - } - - void writeInt(int value) throws IOException - { - writeShort(value >> 16); - writeShort(value); - } - - void writeUTF(String str, int off, int len) throws IOException - { - // compute utf length - int utflen = 0; - for (int i = 0; i < len; i++) - { - int ch = str.charAt(off + i); - if ((ch >= 0x0001) && (ch <= 0x007F)) - { - utflen += 1; - } - else - { - if (ch > 0x07FF) - { - utflen += 3; - } - else - { - utflen += 2; - } - } - } - // write utf length - writeByte(utflen); - // write utf data - for (int i = 0; i < len; i++) - { - int ch = str.charAt(off + i); - if ((ch >= 0x0001) && (ch <= 0x007F)) - { - writeByte(ch); - } - else - { - if (ch > 0x07FF) - { - writeByte(0xE0 | ((ch >> 12) & 0x0F)); - writeByte(0x80 | ((ch >> 6) & 0x3F)); - writeByte(0x80 | ((ch >> 0) & 0x3F)); - } - else - { - writeByte(0xC0 | ((ch >> 6) & 0x1F)); - writeByte(0x80 | ((ch >> 0) & 0x3F)); - } - } - } - } - - void writeName(String name) throws IOException - { - while (true) - { - int n = name.indexOf('.'); - if (n < 0) - { - n = name.length(); - } - if (n <= 0) - { - writeByte(0); - return; - } - Integer offset = names.get(name); - if (offset != null) - { - int val = offset.intValue(); - - if (val > off) - { - logger.log(Level.WARNING, - "DNSOutgoing writeName failed val=" + val + " name=" + name); - } - - writeByte((val >> 8) | 0xC0); - writeByte(val); - return; - } - names.put(name, off); - writeUTF(name, 0, n); - name = name.substring(n); - if (name.startsWith(".")) - { - name = name.substring(1); - } - } - } - - void writeQuestion(DNSQuestion question) throws IOException - { - writeName(question.name); - writeShort(question.type); - writeShort(question.clazz); - } - - void writeRecord(DNSRecord rec, long now) throws IOException - { - int save = off; - try - { - writeName(rec.name); - writeShort(rec.type); - writeShort(rec.clazz | - ((rec.unique && multicast) ? DNSConstants.CLASS_UNIQUE : 0)); - writeInt((now == 0) ? rec.ttl : rec.getRemainingTTL(now)); - writeShort(0); - int start = off; - rec.write(this); - int len = off - start; - data[start - 2] = (byte) (len >> 8); - data[start - 1] = (byte) (len & 0xFF); - } - catch (IOException e) - { - off = save; - throw e; - } - } - - /** - * Finish the message before sending it off. - */ - void finish() throws IOException - { - int save = off; - off = 0; - - writeShort(multicast ? 0 : id); - writeShort(flags); - writeShort(numQuestions); - writeShort(numAnswers); - writeShort(numAuthorities); - writeShort(numAdditionals); - off = save; - } - - boolean isQuery() - { - return (flags & DNSConstants.FLAGS_QR_MASK) == - DNSConstants.FLAGS_QR_QUERY; - } - - public boolean isEmpty() - { - return numQuestions == 0 && numAuthorities == 0 - && numAdditionals == 0 && numAnswers == 0; - } - - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append(isQuery() ? "dns[query," : "dns[response,"); - //buf.append(packet.getAddress().getHostAddress()); - buf.append(':'); - //buf.append(packet.getPort()); - //buf.append(",len="); - //buf.append(packet.getLength()); - buf.append(",id=0x"); - buf.append(Integer.toHexString(id)); - if (flags != 0) - { - buf.append(",flags=0x"); - buf.append(Integer.toHexString(flags)); - if ((flags & DNSConstants.FLAGS_QR_RESPONSE) != 0) - { - buf.append(":r"); - } - if ((flags & DNSConstants.FLAGS_AA) != 0) - { - buf.append(":aa"); - } - if ((flags & DNSConstants.FLAGS_TC) != 0) - { - buf.append(":tc"); - } - } - if (numQuestions > 0) - { - buf.append(",questions="); - buf.append(numQuestions); - } - if (numAnswers > 0) - { - buf.append(",answers="); - buf.append(numAnswers); - } - if (numAuthorities > 0) - { - buf.append(",authorities="); - buf.append(numAuthorities); - } - if (numAdditionals > 0) - { - buf.append(",additionals="); - buf.append(numAdditionals); - } - buf.append(",\nnames=" + names); - buf.append(",\nauthorativeAnswers=" + authorativeAnswers); - - buf.append("]"); - return buf.toString(); - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java deleted file mode 100644 index f6abaa7..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSQuestion.java +++ /dev/null @@ -1,68 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.logging.*; - -/** - * A DNS question. - * - * @version %I%, %G% - * @author Arthur van Hoff - */ -public final class DNSQuestion - extends DNSEntry -{ - private static Logger logger = - Logger.getLogger(DNSQuestion.class.toString()); - - /** - * Create a question. - * @param name - * @param type - * @param clazz - */ - public DNSQuestion(String name, int type, int clazz) - { - super(name, type, clazz); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Check if this question is answered by a given DNS record. - */ - boolean answeredBy(DNSRecord rec) - { - return (clazz == rec.clazz) && - ((type == rec.type) || - (type == DNSConstants.TYPE_ANY)) && - name.equals(rec.name); - } - - /** - * For debugging only. - */ - @Override - public String toString() - { - return toString("question", null); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java deleted file mode 100644 index 673bbc4..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSRecord.java +++ /dev/null @@ -1,796 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.logging.*; - -/** - * DNS record - * - * @version %I%, %G% - * @author Arthur van Hoff, Rick Blair, Werner Randelshofer, Pierre Frisch - */ -public abstract class DNSRecord extends DNSEntry -{ - private static Logger logger = - Logger.getLogger(DNSRecord.class.toString()); - int ttl; - private long created; - - /** - * Create a DNSRecord with a name, type, clazz, and ttl. - */ - DNSRecord(String name, int type, int clazz, int ttl) - { - super(name, type, clazz); - this.ttl = ttl; - this.created = System.currentTimeMillis(); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * True if this record is the same as some other record. - * @param other obj to be compared to. - */ - @Override - public boolean equals(Object other) - { - return (other instanceof DNSRecord) && sameAs((DNSRecord) other); - } - - /** - * True if this record is the same as some other record. - */ - boolean sameAs(DNSRecord other) - { - return super.equals(other) && sameValue(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 type == other.type; - } - - /** - * 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(JmDNS dns, long expirationTime); - - /** - * Handles a responserepresented 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(JmDNS dns); - - /** - * Adds this as an answer to the provided outgoing datagram. - */ - abstract DNSOutgoing addAnswer(JmDNS 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 (int i = msg.numAnswers; i-- > 0;) - { - if (suppressedBy(msg.answers.get(i))) - { - 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 supressed by an answer. - * This is the case if this record would not have a - * significantly longer TTL. - */ - boolean suppressedBy(DNSRecord other) - { - if (sameAs(other) && (other.ttl > ttl / 2)) - { - return true; - } - return false; - } - - /** - * Get the expiration time of this record. - */ - long getExpirationTime(int percent) - { - 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); - } - - /** - * Check if the record is expired. - */ - boolean isExpired(long now) - { - return getExpirationTime(100) <= now; - } - - /** - * Check if the record is stale, ie it has outlived - * more than half of its TTL. - */ - 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; - } - - /** - * Write this record into an outgoing message. - */ - abstract void write(DNSOutgoing out) throws IOException; - - /** - * Address record. - */ - static class Address extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Address.class.toString()); - InetAddress addr; - - Address(String name, int type, int clazz, int ttl, InetAddress addr) - { - super(name, type, clazz, ttl); - this.addr = addr; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - Address(String name, int type, int clazz, int ttl, byte[] rawAddress) - { - super(name, type, clazz, ttl); - try - { - this.addr = InetAddress.getByAddress(rawAddress); - } - catch (UnknownHostException exception) - { - logger.log(Level.WARNING, "Address() exception ", exception); - } - } - - @Override - void write(DNSOutgoing out) throws IOException - { - if (addr != null) - { - byte[] buffer = addr.getAddress(); - if (DNSConstants.TYPE_A == type) - { - // 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); - } - } - else - { - // 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); - } - } - - boolean same(DNSRecord other) - { - return ((sameName(other)) && ((sameValue(other)))); - } - - boolean sameName(DNSRecord other) - { - return name.equalsIgnoreCase(((Address) other).name); - } - - @Override - boolean sameValue(DNSRecord other) - { - return addr.equals(((Address) other).getAddress()); - } - - 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. - */ - private byte[] toByteArray() - { - try - { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream dout = new DataOutputStream(bout); - dout.write(name.getBytes("UTF8")); - dout.writeShort(type); - dout.writeShort(clazz); - //dout.writeInt(len); - byte[] buffer = addr.getAddress(); - for (int i = 0; i < buffer.length; i++) - { - dout.writeByte(buffer[i]); - } - 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. - */ - private int lexCompare(DNSRecord.Address 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; - } - - /** - * Does the necessary actions, when this as a query. - */ - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - DNSRecord.Address dnsAddress = - dns.getLocalHost().getDNSAddressRecord(this); - if (dnsAddress != null) - { - if (dnsAddress.sameType(this) && - dnsAddress.sameName(this) && - (!dnsAddress.sameValue(this))) - { - logger.finer( - "handleQuery() Conflicting probe detected. dns state " + - dns.getState() + - " lex compare " + lexCompare(dnsAddress)); - // Tie-breaker test - if (dns.getState().isProbing() && lexCompare(dnsAddress) >= 0) - { - // We lost the tie-break. We have to choose a different name. - dns.getLocalHost().incrementHostName(); - dns.getCache().clear(); - for (ServiceInfo info : dns.services.values()) - info.revertState(); - } - dns.revertState(); - return true; - } - } - return false; - } - - /** - * Does the necessary actions, when this as a response. - */ - @Override - boolean handleResponse(JmDNS dns) - { - DNSRecord.Address dnsAddress = - dns.getLocalHost().getDNSAddressRecord(this); - if (dnsAddress != null) - { - if (dnsAddress.sameType(this) && - dnsAddress.sameName(this) && - (!dnsAddress.sameValue(this))) - { - logger.finer("handleResponse() Denial detected"); - - if (dns.getState().isProbing()) - { - dns.getLocalHost().incrementHostName(); - dns.getCache().clear(); - for (ServiceInfo info : dns.services.values()) - info.revertState(); - } - dns.revertState(); - return true; - } - } - return false; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - return out; - } - - @Override - public String toString() - { - return toString(" address '" + - (addr != null ? addr.getHostAddress() : "null") + "'"); - } - - } - - /** - * Pointer record. - */ - static class Pointer extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Pointer.class.toString()); - String alias; - - Pointer(String name, int type, int clazz, int ttl, String alias) - { - super(name, type, clazz, ttl); - this.alias = alias; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - void write(DNSOutgoing out) throws IOException - { - out.writeName(alias); - } - - @Override - boolean sameValue(DNSRecord other) - { - return alias.equals(((Pointer) other).alias); - } - - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - // Nothing to do (?) - // I think there is no possibility - // for conflicts for this record type? - return false; - } - - @Override - boolean handleResponse(JmDNS 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(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - return out; - } - - @Override - public String toString() - { - return toString(alias); - } - } - - static class Text extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Text.class.toString()); - byte text[]; - - Text(String name, int type, int clazz, int ttl, byte text[]) - { - super(name, type, clazz, ttl); - this.text = text; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - void write(DNSOutgoing out) throws IOException - { - out.writeBytes(text, 0, text.length); - } - - @Override - boolean sameValue(DNSRecord other) - { - Text txt = (Text) other; - 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 - boolean handleQuery(JmDNS dns, long expirationTime) - { - // Nothing to do (?) - // I think there is no possibility for conflicts for this record type? - return false; - } - - @Override - boolean handleResponse(JmDNS 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(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - return out; - } - - @Override - public String toString() - { - return toString((text.length > 10) ? - new String(text, 0, 7) + "..." : - new String(text)); - } - } - - /** - * Service record. - */ - static class Service extends DNSRecord - { - private static Logger logger = - Logger.getLogger(Service.class.toString()); - int priority; - int weight; - int port; - String server; - - Service(String name, - int type, - int clazz, - int ttl, - int priority, - int weight, - int port, - String server) - { - super(name, type, clazz, ttl); - this.priority = priority; - this.weight = weight; - this.port = port; - this.server = server; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - void write(DNSOutgoing out) throws IOException - { - out.writeShort(priority); - out.writeShort(weight); - out.writeShort(port); - out.writeName(server); - } - - private byte[] toByteArray() - { - try - { - ByteArrayOutputStream bout = new ByteArrayOutputStream(); - DataOutputStream dout = new DataOutputStream(bout); - dout.write(name.getBytes("UTF8")); - dout.writeShort(type); - dout.writeShort(clazz); - //dout.writeInt(len); - dout.writeShort(priority); - dout.writeShort(weight); - dout.writeShort(port); - dout.write(server.getBytes("UTF8")); - dout.close(); - return bout.toByteArray(); - } - catch (IOException e) - { - throw new InternalError(); - } - } - - private int lexCompare(DNSRecord.Service 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; - } - - @Override - boolean sameValue(DNSRecord other) - { - Service s = (Service) other; - return (priority == s.priority) && - (weight == s.weight) && - (port == s.port) && - server.equals(s.server); - } - - @Override - boolean handleQuery(JmDNS dns, long expirationTime) - { - ServiceInfo info = dns.services.get(name.toLowerCase()); - if (info != null && - (port != info.port || - !server.equalsIgnoreCase(dns.getLocalHost().getName()))) - { - logger.finer("handleQuery() Conflicting probe detected"); - - // Tie breaker test - if (info.getState().isProbing() && - lexCompare(new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - dns.getLocalHost().getName())) >= 0) - { - // We lost the tie break - String oldName = info.getQualifiedName().toLowerCase(); - info.setName(dns.incrementName(info.getName())); - dns.services.remove(oldName); - dns.services.put(info.getQualifiedName().toLowerCase(), info); - logger.finer( - "handleQuery() Lost tie break: new unique name chosen:" + info.getName()); - - } - info.revertState(); - return true; - - } - return false; - } - - @Override - boolean handleResponse(JmDNS dns) - { - ServiceInfo info = dns.services.get(name.toLowerCase()); - if (info != null && - (port != info.port || !server.equalsIgnoreCase(dns.getLocalHost().getName()))) - { - logger.finer("handleResponse() Denial detected"); - - if (info.getState().isProbing()) - { - String oldName = info.getQualifiedName().toLowerCase(); - info.setName(dns.incrementName(info.getName())); - dns.services.remove(oldName); - dns.services.put(info.getQualifiedName().toLowerCase(), info); - logger.finer( - "handleResponse() New unique name chose:" + info.getName()); - - } - info.revertState(); - return true; - } - return false; - } - - @Override - DNSOutgoing addAnswer(JmDNS dns, - DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out) - throws IOException - { - ServiceInfo info = dns.services.get(name.toLowerCase()); - if (info != null) - { - if (this.port == info.port != server.equals(dns.getLocalHost().getName())) - { - return dns.addAnswer(in, addr, port, out, - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - dns.getLocalHost().getName())); - } - } - return out; - } - - @Override - public String toString() - { - return toString(server + ":" + port); - } - } - - public String toString(String other) - { - return toString("record", ttl + "/" + - getRemainingTTL(System.currentTimeMillis()) -// + "," + other - ); - } -} - diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java deleted file mode 100644 index 5016050..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/DNSState.java +++ /dev/null @@ -1,139 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; -import java.util.logging.*; - -/** - * DNSState defines the possible states for services registered with JmDNS. - * - * @author Werner Randelshofer, Rick Blair - * @version 1.0 May 23, 2004 Created. - */ -public class DNSState - implements Comparable<DNSState> -{ - private static Logger logger = - Logger.getLogger(DNSState.class.toString()); - - private final String name; - - /** - * Ordinal of next state to be created. - */ - private static int nextOrdinal = 0; - /** - * Assign an ordinal to this state. - */ - private final int ordinal = nextOrdinal++; - /** - * Logical sequence of states. - * The sequence is consistent with the ordinal of a state. - * This is used for advancing through states. - */ - private final static ArrayList<DNSState> sequence - = new ArrayList<DNSState>(); - - private DNSState(String name) - { - this.name = name; - sequence.add(this); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - @Override - public final String toString() - { - return name; - } - - public static final DNSState PROBING_1 = new DNSState("probing 1"); - public static final DNSState PROBING_2 = new DNSState("probing 2"); - public static final DNSState PROBING_3 = new DNSState("probing 3"); - public static final DNSState ANNOUNCING_1 = new DNSState("announcing 1"); - public static final DNSState ANNOUNCING_2 = new DNSState("announcing 2"); - public static final DNSState ANNOUNCED = new DNSState("announced"); - public static final DNSState CANCELED = new DNSState("canceled"); - - /** - * Returns the next advanced state. - * In general, this advances one step in the following sequence: PROBING_1, - * PROBING_2, PROBING_3, ANNOUNCING_1, ANNOUNCING_2, ANNOUNCED. - * Does not advance for ANNOUNCED and CANCELED state. - * @return Returns the next advanced state. - */ - public final DNSState advance() - { - return (isProbing() || isAnnouncing()) ? - sequence.get(ordinal + 1) : - this; - } - - /** - * Returns to the next reverted state. - * All states except CANCELED revert to PROBING_1. - * Status CANCELED does not revert. - * @return Returns to the next reverted state. - */ - public final DNSState revert() - { - return (this == CANCELED) ? this : PROBING_1; - } - - /** - * Returns true, if this is a probing state. - * @return Returns true, if this is a probing state. - */ - public boolean isProbing() - { - return compareTo(PROBING_1) >= 0 && compareTo(PROBING_3) <= 0; - } - - /** - * Returns true, if this is an announcing state. - * @return Returns true, if this is an announcing state. - */ - public boolean isAnnouncing() - { - return compareTo(ANNOUNCING_1) >= 0 && compareTo(ANNOUNCING_2) <= 0; - } - - /** - * Returns true, if this is an announced state. - * @return Returns true, if this is an announced state. - */ - public boolean isAnnounced() - { - return compareTo(ANNOUNCED) == 0; - } - - /** - * Compares two states. - * The states compare as follows: - * PROBING_1 < PROBING_2 < PROBING_3 < ANNOUNCING_1 < - * ANNOUNCING_2 < RESPONDING < ANNOUNCED < CANCELED. - */ - public int compareTo(DNSState state) - { - return ordinal - state.ordinal; - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java deleted file mode 100644 index 644afc9..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/HostInfo.java +++ /dev/null @@ -1,173 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.net.*; -import java.util.logging.*; - - -/** - * HostInfo information on the local host to be able to cope with change of addresses. - * - * @version %I%, %G% - * @author Pierre Frisch, Werner Randelshofer - */ -class HostInfo -{ - private static Logger logger = Logger.getLogger(HostInfo.class.toString()); - protected String name; - protected InetAddress address; - protected NetworkInterface interfaze; - /** - * This is used to create a unique name for the host name. - */ - private int hostNameCount; - - public HostInfo(InetAddress address, String name) - { - super(); - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - - this.address = address; - this.name = name; - if (address != null) - { - try - { - interfaze = NetworkInterface.getByInetAddress(address); - } - catch (Exception exception) - { - // FIXME Shouldn't we take an action here? - logger.log(Level.WARNING, - "LocalHostInfo() exception ", exception); - } - } - } - - public String getName() - { - return name; - } - - public InetAddress getAddress() - { - return address; - } - - public NetworkInterface getInterface() - { - return interfaze; - } - - synchronized String incrementHostName() - { - hostNameCount++; - int plocal = name.indexOf(".local."); - int punder = name.lastIndexOf("-"); - name = name.substring(0, (punder == -1 ? plocal : punder)) + "-" + - hostNameCount + ".local."; - return name; - } - - boolean shouldIgnorePacket(DatagramPacket packet) - { - boolean result = false; - if (getAddress() != null) - { - InetAddress from = packet.getAddress(); - if (from != null) - { - if (from.isLinkLocalAddress() && - (!getAddress().isLinkLocalAddress())) - { - // Ignore linklocal packets on regular interfaces, unless this is - // also a linklocal interface. This is to avoid duplicates. This is - // a terrible hack caused by the lack of an API to get the address - // of the interface on which the packet was received. - result = true; - } - if (from.isLoopbackAddress() && - (!getAddress().isLoopbackAddress())) - { - // Ignore loopback packets on a regular interface unless this is - // also a loopback interface. - result = true; - } - } - } - return result; - } - - DNSRecord.Address getDNSAddressRecord(DNSRecord.Address address) - { - return (DNSConstants.TYPE_AAAA == address.type ? - getDNS6AddressRecord() : - getDNS4AddressRecord()); - } - - DNSRecord.Address getDNS4AddressRecord() - { - if ((getAddress() != null) && - ((getAddress() instanceof Inet4Address) || - ((getAddress() instanceof Inet6Address) && - (((Inet6Address) getAddress()).isIPv4CompatibleAddress())))) - { - return new DNSRecord.Address(getName(), - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, getAddress()); - } - return null; - } - - DNSRecord.Address getDNS6AddressRecord() - { - if ((getAddress() != null) && (getAddress() instanceof Inet6Address)) - { - return new DNSRecord.Address( - getName(), - DNSConstants.TYPE_AAAA, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - getAddress()); - } - return null; - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append("local host info["); - buf.append(getName() != null ? getName() : "no name"); - buf.append(", "); - buf.append(getInterface() != null ? - getInterface().getDisplayName() : - "???"); - buf.append(":"); - buf.append(getAddress() != null ? - getAddress().getHostAddress() : - "no address"); - buf.append("]"); - return buf.toString(); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java deleted file mode 100644 index 96420ba..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java +++ /dev/null @@ -1,3048 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.*; - -import net.java.sip.communicator.util.*; - -// REMIND: multiple IP addresses - -/** - * mDNS implementation in Java. - * - * @version %I%, %G% - * @author Arthur van Hoff, Rick Blair, Jeff Sonstein, - * Werner Randelshofer, Pierre Frisch, Scott Lewis - * @author Christian Vincenot - */ -public class JmDNS -{ - private static final Logger logger - = Logger.getLogger(JmDNS.class); - - /** - * The version of JmDNS. - */ - public static String VERSION = "2.0"; - - /** - * This is the multicast group, we are listening to for - * multicast DNS messages. - */ - private InetAddress group; - /** - * This is our multicast socket. - */ - private MulticastSocket socket; - - /** - * Used to fix live lock problem on unregester. - */ - - protected boolean closed = false; - - /** - * Holds instances of JmDNS.DNSListener. - * Must by a synchronized collection, because it is updated from - * concurrent threads. - */ - private List<DNSListener> listeners; - /** - * Holds instances of ServiceListener's. - * Keys are Strings holding a fully qualified service type. - * Values are LinkedList's of ServiceListener's. - */ - private Map<String, List<ServiceListener>> serviceListeners; - /** - * Holds instances of ServiceTypeListener's. - */ - private List<ServiceTypeListener> typeListeners; - - - /** - * Cache for DNSEntry's. - */ - private DNSCache cache; - - /** - * This hashtable holds the services that have been registered. - * Keys are instances of String which hold an all lower-case version of the - * fully qualified service name. - * Values are instances of ServiceInfo. - */ - Map<String, ServiceInfo> services; - - /** - * This hashtable holds the service types that have been registered or - * that have been received in an incoming datagram. - * Keys are instances of String which hold an all lower-case version of the - * fully qualified service type. - * Values hold the fully qualified service type. - */ - Map<String, String> serviceTypes; - - /** - * Handle on the local host - */ - HostInfo localHost; - - private Thread incomingListener = null; - - /** - * Throttle count. - * This is used to count the overall number of probes sent by JmDNS. - * When the last throttle increment happened . - */ - private int throttle; - /** - * Last throttle increment. - */ - private long lastThrottleIncrement; - - /** - * The timer is used to dispatch all outgoing messages of JmDNS. - * It is also used to dispatch maintenance tasks for the DNS cache. - */ - private Timer timer; - - /** - * The source for random values. - * This is used to introduce random delays in responses. This reduces the - * potential for collisions on the network. - */ - private final static Random random = new Random(); - - /** - * This lock is used to coordinate processing of incoming and outgoing - * messages. This is needed, because the Rendezvous Conformance Test - * does not forgive race conditions. - */ - private Object ioLock = new Object(); - - /** - * If an incoming package which needs an answer is truncated, we store it - * here. We add more incoming DNSRecords to it, until the JmDNS.Responder - * timer picks it up. - * Remind: This does not work well with multiple planned answers for packages - * that came in from different clients. - */ - private DNSIncoming plannedAnswer; - - // State machine - /** - * The state of JmDNS. - * <p/> - * For proper handling of concurrency, this variable must be - * changed only using methods advanceState(), revertState() and cancel(). - */ - private DNSState state = DNSState.PROBING_1; - - /** - * Timer task associated to the host name. - * This is used to prevent from having multiple tasks associated to the host - * name at the same time. - */ - TimerTask task; - - /** - * This hashtable is used to maintain a list of service types being collected - * by this JmDNS instance. - * The key of the hashtable is a service type name, the value is an instance - * of JmDNS.ServiceCollector. - * - * @see #list - */ - private HashMap<String, ServiceCollector> serviceCollectors = new HashMap<String, ServiceCollector>(); - - /** - * Create an instance of JmDNS. - * @throws java.io.IOException - */ - public JmDNS() - throws IOException - { - //String SLevel = System.getProperty("jmdns.debug"); - - if (logger.isDebugEnabled()) - logger.debug("JmDNS instance created"); - try - { - InetAddress addr = InetAddress.getLocalHost(); - // [PJYF Oct 14 2004] Why do we disallow the loopback address? - init(addr.isLoopbackAddress() ? null : addr, addr.getHostName()); - } - catch (IOException exc) - { - logger.error("Failed to get a reference to localhost", exc); - init(null, "computer"); - } - } - - /** - * Create an instance of JmDNS and bind it to a - * specific network interface given its IP-address. - * @param addr - * @throws java.io.IOException - */ - public JmDNS(InetAddress addr) - throws IOException - { - try - { - init(addr, addr.getHostName()); - } - catch (IOException e) - { - init(null, "computer"); - } - } - - /** - * Initialize everything. - * - * @param address The interface to which JmDNS binds to. - * @param name The host name of the interface. - */ - private void init(InetAddress address, String name) throws IOException - { - // A host name with "." is illegal. - // so strip off everything and append .local. - int idx = name.indexOf("."); - if (idx > 0) - { - name = name.substring(0, idx); - } - name += ".local."; - // localHost to IP address binding - localHost = new HostInfo(address, name); - - cache = new DNSCache(100); - - listeners = Collections.synchronizedList(new ArrayList<DNSListener>()); - serviceListeners = new HashMap<String, List<ServiceListener>>(); - typeListeners = new ArrayList<ServiceTypeListener>(); - - services = new Hashtable<String, ServiceInfo>(20); - serviceTypes = new Hashtable<String, String>(20); - - // REMIND: If I could pass in a name for the Timer thread, - // I would pass 'JmDNS.Timer'. - timer = new Timer(); - new RecordReaper().start(); - - incomingListener = new Thread( - new SocketListener(), "JmDNS.SocketListener"); - incomingListener.setDaemon(true); - // Bind to multicast socket - openMulticastSocket(localHost); - start(services.values()); - } - - private void start(Collection<ServiceInfo> serviceInfos) - { - state = DNSState.PROBING_1; - incomingListener.start(); - new Prober().start(); - for (ServiceInfo serviceInfo : serviceInfos) - { - try - { - registerService(new ServiceInfo(serviceInfo)); - } - catch (Exception exception) - { - logger.warn("start() Registration exception ", exception); - } - } - } - - private void openMulticastSocket(HostInfo hostInfo) throws IOException - { - if (group == null) - { - group = InetAddress.getByName(DNSConstants.MDNS_GROUP); - } - if (socket != null) - { - this.closeMulticastSocket(); - } - socket = new MulticastSocket(DNSConstants.MDNS_PORT); - if ((hostInfo != null) && (localHost.getInterface() != null)) - { - socket.setNetworkInterface(hostInfo.getInterface()); - } - socket.setTimeToLive(255); - socket.joinGroup(group); - } - - private void closeMulticastSocket() - { - if (logger.isDebugEnabled()) - logger.debug("closeMulticastSocket()"); - if (socket != null) - { - // close socket - try - { - socket.leaveGroup(group); - socket.close(); - if (incomingListener != null) - { - incomingListener.join(); - } - } - catch (Exception exception) - { - logger.warn("closeMulticastSocket() Close socket exception ", - exception); - } - socket = null; - } - } - - // State machine - /** - * Sets the state and notifies all objects that wait on JmDNS. - */ - synchronized void advanceState() - { - state = state.advance(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on JmDNS. - */ - synchronized void revertState() - { - state = state.revert(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on JmDNS. - */ - synchronized void cancel() - { - state = DNSState.CANCELED; - notifyAll(); - } - - /** - * Returns the current state of this info. - */ - DNSState getState() - { - return state; - } - - - /** - * Return the DNSCache associated with the cache variable - */ - DNSCache getCache() - { - return cache; - } - - /** - * Return the HostName associated with this JmDNS instance. - * Note: May not be the same as what started. The host name is subject to - * negotiation. - * @return Return the HostName associated with this JmDNS instance. - */ - public String getHostName() - { - return localHost.getName(); - } - - public HostInfo getLocalHost() - { - return localHost; - } - - /** - * Return the address of the interface to which this instance of JmDNS is - * bound. - * @return Return the address of the interface to which this instance - * of JmDNS is bound. - * @throws java.io.IOException - */ - public InetAddress getInterface() - throws IOException - { - return socket.getInterface(); - } - - /** - * Get service information. If the information is not cached, the method - * will block until updated information is received. - * <p/> - * Usage note: Do not call this method from the AWT event dispatcher thread. - * You will make the user interface unresponsive. - * - * @param type fully qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - * @return null if the service information cannot be obtained - */ - public ServiceInfo getServiceInfo(String type, String name) - { - return getServiceInfo(type, name, 3 * 1000); - } - - /** - * Get service information. If the information is not cached, the method - * will block for the given timeout until updated information is received. - * <p/> - * Usage note: If you call this method from the AWT event dispatcher thread, - * use a small timeout, or you will make the user interface unresponsive. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - * @param timeout timeout in milliseconds - * @return null if the service information cannot be obtained - */ - public ServiceInfo getServiceInfo(String type, String name, int timeout) - { - ServiceInfo info = new ServiceInfo(type, name); - new ServiceInfoResolver(info).start(); - - try - { - long end = System.currentTimeMillis() + timeout; - long delay; - synchronized (info) - { - while (!info.hasData() && - (delay = end - System.currentTimeMillis()) > 0) - { - info.wait(delay); - } - } - } - catch (InterruptedException e) - { - // empty - } - - return (info.hasData()) ? info : null; - } - - /** - * Request service information. The information about the service is - * requested and the ServiceListener.resolveService method is called as soon - * as it is available. - * <p/> - * Usage note: Do not call this method from the AWT event dispatcher thread. - * You will make the user interface unresponsive. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - */ - public void requestServiceInfo(String type, String name) - { - requestServiceInfo(type, name, 3 * 1000); - } - - /** - * Request service information. The information about the service - * is requested and the ServiceListener.resolveService method is - * called as soon as it is available. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code> . - * @param name unqualified service name, such as <code>foobar</code> . - * @param timeout timeout in milliseconds - */ - public void requestServiceInfo(String type, String name, int timeout) - { - registerServiceType(type); - ServiceInfo info = new ServiceInfo(type, name); - new ServiceInfoResolver(info).start(); - - try - { - long end = System.currentTimeMillis() + timeout; - long delay; - synchronized (info) - { - while (!info.hasData() && - (delay = end - System.currentTimeMillis()) > 0) - { - info.wait(delay); - } - } - } - catch (InterruptedException e) - { - // empty - } - } - - void handleServiceResolved(ServiceInfo info) - { - List<ServiceListener> list = serviceListeners.get(info.type.toLowerCase()); - if (list != null) - { - ServiceEvent event = - new ServiceEvent(this, info.type, info.getName(), info); - // Iterate on a copy in case listeners will modify it - List<ServiceListener> listCopy - = new ArrayList<ServiceListener> (list); - for (ServiceListener serviceListener : listCopy) - serviceListener.serviceResolved(event); - } - } - - /** - * Listen for service types. - * - * @param listener listener for service types - * @throws java.io.IOException - */ - public void addServiceTypeListener(ServiceTypeListener listener) - throws IOException - { - synchronized (this) - { - typeListeners.remove(listener); - typeListeners.add(listener); - } - - // report cached service types - for (String serviceType : serviceTypes.values()) - { - listener.serviceTypeAdded( - new ServiceEvent(this, serviceType, null, null)); - } - - new TypeResolver().start(); - } - - /** - * Remove listener for service types. - * - * @param listener listener for service types - */ - public void removeServiceTypeListener(ServiceTypeListener listener) - { - synchronized (this) - { - typeListeners.remove(listener); - } - } - - /** - * Listen for services of a given type. The type has to be a fully - * qualified type name such as <code>_http._tcp.local.</code>. - * - * @param type full qualified service type, - * such as <code>_http._tcp.local.</code>. - * @param listener listener for service updates - */ - public void addServiceListener(String type, ServiceListener listener) - { - String lotype = type.toLowerCase(); - removeServiceListener(lotype, listener); - List<ServiceListener> list = null; - synchronized (this) - { - list = serviceListeners.get(lotype); - if (list == null) - { - list = Collections.synchronizedList(new LinkedList<ServiceListener>()); - serviceListeners.put(lotype, list); - } - list.add(listener); - } - - // report cached service types - for (Iterator<DNSCache.CacheNode> i = cache.iterator(); i.hasNext();) - { - for (DNSCache.CacheNode n = i.next(); n != null; n = n.next()) - { - DNSRecord rec = (DNSRecord) n.getValue(); - if (rec.type == DNSConstants.TYPE_SRV) - { - if (rec.name.endsWith(type)) - { - listener.serviceAdded( - new ServiceEvent( - this, - type, - toUnqualifiedName(type, rec.name), - null)); - } - } - } - } - new ServiceResolver(type).start(); - } - - /** - * Remove listener for services of a given type. - * - * @param type of listener to be removed - * @param listener listener for service updates - */ - public void removeServiceListener(String type, ServiceListener listener) - { - type = type.toLowerCase(); - List<ServiceListener> list = serviceListeners.get(type); - if (list != null) - { - synchronized (this) - { - list.remove(listener); - if (list.size() == 0) - { - serviceListeners.remove(type); - } - } - } - } - - /** - * Register a service. The service is registered - * for access by other jmdns clients. - * The name of the service may be changed to make it unique. - * @param info of service - * @throws java.io.IOException - */ - public void registerService(ServiceInfo info) throws IOException - { - registerServiceType(info.type); - - // bind the service to this address - info.server = localHost.getName(); - info.addr = localHost.getAddress(); - - synchronized (this) - { - makeServiceNameUnique(info); - services.put(info.getQualifiedName().toLowerCase(), info); - } - - new /*Service*/Prober().start(); - try - { - synchronized (info) - { - while (info.getState().compareTo(DNSState.ANNOUNCED) < 0) - { - info.wait(); - } - } - } - catch (InterruptedException e) - { - logger.error(e.getMessage(), e); - } - if (logger.isDebugEnabled()) - logger.debug("registerService() JmDNS registered service as " + info); - } - - /** - * Unregister a service. The service should have been registered. - * @param info of service - */ - public void unregisterService(ServiceInfo info) - { - synchronized (this) - { - services.remove(info.getQualifiedName().toLowerCase()); - } - info.cancel(); - - // Note: We use this lock object to synchronize on it. - // Synchronizing on another object (e.g. the ServiceInfo) does - // not make sense, because the sole purpose of the lock is to - // wait until the canceler has finished. If we synchronized on - // the ServiceInfo or on the Canceler, we would block all - // accesses to synchronized methods on that object. This is not - // what we want! - Object lock = new Object(); - new Canceler(info, lock).start(); - - // Remind: We get a deadlock here, if the Canceler does not run! - try - { - synchronized (lock) - { - lock.wait(); - } - } - catch (InterruptedException e) - { - // empty - } - } - - /** - * Unregister all services. - */ - public void unregisterAllServices() - { - if (logger.isDebugEnabled()) - logger.debug("unregisterAllServices()"); - if (services.size() == 0) - { - return; - } - - Collection<ServiceInfo> list; - synchronized (this) - { - list = new LinkedList<ServiceInfo>(services.values()); - services.clear(); - } - for (Iterator<ServiceInfo> iterator = list.iterator(); iterator.hasNext();) - { - iterator.next().cancel(); - } - - - Object lock = new Object(); - new Canceler(list, lock).start(); - // Remind: We get a livelock here, if the Canceler does not run! - try - { - synchronized (lock) - { - if (!closed) - { - lock.wait(); - } - } - } - catch (InterruptedException e) - { - // empty - } - } - - /** - * Register a service type. If this service type was not already known, - * all service listeners will be notified of the new service type. - * Service types are automatically registered as they are discovered. - * @param type of service - */ - public void registerServiceType(String type) - { - String name = type.toLowerCase(); - if (serviceTypes.get(name) == null) - { - if ((type.indexOf("._mdns._udp.") < 0) && - !type.endsWith(".in-addr.arpa.")) - { - Collection<ServiceTypeListener> list; - synchronized (this) - { - serviceTypes.put(name, type); - list = new LinkedList<ServiceTypeListener>(typeListeners); - } - for (ServiceTypeListener listener : list) - listener - .serviceTypeAdded( - new ServiceEvent(this, type, null, null)); - } - } - } - - /** - * Generate a possibly unique name for a service using the information we - * have in the cache. - * - * @return returns true, if the name of the service info had to be changed. - */ - private boolean makeServiceNameUnique(ServiceInfo info) - { - String originalQualifiedName = info.getQualifiedName(); - long now = System.currentTimeMillis(); - - boolean collision; - do - { - collision = false; - - // Check for collision in cache - for (DNSCache.CacheNode j = cache.find( - info.getQualifiedName().toLowerCase()); - j != null; - j = j.next()) - { - DNSRecord a = (DNSRecord) j.getValue(); - if ((a.type == DNSConstants.TYPE_SRV) && !a.isExpired(now)) - { - DNSRecord.Service s = (DNSRecord.Service) a; - if (s.port != info.port || !s.server.equals(localHost.getName())) - { - if (logger.isDebugEnabled()) - logger.debug("makeServiceNameUnique() " + - "JmDNS.makeServiceNameUnique srv collision:" + - a + " s.server=" + s.server + " " + - localHost.getName() + " equals:" + - (s.server.equals(localHost.getName()))); - info.setName(incrementName(info.getName())); - collision = true; - break; - } - } - } - - // Check for collision with other service infos published by JmDNS - Object selfService = - services.get(info.getQualifiedName().toLowerCase()); - if (selfService != null && selfService != info) - { - info.setName(incrementName(info.getName())); - collision = true; - } - } - while (collision); - - return !(originalQualifiedName.equals(info.getQualifiedName())); - } - - String incrementName(String name) - { - try - { - int l = name.lastIndexOf('('); - int r = name.lastIndexOf(')'); - if ((l >= 0) && (l < r)) - { - name = name.substring(0, l) + "(" + - (Integer.parseInt(name.substring(l + 1, r)) + 1) + ")"; - } - else - { - name += " (2)"; - } - } - catch (NumberFormatException e) - { - name += " (2)"; - } - return name; - } - - /** - * Add a listener for a question. The listener will receive updates - * of answers to the question as they arrive, or from the cache if they - * are already available. - * @param listener to be added - * @param question - which the listener is responsible for. - */ - public void addListener(DNSListener listener, DNSQuestion question) - { - long now = System.currentTimeMillis(); - - // add the new listener - synchronized (this) - { - listeners.add(listener); - } - - // report existing matched records - if (question != null) - { - for (DNSCache.CacheNode i = cache.find(question.name); - i != null; - i = i.next()) - { - DNSRecord c = (DNSRecord) i.getValue(); - if (question.answeredBy(c) && !c.isExpired(now)) - { - listener.updateRecord(this, now, c); - } - } - } - } - - /** - * Remove a listener from all outstanding questions. - * The listener will no longer receive any updates. - */ - void removeListener(DNSListener listener) - { - synchronized (this) - { - listeners.remove(listener); - } - } - - - // Remind: Method updateRecord should receive a better name. - /** - * Notify all listeners that a record was updated. - */ - void updateRecord(long now, DNSRecord rec) - { - // We do not want to block the entire DNS - // while we are updating the record for each listener (service info) - List<DNSListener> listenerList = null; - synchronized (this) - { - listenerList = new ArrayList<DNSListener>(listeners); - } - - //System.out.println("OUT OF MUTEX!!!!!"); - - for (DNSListener listener : listenerList) - listener.updateRecord(this, now, rec); - - if (rec.type == DNSConstants.TYPE_PTR || - rec.type == DNSConstants.TYPE_SRV) - { - List<ServiceListener> serviceListenerList = null; - synchronized (this) - { - serviceListenerList = serviceListeners.get(rec.name.toLowerCase()); - // Iterate on a copy in case listeners will modify it - if (serviceListenerList != null) - { - serviceListenerList = new ArrayList<ServiceListener>(serviceListenerList); - } - } - if (serviceListenerList != null) - { - boolean expired = rec.isExpired(now); - String type = rec.getName(); - String name = ((DNSRecord.Pointer) rec).getAlias(); - // DNSRecord old = (DNSRecord)services.get(name.toLowerCase()); - if (!expired) - { - // new record - ServiceEvent event = - new ServiceEvent( - this, - type, - toUnqualifiedName(type, name), - null); - for (Iterator<ServiceListener> iterator = serviceListenerList.iterator(); - iterator.hasNext();) - { - iterator.next().serviceAdded(event); - } - } - else - { - // expire record - ServiceEvent event = - new ServiceEvent( - this, - type, - toUnqualifiedName(type, name), - null); - for (Iterator<ServiceListener> iterator = serviceListenerList.iterator(); - iterator.hasNext();) - { - iterator.next().serviceRemoved(event); - } - } - } - } - } - - /** - * Handle an incoming response. Cache answers, and pass them on to - * the appropriate questions. - */ - private void handleResponse(DNSIncoming msg) - throws IOException - { - long now = System.currentTimeMillis(); - - boolean hostConflictDetected = false; - boolean serviceConflictDetected = false; - - if (logger.isTraceEnabled()) - logger.trace("JMDNS/handleResponse received " + - msg.answers.size()+ " messages"); - for (DNSRecord rec : msg.answers) - { - if (logger.isTraceEnabled()) - logger.trace("PRINT: "+ rec); - //cache.add(rec); - } - - for (DNSRecord rec : msg.answers) - { - boolean isInformative = false; - boolean expired = rec.isExpired(now); - - if (logger.isTraceEnabled()) - logger.trace("JMDNS received : " + rec + " expired: "+expired); - - // update the cache - DNSRecord c = (DNSRecord) cache.get(rec); - if (c != null) - { - if (logger.isTraceEnabled()) - logger.trace("JMDNS has found "+rec+" in cache"); - if (expired) - { - isInformative = true; - cache.remove(c); - } - else - { - /* Special case for SIP Communicator. - * We want to be informed if a cache entry is modified - */ -// if ((c.isUnique() -// && c.getType() == DNSConstants.TYPE_TXT -// && ((c.getClazz() & DNSConstants.CLASS_IN) != 0))) -// isInformative = true; -// c.resetTTL(rec); -// rec = c; - if (logger.isTraceEnabled()) - logger.trace( - new Boolean(c.isUnique()).toString() + - c.getType()+c.getClazz() + "/" + - DNSConstants.TYPE_TXT + " "+DNSConstants.CLASS_IN); - - if ((rec.isUnique() - && ((rec.getType() & DNSConstants.TYPE_TXT) != 0) - && ((rec.getClazz() & DNSConstants.CLASS_IN) != 0))) - { - if (logger.isTraceEnabled()) - logger.trace("UPDATING CACHE !! "); - isInformative = true; - cache.remove(c); - cache.add(rec); - } - else - { - c.resetTTL(rec); - rec = c; - } - } - } - else - { - if (!expired) - { - isInformative = true; - if (logger.isTraceEnabled()) - logger.trace("Adding "+rec+" to the cache"); - cache.add(rec); - } - } - switch (rec.type) - { - case DNSConstants.TYPE_PTR: - // handle _mdns._udp records - if (rec.getName().indexOf("._mdns._udp.") >= 0) - { - if (!expired && - rec.name.startsWith("_services._mdns._udp.")) - { - isInformative = true; - registerServiceType(((DNSRecord.Pointer)rec).alias); - } - continue; - } - registerServiceType(rec.name); - break; - } - - - if ((rec.getType() == DNSConstants.TYPE_A) || - (rec.getType() == DNSConstants.TYPE_AAAA)) - { - hostConflictDetected |= rec.handleResponse(this); - } - else - { - serviceConflictDetected |= rec.handleResponse(this); - } - - // notify the listeners - if (isInformative) - { - updateRecord(now, rec); - } - - - } - - if (hostConflictDetected || serviceConflictDetected) - { - new Prober().start(); - } - } - - /** - * Handle an incoming query. See if we can answer any part of it - * given our service infos. - */ - private void handleQuery(DNSIncoming in, InetAddress addr, int port) - throws IOException - { - // Track known answers - boolean hostConflictDetected = false; - boolean serviceConflictDetected = false; - long expirationTime = System.currentTimeMillis() + - DNSConstants.KNOWN_ANSWER_TTL; - for (DNSRecord answer : in.answers) - { - if ((answer.getType() == DNSConstants.TYPE_A) || - (answer.getType() == DNSConstants.TYPE_AAAA)) - { - hostConflictDetected |= - answer.handleQuery(this, expirationTime); - } - else - { - serviceConflictDetected |= - answer.handleQuery(this, expirationTime); - } - } - - if (plannedAnswer != null) - { - plannedAnswer.append(in); - } - else - { - if (in.isTruncated()) - { - plannedAnswer = in; - } - - new Responder(in, addr, port).start(); - } - - if (hostConflictDetected || serviceConflictDetected) - { - new Prober().start(); - } - } - - /** - * Add an answer to a question. Deal with the case when the - * outgoing packet overflows - */ - DNSOutgoing addAnswer(DNSIncoming in, - InetAddress addr, - int port, - DNSOutgoing out, - DNSRecord rec) - throws IOException - { - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - } - try - { - out.addAnswer(in, rec); - } - catch (IOException e) - { - out.flags |= DNSConstants.FLAGS_TC; - out.id = in.id; - out.finish(); - send(out); - - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - out.addAnswer(in, rec); - } - return out; - } - - - /** - * Send an outgoing multicast DNS message. - */ - private void send(DNSOutgoing out) throws IOException - { - out.finish(); - if (!out.isEmpty()) - { - DatagramPacket packet = - new DatagramPacket( - out.data, out.off, group, DNSConstants.MDNS_PORT); - - try - { - DNSIncoming msg = new DNSIncoming(packet); - if (logger.isTraceEnabled()) - logger.trace("send() JmDNS out:" + msg.print(true)); - } - catch (IOException exc) - { - logger.error( - "send(DNSOutgoing) - JmDNS can not parse what it sends!!!", - exc); - } - socket.send(packet); - } - } - - /** - * Listen for multicast packets. - */ - class SocketListener implements Runnable - { - public void run() - { - try - { - byte buf[] = new byte[DNSConstants.MAX_MSG_ABSOLUTE]; - DatagramPacket packet = new DatagramPacket(buf, buf.length); - while (state != DNSState.CANCELED) - { - packet.setLength(buf.length); - socket.receive(packet); - if (state == DNSState.CANCELED) - { - break; - } - try - { - if (localHost.shouldIgnorePacket(packet)) - { - continue; - } - - DNSIncoming msg = new DNSIncoming(packet); - if (logger.isTraceEnabled()) - logger.trace("SocketListener.run() JmDNS in:" + - msg.print(true)); - - synchronized (ioLock) - { - if (msg.isQuery()) - { - if (packet.getPort() != DNSConstants.MDNS_PORT) - { - handleQuery(msg, - packet.getAddress(), - packet.getPort()); - } - handleQuery(msg, group, DNSConstants.MDNS_PORT); - } - else - { - handleResponse(msg); - } - } - } - catch (IOException e) - { - logger.warn( "run() exception ", e); - } - } - } - catch (IOException e) - { - if (state != DNSState.CANCELED) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - } - - - /** - * Periodicaly removes expired entries from the cache. - */ - private class RecordReaper extends TimerTask - { - public void start() - { - timer.schedule( this, - DNSConstants.RECORD_REAPER_INTERVAL, - DNSConstants.RECORD_REAPER_INTERVAL); - } - - @Override - public void run() - { - synchronized (JmDNS.this) - { - if (state == DNSState.CANCELED) - { - return; - } - if (logger.isTraceEnabled()) - logger.trace("run() JmDNS reaping cache"); - - // Remove expired answers from the cache - // ------------------------------------- - // To prevent race conditions, we defensively copy all cache - // entries into a list. - List<DNSEntry> list = new ArrayList<DNSEntry>(); - synchronized (cache) - { - for (Iterator<DNSCache.CacheNode> i = cache.iterator(); - i.hasNext();) - { - for (DNSCache.CacheNode n = i.next(); - n != null; - n = n.next()) - { - list.add(n.getValue()); - } - } - } - // Now, we remove them. - long now = System.currentTimeMillis(); - for (Iterator<DNSEntry> i = list.iterator(); i.hasNext();) - { - DNSRecord c = (DNSRecord)i.next(); - if (c.isExpired(now)) - { - updateRecord(now, c); - cache.remove(c); - } - } - } - } - } - - - /** - * The Prober sends three consecutive probes for all service infos - * that needs probing as well as for the host name. - * The state of each service info of the host name is advanced, - * when a probe has been sent for it. - * When the prober has run three times, it launches an Announcer. - * <p/> - * If a conflict during probes occurs, the affected service - * infos (and affected host name) are taken away from the prober. - * This eventually causes the prober tho cancel itself. - */ - private class Prober extends TimerTask - { - /** - * The state of the prober. - */ - DNSState taskState = DNSState.PROBING_1; - - public Prober() - { - // Associate the host name to this, if it needs probing - if (state == DNSState.PROBING_1) - { - task = this; - } - // Associate services to this, if they need probing - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> iterator = services.values().iterator(); - iterator.hasNext();) - { - ServiceInfo info = iterator.next(); - if (info.getState() == DNSState.PROBING_1) - { - info.task = this; - } - } - } - } - - - public void start() - { - long now = System.currentTimeMillis(); - if (now - lastThrottleIncrement < - DNSConstants.PROBE_THROTTLE_COUNT_INTERVAL) - { - throttle++; - } - else - { - throttle = 1; - } - lastThrottleIncrement = now; - - if (state == DNSState.ANNOUNCED && - throttle < DNSConstants.PROBE_THROTTLE_COUNT) - { - timer.schedule(this, - random.nextInt(1 + DNSConstants.PROBE_WAIT_INTERVAL), - DNSConstants.PROBE_WAIT_INTERVAL); - } - else - { - timer.schedule(this, - DNSConstants.PROBE_CONFLICT_INTERVAL, - DNSConstants.PROBE_CONFLICT_INTERVAL); - } - } - - @Override - public boolean cancel() - { - // Remove association from host name to this - if (task == this) - { - task = null; - } - - // Remove associations from services to this - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> i = services.values().iterator(); - i.hasNext();) - { - ServiceInfo info = i.next(); - if (info.task == this) - { - info.task = null; - } - } - } - - return super.cancel(); - } - - @Override - public void run() - { - synchronized (ioLock) - { - DNSOutgoing out = null; - try - { - // send probes for JmDNS itself - if (state == taskState && task == this) - { - if (out == null) - { - out = new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - } - out.addQuestion( - new DNSQuestion( - localHost.getName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAuthorativeAnswer(answer); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAuthorativeAnswer(answer); - } - advanceState(); - } - // send probes for services - // Defensively copy the services into a local list, - // to prevent race conditions with methods registerService - // and unregisterService. - List<ServiceInfo> list; - synchronized (JmDNS.this) - { - list = new LinkedList<ServiceInfo>(services.values()); - } - for (Iterator<ServiceInfo> i = list.iterator(); i.hasNext();) - { - ServiceInfo info = i.next(); - - synchronized (info) - { - if (info.getState() == taskState && - info.task == this) - { - info.advanceState(); - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS probing " + - info.getQualifiedName() + " state " + - info.getState()); - - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - } - out.addAuthorativeAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName())); - } - } - } - if (out != null) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS probing #" + taskState); - send(out); - } - else - { - // If we have nothing to send, another timer taskState - // ahead of us has done the job for us. We can cancel. - cancel(); - return; - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - - taskState = taskState.advance(); - if (!taskState.isProbing()) - { - cancel(); - - new Announcer().start(); - } - } - } - - } - - /** - * The Announcer sends an accumulated query of all announces, and advances - * the state of all serviceInfos, for which it has sent an announce. - * The Announcer also sends announcements and advances the state of JmDNS - * itself. - * <p/> - * When the announcer has run two times, it finishes. - */ - private class Announcer extends TimerTask - { - /** - * The state of the announcer. - */ - DNSState taskState = DNSState.ANNOUNCING_1; - - public Announcer() - { - // Associate host to this, if it needs announcing - if (state == DNSState.ANNOUNCING_1) - { - task = this; - } - // Associate services to this, if they need announcing - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> s = services.values().iterator(); s.hasNext();) - { - ServiceInfo info = s.next(); - if (info.getState() == DNSState.ANNOUNCING_1) - { - info.task = this; - } - } - } - } - - public void start() - { - timer.schedule(this, - DNSConstants.ANNOUNCE_WAIT_INTERVAL, - DNSConstants.ANNOUNCE_WAIT_INTERVAL); - } - - @Override - public boolean cancel() - { - // Remove association from host to this - if (task == this) - { - task = null; - } - - // Remove associations from services to this - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> i = services.values().iterator(); - i.hasNext();) - { - ServiceInfo info = i.next(); - if (info.task == this) - { - info.task = null; - } - } - } - - return super.cancel(); - } - - @Override - public void run() - { - DNSOutgoing out = null; - try - { - // send probes for JmDNS itself - if (state == taskState) - { - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - } - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - advanceState(); - } - // send announces for services - // Defensively copy the services into a local list, - // to prevent race conditions with methods registerService - // and unregisterService. - List<ServiceInfo> list; - synchronized (JmDNS.this) - { - list = new ArrayList<ServiceInfo>(services.values()); - } - for (Iterator<ServiceInfo> i = list.iterator(); i.hasNext();) - { - ServiceInfo info = i.next(); - synchronized (info) - { - if (info.getState() == taskState && info.task == this) - { - info.advanceState(); - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announcing " + - info.getQualifiedName() + - " state " + info.getState()); - - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | - DNSConstants.FLAGS_AA); - } - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName()), 0); - out.addAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName()), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.text), 0); - } - } - } - if (out != null) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announcing #" + taskState); - send(out); - } - else - { - // If we have nothing to send, another timer taskState ahead - // of us has done the job for us. We can cancel. - cancel(); - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - - taskState = taskState.advance(); - if (!taskState.isAnnouncing()) - { - cancel(); - - new Renewer().start(); - } - } - } - - /** - * The Renewer is there to send renewal announcment - * when the record expire for ours infos. - */ - private class Renewer extends TimerTask - { - /** - * The state of the announcer. - */ - DNSState taskState = DNSState.ANNOUNCED; - - public Renewer() - { - // Associate host to this, if it needs renewal - if (state == DNSState.ANNOUNCED) - { - task = this; - } - // Associate services to this, if they need renewal - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> s = services.values().iterator(); s.hasNext();) - { - ServiceInfo info = s.next(); - if (info.getState() == DNSState.ANNOUNCED) - { - info.task = this; - } - } - } - } - - public void start() - { - timer.schedule(this, - DNSConstants.ANNOUNCED_RENEWAL_TTL_INTERVAL, - DNSConstants.ANNOUNCED_RENEWAL_TTL_INTERVAL); - } - - @Override - public boolean cancel() - { - // Remove association from host to this - if (task == this) - { - task = null; - } - - // Remove associations from services to this - synchronized (JmDNS.this) - { - for (Iterator<ServiceInfo> i = services.values().iterator(); - i.hasNext();) - { - ServiceInfo info = i.next(); - if (info.task == this) - { - info.task = null; - } - } - } - - return super.cancel(); - } - - @Override - public void run() - { - DNSOutgoing out = null; - try - { - // send probes for JmDNS itself - if (state == taskState) - { - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - } - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - advanceState(); - } - // send announces for services - // Defensively copy the services into a local list, - // to prevent race conditions with methods registerService - // and unregisterService. - List<ServiceInfo> list; - synchronized (JmDNS.this) - { - list = new ArrayList<ServiceInfo>(services.values()); - } - for (Iterator<ServiceInfo> i = list.iterator(); i.hasNext();) - { - ServiceInfo info = i.next(); - synchronized (info) - { - if (info.getState() == taskState && info.task == this) - { - info.advanceState(); - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announced " + - info.getQualifiedName() + " state " + info.getState()); - - if (out == null) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | - DNSConstants.FLAGS_AA); - } - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName()), 0); - out.addAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName()), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.text), 0); - } - } - } - if (out != null) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS announced"); - send(out); - } - else - { - // If we have nothing to send, another timer taskState ahead - // of us has done the job for us. We can cancel. - cancel(); - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - - taskState = taskState.advance(); - if (!taskState.isAnnounced()) - { - cancel(); - - } - } - } - - /** - * The Responder sends a single answer for the specified service infos - * and for the host name. - */ - private class Responder extends TimerTask - { - private DNSIncoming in; - private InetAddress addr; - private int port; - - public Responder(DNSIncoming in, InetAddress addr, int port) - { - this.in = in; - this.addr = addr; - this.port = port; - } - - public void start() - { - // According to draft-cheshire-dnsext-multicastdns.txt - // chapter "8 Responding": - // We respond immediately if we know for sure, that we are - // the only one who can respond to the query. - // In all other cases, we respond within 20-120 ms. - // - // According to draft-cheshire-dnsext-multicastdns.txt - // chapter "7.2 Multi-Packet Known Answer Suppression": - // We respond after 20-120 ms if the query is truncated. - - boolean iAmTheOnlyOne = true; - for (DNSEntry entry : in.questions) - { - if (entry instanceof DNSQuestion) - { - DNSQuestion q = (DNSQuestion) entry; - if (logger.isTraceEnabled()) - logger.trace("start() question=" + q); - iAmTheOnlyOne &= (q.type == DNSConstants.TYPE_SRV - || q.type == DNSConstants.TYPE_TXT - || q.type == DNSConstants.TYPE_A - || q.type == DNSConstants.TYPE_AAAA - || localHost.getName().equalsIgnoreCase(q.name) - || services.containsKey(q.name.toLowerCase())); - if (!iAmTheOnlyOne) - { - break; - } - } - } - int delay = (iAmTheOnlyOne && !in.isTruncated()) ? - 0 : - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + - random.nextInt( - DNSConstants.RESPONSE_MAX_WAIT_INTERVAL - - DNSConstants.RESPONSE_MIN_WAIT_INTERVAL + 1) - - in.elapseSinceArrival(); - if (delay < 0) - { - delay = 0; - } - if (logger.isTraceEnabled()) - logger.trace("start() Responder chosen delay=" + delay); - timer.schedule(this, delay); - } - - @Override - public void run() - { - synchronized (ioLock) - { - if (plannedAnswer == in) - { - plannedAnswer = null; - } - - // We use these sets to prevent duplicate records - // FIXME - This should be moved into DNSOutgoing - HashSet<DNSQuestion> questions = new HashSet<DNSQuestion>(); - HashSet<DNSRecord> answers = new HashSet<DNSRecord>(); - - - if (state == DNSState.ANNOUNCED) - { - try - { - boolean isUnicast = (port != DNSConstants.MDNS_PORT); - - - // Answer questions - for (Iterator<DNSEntry> iterator = in.questions.iterator(); - iterator.hasNext();) - { - DNSEntry entry = iterator.next(); - if (entry instanceof DNSQuestion) - { - DNSQuestion q = (DNSQuestion) entry; - - // for unicast responses the question - // must be included - if (isUnicast) - { - //out.addQuestion(q); - questions.add(q); - } - - int type = q.type; - if (type == DNSConstants.TYPE_ANY || - type == DNSConstants.TYPE_SRV) - { // I ama not sure of why there is a special - // case here [PJYF Oct 15 2004] - if (localHost.getName(). - equalsIgnoreCase(q.getName())) - { - // type = DNSConstants.TYPE_A; - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - type = DNSConstants.TYPE_IGNORE; - } - else - { - if (serviceTypes.containsKey( - q.getName().toLowerCase())) - { - type = DNSConstants.TYPE_PTR; - } - } - } - - switch (type) - { - case DNSConstants.TYPE_A: - { - // Answer a query for a domain name - //out = addAnswer( in, addr, port, out, host ); - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - break; - } - case DNSConstants.TYPE_AAAA: - { - // Answer a query for a domain name - DNSRecord answer = - localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - break; - } - case DNSConstants.TYPE_PTR: - { - // Answer a query for services of a given type - - // find matching services - for (Iterator<ServiceInfo> serviceIterator = - services.values().iterator(); - serviceIterator.hasNext();) - { - ServiceInfo info = serviceIterator.next(); - if (info.getState() == DNSState.ANNOUNCED) - { - if (q.name.equalsIgnoreCase(info.type)) - { - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answer = - localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answers.add( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName())); - answers.add( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName())); - answers.add( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text)); - } - } - } - if (q.name.equalsIgnoreCase("_services._mdns._udp.local.")) - { - for (Iterator<String> serviceTypeIterator = serviceTypes.values().iterator(); - serviceTypeIterator.hasNext();) - { - answers.add( - new DNSRecord.Pointer( - "_services._mdns._udp.local.", - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - serviceTypeIterator.next())); - } - } - break; - } - case DNSConstants.TYPE_SRV: - case DNSConstants.TYPE_ANY: - case DNSConstants.TYPE_TXT: - { - ServiceInfo info = services.get(q.name.toLowerCase()); - if (info != null && - info.getState() == DNSState.ANNOUNCED) - { - DNSRecord answer = - localHost.getDNS4AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answer = - localHost.getDNS6AddressRecord(); - if (answer != null) - { - answers.add(answer); - } - answers.add( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName())); - answers.add( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.priority, - info.weight, - info.port, - localHost.getName())); - answers.add( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text)); - } - break; - } - default : - { - //System.out.println("JmDNSResponder.unhandled query:"+q); - break; - } - } - } - } - - - // remove known answers, if the ttl is at least half of - // the correct value. (See Draft Cheshire chapter 7.1.). - for (DNSRecord knownAnswer : in.answers) - { - if (knownAnswer.ttl > DNSConstants.DNS_TTL / 2 && - answers.remove(knownAnswer)) - { - if (logger.isDebugEnabled()) - logger.debug( - "JmDNS Responder Known Answer Removed"); - } - } - - - // responde if we have answers - if (answers.size() != 0) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS responding"); - DNSOutgoing out = null; - if (isUnicast) - { - out = new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE - | DNSConstants.FLAGS_AA, - false); - } - - for (Iterator<DNSQuestion> i = questions.iterator(); - i.hasNext();) - { - out.addQuestion(i.next()); - } - for (Iterator<DNSRecord> i = answers.iterator(); - i.hasNext();) - { - out = addAnswer(in, addr, port, out, i.next()); - } - send(out); - } - this.cancel(); - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - close(); - } - } - } - } - } - - /** - * Helper class to resolve service types. - * <p/> - * The TypeResolver queries three times consecutively for service types, and then - * removes itself from the timer. - * <p/> - * The TypeResolver will run only if JmDNS is in state ANNOUNCED. - */ - private class TypeResolver extends TimerTask - { - public void start() - { - timer.schedule(this, - DNSConstants.QUERY_WAIT_INTERVAL, - DNSConstants.QUERY_WAIT_INTERVAL); - } - - /** - * Counts the number of queries that were sent. - */ - int count = 0; - - @Override - public void run() - { - try - { - if (state == DNSState.ANNOUNCED) - { - if (count++ < 3) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS querying type"); - DNSOutgoing out = - new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - "_services._mdns._udp.local.", - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN)); - for (String serviceType : serviceTypes.values()) - { - out.addAnswer( - new DNSRecord.Pointer( - "_services._mdns._udp.local.", - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - serviceType), 0); - } - send(out); - } - else - { - // After three queries, we can quit. - this.cancel(); - } - } - else - { - if (state == DNSState.CANCELED) - { - this.cancel(); - } - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * The ServiceResolver queries three times consecutively for services of - * a given type, and then removes itself from the timer. - * <p/> - * The ServiceResolver will run only if JmDNS is in state ANNOUNCED. - * REMIND: Prevent having multiple service resolvers for the same type in the - * timer queue. - */ - private class ServiceResolver extends TimerTask - { - /** - * Counts the number of queries being sent. - */ - int count = 0; - private String type; - - public ServiceResolver(String type) - { - this.type = type; - } - - public void start() - { - timer.schedule(this, - DNSConstants.QUERY_WAIT_INTERVAL, - DNSConstants.QUERY_WAIT_INTERVAL); - } - - @Override - public void run() - { - try - { - if (state == DNSState.ANNOUNCED) - { - if (count++ < 3) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS querying service"); - long now = System.currentTimeMillis(); - DNSOutgoing out = - new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN)); - for (Iterator<ServiceInfo> s = services.values().iterator(); s.hasNext();) - { - final ServiceInfo info = s.next(); - try - { - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - DNSConstants.DNS_TTL, - info.getQualifiedName()), now); - } - catch (IOException ee) - { - break; - } - } - send(out); - } - else - { - // After three queries, we can quit. - this.cancel(); - } - } - else - { - if (state == DNSState.CANCELED) - { - this.cancel(); - } - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * The ServiceInfoResolver queries up to three times consecutively for - * a service info, and then removes itself from the timer. - * <p/> - * The ServiceInfoResolver will run only if JmDNS is in state ANNOUNCED. - * REMIND: Prevent having multiple service resolvers for the same info in the - * timer queue. - */ - private class ServiceInfoResolver extends TimerTask - { - /** - * Counts the number of queries being sent. - */ - int count = 0; - private ServiceInfo info; - - public ServiceInfoResolver(ServiceInfo info) - { - this.info = info; - info.dns = JmDNS.this; - addListener(info, - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - } - - public void start() - { - timer.schedule(this, - DNSConstants.QUERY_WAIT_INTERVAL, - DNSConstants.QUERY_WAIT_INTERVAL); - } - - @Override - public void run() - { - try - { - if (state == DNSState.ANNOUNCED) - { - if (count++ < 3 && !info.hasData()) - { - long now = System.currentTimeMillis(); - DNSOutgoing out = - new DNSOutgoing(DNSConstants.FLAGS_QR_QUERY); - out.addQuestion( - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN)); - out.addQuestion( - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN)); - if (info.server != null) - { - out.addQuestion( - new DNSQuestion( - info.server, - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN)); - } - out.addAnswer((DNSRecord) cache.get( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN), now); - out.addAnswer((DNSRecord) cache.get( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN), now); - if (info.server != null) - { - out.addAnswer((DNSRecord) cache.get( - info.server, - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN), now); - } - send(out); - } - else - { - // After three queries, we can quit. - this.cancel(); - removeListener(info); - } - } - else - { - if (state == DNSState.CANCELED) - { - this.cancel(); - removeListener(info); - } - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * The Canceler sends two announces with TTL=0 for the specified services. - */ - /* TODO: Clarify whether 2 or 3 announces should be sent. The header says 2, - * run() uses the (misleading) ++count < 3 (while all other tasks use count++ < 3) - * and the comment in the else block in run() says: "After three successful..." - */ - public class Canceler extends TimerTask - { - /** - * Counts the number of announces being sent. - */ - int count = 0; - /** - * The services that need cancelling. - * Note: We have to use a local variable here, because the services - * that are canceled, are removed immediately from variable JmDNS.services. - */ - private ServiceInfo[] infos; - /** - * We call notifyAll() on the lock object, when we have canceled the - * service infos. - * This is used by method JmDNS.unregisterService() and - * JmDNS.unregisterAllServices, to ensure that the JmDNS - * socket stays open until the Canceler has canceled all services. - * <p/> - * Note: We need this lock, because ServiceInfos do the transition from - * state ANNOUNCED to state CANCELED before we get here. We could get - * rid of this lock, if we added a state named CANCELLING to DNSState. - */ - private Object lock; - int ttl = 0; - - public Canceler(ServiceInfo info, Object lock) - { - this.infos = new ServiceInfo[]{info}; - this.lock = lock; - addListener(info, - new DNSQuestion( - info.getQualifiedName(), - DNSConstants.TYPE_ANY, - DNSConstants.CLASS_IN)); - } - - public Canceler(ServiceInfo[] infos, Object lock) - { - this.infos = infos; - this.lock = lock; - } - - public Canceler(Collection<ServiceInfo> infos, Object lock) - { - this.infos = infos.toArray(new ServiceInfo[infos.size()]); - this.lock = lock; - } - - public void start() - { - timer.schedule(this, 0, DNSConstants.ANNOUNCE_WAIT_INTERVAL); - } - - @Override - public void run() - { - try - { - if (++count < 3) - { - if (logger.isDebugEnabled()) - logger.debug("run() JmDNS canceling service"); - // announce the service - //long now = System.currentTimeMillis(); - DNSOutgoing out = - new DNSOutgoing( - DNSConstants.FLAGS_QR_RESPONSE | DNSConstants.FLAGS_AA); - for (int i = 0; i < infos.length; i++) - { - ServiceInfo info = infos[i]; - out.addAnswer( - new DNSRecord.Pointer( - info.type, - DNSConstants.TYPE_PTR, - DNSConstants.CLASS_IN, - ttl, - info.getQualifiedName()), 0); - out.addAnswer( - new DNSRecord.Service( - info.getQualifiedName(), - DNSConstants.TYPE_SRV, - DNSConstants.CLASS_IN, - ttl, - info.priority, - info.weight, - info.port, - localHost.getName()), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN, - ttl, - info.text), 0); - DNSRecord answer = localHost.getDNS4AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - answer = localHost.getDNS6AddressRecord(); - if (answer != null) - { - out.addAnswer(answer, 0); - } - } - send(out); - } - else - { - // After three successful announcements, we are finished. - synchronized (lock) - { - closed=true; - lock.notifyAll(); - } - this.cancel(); - } - } - catch (Throwable e) - { - logger.warn( "run() exception ", e); - recover(); - } - } - } - - /** - * Recover jmdns when there is an error. - */ - protected void recover() - { - if (logger.isDebugEnabled()) - logger.debug("recover()"); - // We have an IO error so lets try to recover if anything happens lets close it. - // This should cover the case of the IP address changing under our feet - if (DNSState.CANCELED != state) - { - synchronized (this) - { // Synchronize only if we are not already in process to prevent dead locks - // - if (logger.isDebugEnabled()) - logger.debug("recover() Cleanning up"); - // Stop JmDNS - state = DNSState.CANCELED; // This protects against recursive calls - - // We need to keep a copy for reregistration - Collection<ServiceInfo> oldServiceInfos = new ArrayList<ServiceInfo>(services.values()); - - // Cancel all services - unregisterAllServices(); - disposeServiceCollectors(); - // - // close multicast socket - closeMulticastSocket(); - // - cache.clear(); - if (logger.isDebugEnabled()) - logger.debug("recover() All is clean"); - // - // All is clear now start the services - // - try - { - openMulticastSocket(localHost); - start(oldServiceInfos); - } - catch (Exception exception) - { - logger.warn( - "recover() Start services exception ", exception); - } - logger.warn( "recover() We are back!"); - } - } - } - - /** - * Close down jmdns. Release all resources and unregister all services. - */ - public void close() - { - if (state != DNSState.CANCELED) - { - synchronized (this) - { // Synchronize only if we are not already in process to prevent dead locks - // Stop JmDNS - state = DNSState.CANCELED; // This protects against recursive calls - - unregisterAllServices(); - disposeServiceCollectors(); - - // close socket - closeMulticastSocket(); - - // Stop the timer - timer.cancel(); - } - } - } - - /** - * List cache entries, for debugging only. - */ - void print() - { - if (logger.isInfoEnabled()) - logger.info("---- cache ----\n"); - cache.print(); - if (logger.isInfoEnabled()) - logger.info("\n"); - } - - /** - * List Services and serviceTypes. - * Debugging Only - */ - - public void printServices() - { - if (logger.isInfoEnabled()) - logger.info(toString()); - } - - @Override - public String toString() - { - StringBuffer aLog = new StringBuffer(); - aLog.append("\t---- Services -----"); - if (services != null) - { - for (Map.Entry<String, ServiceInfo> entry : services.entrySet()) - { - aLog.append("\n\t\tService: " + entry.getKey() + ": " - + entry.getValue()); - } - } - aLog.append("\n"); - aLog.append("\t---- Types ----"); - if (serviceTypes != null) - { - for (Map.Entry<String, String> entry : serviceTypes.entrySet()) - { - aLog.append("\n\t\tType: " + entry.getKey() + ": " - + entry.getValue()); - } - } - aLog.append("\n"); - aLog.append(cache.toString()); - aLog.append("\n"); - aLog.append("\t---- Service Collectors ----"); - if (serviceCollectors != null) - { - synchronized (serviceCollectors) - { - for (Map.Entry<String, ServiceCollector> entry - : serviceCollectors.entrySet()) - { - aLog.append("\n\t\tService Collector: " + entry.getKey() - + ": " + entry.getValue()); - } - serviceCollectors.clear(); - } - } - return aLog.toString(); - } - - /** - * Returns a list of service infos of the specified type. - * - * @param type Service type name, such as <code>_http._tcp.local.</code>. - * @return An array of service instance names. - */ - public ServiceInfo[] list(String type) - { - // Implementation note: The first time a list for a given type is - // requested, a ServiceCollector is created which collects service - // infos. This greatly speeds up the performance of subsequent calls - // to this method. The caveats are, that 1) the first call to this method - // for a given type is slow, and 2) we spawn a ServiceCollector - // instance for each service type which increases network traffic a - // little. - - ServiceCollector collector; - - boolean newCollectorCreated; - synchronized (serviceCollectors) - { - collector = serviceCollectors.get(type); - if (collector == null) - { - collector = new ServiceCollector(type); - serviceCollectors.put(type, collector); - addServiceListener(type, collector); - newCollectorCreated = true; - } - else - { - newCollectorCreated = false; - } - } - - // After creating a new ServiceCollector, we collect service infos for - // 200 milliseconds. This should be enough time, to get some service - // infos from the network. - if (newCollectorCreated) - { - try - { - Thread.sleep(200); - } - catch (InterruptedException e) - { - } - } - - return collector.list(); - } - - /** - * This method disposes all ServiceCollector instances which have been - * created by calls to method <code>list(type)</code>. - * - * @see #list - */ - private void disposeServiceCollectors() - { - if (logger.isDebugEnabled()) - logger.debug("disposeServiceCollectors()"); - synchronized (serviceCollectors) - { - for (Iterator<ServiceCollector> i = serviceCollectors.values().iterator(); i.hasNext();) - { - ServiceCollector collector = i.next(); - removeServiceListener(collector.type, collector); - } - serviceCollectors.clear(); - } - } - - /** - * Instances of ServiceCollector are used internally to speed up the - * performance of method <code>list(type)</code>. - * - * @see #list - */ - private static class ServiceCollector implements ServiceListener - { - - /** - * A set of collected service instance names. - */ - private Map<String, ServiceInfo> infos = Collections.synchronizedMap(new HashMap<String, ServiceInfo>()); - - public String type; - - public ServiceCollector(String type) - { - this.type = type; - } - - /** - * A service has been added. - */ - public void serviceAdded(ServiceEvent event) - { - synchronized (infos) - { - event.getDNS().requestServiceInfo( - event.getType(), event.getName(), 0); - } - } - - /** - * A service has been removed. - */ - public void serviceRemoved(ServiceEvent event) - { - synchronized (infos) - { - infos.remove(event.getName()); - } - } - - /** - * A service hase been resolved. Its details are now available in the - * ServiceInfo record. - */ - public void serviceResolved(ServiceEvent event) - { - synchronized (infos) - { - infos.put(event.getName(), event.getInfo()); - } - } - - /** - * Returns an array of all service infos which have been collected by this - * ServiceCollector. - * @return - */ - public ServiceInfo[] list() - { - synchronized (infos) - { - return infos.values(). - toArray(new ServiceInfo[infos.size()]); - } - } - - @Override - public String toString() - { - StringBuffer aLog = new StringBuffer(); - synchronized (infos) - { - for (Map.Entry<String, ServiceInfo> entry : infos.entrySet()) - { - aLog.append("\n\t\tService: " + entry.getKey() + ": " - + entry.getValue()); - } - } - return aLog.toString(); - } - }; - - private static String toUnqualifiedName(String type, String qualifiedName) - { - if (qualifiedName.endsWith(type)) - { - return qualifiedName.substring(0, - qualifiedName.length() - type.length() - 1); - } - else - { - return qualifiedName; - } - } - - /** - * SC-Bonjour Implementation : Method used to update the corresponding DNS - * entry in the cache of JmDNS with the new information in this ServiceInfo. - * A call to getLocalService must first be issued to get the - * ServiceInfo object to be modified. - * THIS METHOD MUST BE USED INSTEAD OF ANY DIRECT ACCESS TO JMDNS' CACHE!! - * This is used in the implementation of Zeroconf in SIP Communicator - * to be able to change fields declared by the local contact (status, etc). - * @param info Updated service data to be used to replace the old - * stuff contained in JmDNS' cache - * @param old info bytes - */ - public void updateInfos(ServiceInfo info, byte[] old) - { - - DNSOutgoing out, out2; - synchronized (JmDNS.this) - { - //list = new ArrayList(services.values()); - services.put(info.getQualifiedName().toLowerCase(), info); - } - - synchronized (info) - { - if (logger.isDebugEnabled()) - logger.debug("updateInfos() JmDNS updating " + - info.getQualifiedName() + " state " + - info.getState()); - - out = new DNSOutgoing( - /*DNSConstants.FLAGS_QR_RESPONSE*/ - DNSConstants.FLAGS_RA | DNSConstants.FLAGS_AA); - out2 = new DNSOutgoing( - /*DNSConstants.FLAGS_QR_RESPONSE*/ - DNSConstants.FLAGS_RA | DNSConstants.FLAGS_AA); - - - try - { - //out.addAnswer(new DNSRecord.Pointer(info.type, DNSConstants.TYPE_PTR, DNSConstants.CLASS_IN, DNSConstants.DNS_TTL, info.getQualifiedName()), 0); - //out.addAnswer(new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_A, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.priority, info.weight, info.port, localHost.getName()), 0); - //out.addAnswer(new DNSRecord.Service(info.getQualifiedName(), DNSConstants.TYPE_SRV, DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, DNSConstants.DNS_TTL, info.priority, info.weight, info.port, localHost.getName()), 0); -// out.addAnswer( -// new DNSRecord.Text( -// info.getQualifiedName(), -// DNSConstants.TYPE_TXT, -// DNSConstants.CLASS_IN , -// DNSConstants.DNS_TTL, -// info.text), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN , - 0, - old), 0); - out.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text), 0); - - out2.addAnswer( - new DNSRecord.Text( - info.getQualifiedName(), - DNSConstants.TYPE_TXT, - DNSConstants.CLASS_IN | DNSConstants.CLASS_UNIQUE, - DNSConstants.DNS_TTL, - info.text), 0); - - if (logger.isDebugEnabled()) - logger.debug("updateInfos() JmDNS updated infos for "+info); - - send(out); - Thread.sleep(1000); - send(out2); - Thread.sleep(2000); - send(out2); - } - catch( Exception e) - { - logger.warn( "", e); - } - } - } - - - /** - * SC-Bonjour Implementation: Method to retrieve the DNS Entry corresponding to a service - * that has been declared and return it as a ServiceInfo structure. - * It is used in the implementation of Bonjour in SIP Communicator to retrieve the information - * concerning the service declared by the local contact. THIS METHOD MUST BE USED INSTEAD OF ANY - * LOCAL COPY SAVED BEFORE SERVICE REGISTRATION!! - * @return information corresponding to the specified service - * @param FQN String representing the Fully Qualified name of the service we want info about - */ - public ServiceInfo getLocalService(String FQN) - { - return services.get(FQN); - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java deleted file mode 100644 index ff922d1..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceEvent.java +++ /dev/null @@ -1,124 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; -import java.util.logging.*; - -/** - * ServiceEvent. - * - * @author Werner Randelshofer, Rick Blair - * @version %I%, %G% - */ -public class ServiceEvent - extends EventObject -{ - private static Logger logger = - Logger.getLogger(ServiceEvent.class.toString()); - /** - * The type name of the service. - */ - private String type; - /** - * The instance name of the service. Or null, if the event was - * fired to a service type listener. - */ - private String name; - /** - * The service info record, or null if the service could be be resolved. - * This is also null, if the event was fired to a service type listener. - */ - private ServiceInfo info; - - /** - * Creates a new instance. - * - * @param source the JmDNS instance which originated the event. - * @param type the type name of the service. - * @param name the instance name of the service. - * @param info the service info record, or null if the - * service could be be resolved. - */ - public ServiceEvent(JmDNS source, String type, String name, ServiceInfo info) - { - super(source); - this.type = type; - this.name = name; - this.info = info; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Returns the JmDNS instance which originated the event. - * @return Returns the JmDNS instance which originated the event. - */ - public JmDNS getDNS() - { - return (JmDNS) getSource(); - } - - /** - * Returns the fully qualified type of the service. - * @return Returns the fully qualified type of the service. - */ - public String getType() - { - return type; - } - - /** - * Returns the instance name of the service. - * Always returns null, if the event is sent to a service type listener. - * @return Returns the instance name of the service. - */ - public String getName() - { - return name; - } - - /** - * Returns the service info record, or null if the service could not be - * resolved. - * Always returns null, if the event is sent to a service type listener. - * @return Returns the service info record. - */ - public ServiceInfo getInfo() - { - return info; - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append("<" + getClass().getName() + "> "); - buf.append(super.toString()); - buf.append(" name "); - buf.append(getName()); - buf.append(" type "); - buf.append(getType()); - buf.append(" info "); - buf.append(getInfo()); - return buf.toString(); - } - -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java deleted file mode 100644 index b7f7f2d..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceInfo.java +++ /dev/null @@ -1,785 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.io.*; -import java.net.*; -import java.util.*; -import java.util.logging.*; - -/** - * JmDNS service information. - * - * @version %I%, %G% - * @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer - * @author Christian Vincenot - */ -public class ServiceInfo implements DNSListener -{ - private static Logger logger = - Logger.getLogger(ServiceInfo.class.toString()); - public final static byte[] NO_VALUE = new byte[0]; - JmDNS dns; - - // State machine - /** - * The state of this service info. - * This is used only for services announced by JmDNS. - * <p/> - * For proper handling of concurrency, this variable must be - * changed only using methods advanceState(), revertState() and cancel(). - */ - private DNSState state = DNSState.PROBING_1; - - /** - * Task associated to this service info. - * Possible tasks are JmDNS.Prober, JmDNS.Announcer, JmDNS.Responder, - * JmDNS.Canceler. - */ - TimerTask task; - - String type; - private String name; - String server; - int port; - int weight; - int priority; - byte text[]; - private Map<String, Object> props; - InetAddress addr; - - - /** - * Construct a service description for registrating with JmDNS. - * - * @param type fully qualified service type name, - * such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, - * such as <code>foobar</code> - * @param port the local port on which the service runs - * @param text string describing the service - */ - public ServiceInfo(String type, String name, int port, String text) - { - this(type, name, port, 0, 0, text); - } - - /** - * Construct a service description for registrating with JmDNS. - * - * @param type fully qualified service type name, - * such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, - * such as <code>foobar</code> - * @param port the local port on which the service runs - * @param weight weight of the service - * @param priority priority of the service - * @param text string describing the service - */ - public ServiceInfo(String type, String name, - int port, int weight, - int priority, String text) - { - this(type, name, port, weight, priority, (byte[]) null); - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(text.length()); - writeUTF(out, text); - this.text = out.toByteArray(); - } - catch (IOException e) - { - throw new RuntimeException("unexpected exception: " + e); - } - } - - /** - * Construct a service description for registrating with JmDNS. The properties hashtable must - * map property names to either Strings or byte arrays describing the property values. - * - * @param type fully qualified service type name, such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, such as <code>foobar</code> - * @param port the local port on which the service runs - * @param weight weight of the service - * @param priority priority of the service - * @param props properties describing the service - */ - public ServiceInfo(String type, String name, - int port, int weight, - int priority, Map<String, Object> props) - { - this(type, name, port, weight, priority, new byte[0]); - if (props != null) - { - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(256); - for (Map.Entry<String, Object> prop : props.entrySet()) - { - String key = prop.getKey(); - Object val = prop.getValue(); - ByteArrayOutputStream out2 = new ByteArrayOutputStream(100); - writeUTF(out2, key); - if (val instanceof String) - { - out2.write('='); - writeUTF(out2, (String) val); - } - else - { - if (val instanceof byte[]) - { - out2.write('='); - byte[] bval = (byte[]) val; - out2.write(bval, 0, bval.length); - } - else - { - if (val != NO_VALUE) - { - throw new IllegalArgumentException( - "invalid property value: " + val); - } - } - } - byte data[] = out2.toByteArray(); - out.write(data.length); - out.write(data, 0, data.length); - } - this.text = out.toByteArray(); - } - catch (IOException e) - { - throw new RuntimeException("unexpected exception: " + e); - } - } - } - - /** - * Construct a service description for registrating with JmDNS. - * - * @param type fully qualified service type name, - * such as <code>_http._tcp.local.</code>. - * @param name unqualified service instance name, - * such as <code>foobar</code> - * @param port the local port on which the service runs - * @param weight weight of the service - * @param priority priority of the service - * @param text bytes describing the service - */ - public ServiceInfo(String type, String name, - int port, int weight, - int priority, byte text[]) - { - this.type = type; - this.name = name; - this.port = port; - this.weight = weight; - this.priority = priority; - this.text = text; - - String SLevel = System.getProperty("jmdns.debug"); - if (SLevel == null) SLevel = "INFO"; - logger.setLevel(Level.parse(SLevel)); - } - - /** - * Construct a service record during service discovery. - */ - ServiceInfo(String type, String name) - { - if (!type.endsWith(".")) - { - throw new IllegalArgumentException( - "type must be fully qualified DNS name ending in '.': " + type); - } - - this.type = type; - this.name = name; - } - - /** - * During recovery we need to duplicate service info to reregister them - */ - ServiceInfo(ServiceInfo info) - { - if (info != null) - { - this.type = info.type; - this.name = info.name; - this.port = info.port; - this.weight = info.weight; - this.priority = info.priority; - this.text = info.text; - } - } - - /** - * Fully qualified service type name, - * such as <code>_http._tcp.local.</code> . - * @return Returns fully qualified service type name. - */ - public String getType() - { - return type; - } - - /** - * Unqualified service instance name, - * such as <code>foobar</code> . - * @return Returns unqualified service instance name. - */ - public String getName() - { - return name; - } - - /** - * Sets the service instance name. - * - * @param name unqualified service instance name, - * such as <code>foobar</code> - */ - void setName(String name) - { - this.name = name; - } - - /** - * Fully qualified service name, - * such as <code>foobar._http._tcp.local.</code> . - * @return Returns fully qualified service name. - */ - public String getQualifiedName() - { - return name + "." + type; - } - - /** - * Get the name of the server. - * @return Returns name of the server. - */ - public String getServer() - { - return server; - } - - /** - * Get the host address of the service (ie X.X.X.X). - * @return Returns host address of the service. - */ - public String getHostAddress() - { - return (addr != null ? addr.getHostAddress() : ""); - } - - public InetAddress getAddress() - { - return addr; - } - - /** - * Get the InetAddress of the service. - * @return Returns the InetAddress of the service. - */ - public InetAddress getInetAddress() - { - return addr; - } - - /** - * Get the port for the service. - * @return Returns port for the service. - */ - public int getPort() - { - return port; - } - - /** - * Get the priority of the service. - * @return Returns the priority of the service. - */ - public int getPriority() - { - return priority; - } - - /** - * Get the weight of the service. - * @return Returns the weight of the service. - */ - public int getWeight() - { - return weight; - } - - /** - * Get the text for the serivce as raw bytes. - * @return Returns the text for the serivce as raw bytes. - */ - public byte[] getTextBytes() - { - return text; - } - - /** - * Get the text for the service. This will interpret the text bytes - * as a UTF8 encoded string. Will return null if the bytes are not - * a valid UTF8 encoded string. - * @return Returns the text for the service. - */ - public String getTextString() - { - if ((text == null) || - (text.length == 0) || - ((text.length == 1) && (text[0] == 0))) - { - return null; - } - return readUTF(text, 0, text.length); - } - - /** - * Get the URL for this service. An http URL is created by - * combining the address, port, and path properties. - * @return Returns the URL for this service. - */ - public String getURL() - { - return getURL("http"); - } - - /** - * Get the URL for this service. An URL is created by - * combining the protocol, address, port, and path properties. - * @param protocol - * @return Returns URL for this service. - */ - public String getURL(String protocol) - { - String url = protocol + "://" + getHostAddress() + ":" + getPort(); - String path = getPropertyString("path"); - if (path != null) - { - if (path.indexOf("://") >= 0) - { - url = path; - } - else - { - url += path.startsWith("/") ? path : "/" + path; - } - } - return url; - } - - /** - * Get a property of the service. This involves decoding the - * text bytes into a property list. Returns null if the property - * is not found or the text data could not be decoded correctly. - * @param name - * @return Returns property of the service as bytes. - */ - public synchronized byte[] getPropertyBytes(String name) - { - return (byte[]) getProperties().get(name); - } - - /** - * Get a property of the service. This involves decoding the - * text bytes into a property list. Returns null if the property - * is not found, the text data could not be decoded correctly, or - * the resulting bytes are not a valid UTF8 string. - * @param name - * @return Returns property of the service as string. - */ - public synchronized String getPropertyString(String name) - { - byte data[] = (byte[]) getProperties().get(name); - - if (data == null) - { - return null; - } - if (data == NO_VALUE) - { - return "true"; - } - String res = readUTF(data, 0, data.length); - - return res; - } - - /** - * Iterator<String> of the property names. - * @return Iterator<String> of the property names. - */ - public Iterator<String> getPropertyNames() - { - Map<String, Object> properties = getProperties(); - Iterable<String> propertyNames - = (properties != null) ? properties.keySet() : new Vector<String>(); - return propertyNames.iterator(); - } - - /** - * Write a UTF string with a length to a stream. - */ - void writeUTF(OutputStream out, String str) throws IOException - { - for (int i = 0, len = str.length(); i < len; i++) - { - int c = str.charAt(i); - if ((c >= 0x0001) && (c <= 0x007F)) - { - out.write(c); - } - else - { - if (c > 0x07FF) - { - out.write(0xE0 | ((c >> 12) & 0x0F)); - out.write(0x80 | ((c >> 6) & 0x3F)); - out.write(0x80 | ((c >> 0) & 0x3F)); - } - else - { - out.write(0xC0 | ((c >> 6) & 0x1F)); - out.write(0x80 | ((c >> 0) & 0x3F)); - } - } - } - } - - /** - * Read data bytes as a UTF stream. - */ - String readUTF(byte data[], int off, int len) - { - StringBuffer buf = new StringBuffer(); - for (int end = off + len; off < end;) - { - int ch = data[off++] & 0xFF; - switch (ch >> 4) - { - case 0: - case 1: - case 2: - case 3: - case 4: - case 5: - case 6: - case 7: - // 0xxxxxxx - break; - case 12: - case 13: - if (off >= len) - { - return null; - } - // 110x xxxx 10xx xxxx - ch = ((ch & 0x1F) << 6) | (data[off++] & 0x3F); - break; - case 14: - if (off + 2 >= len) - { - return null; - } - // 1110 xxxx 10xx xxxx 10xx xxxx - ch = ((ch & 0x0f) << 12) | - ((data[off++] & 0x3F) << 6) | - (data[off++] & 0x3F); - break; - default: - if (off + 1 >= len) - { - return null; - } - // 10xx xxxx, 1111 xxxx - ch = ((ch & 0x3F) << 4) | (data[off++] & 0x0f); - break; - } - buf.append((char) ch); - } - return buf.toString(); - } - - synchronized Map<String, Object> getProperties() - { - if ((props == null) && (text != null)) - { - Map<String, Object> props = new Hashtable<String, Object>(); - int off = 0; - while (off < text.length) - { - // length of the next key value pair - int len = text[off++] & 0xFF; - if ((len == 0) || (off + len > text.length)) - { - props.clear(); - break; - } - // look for the '=' - int i = 0; - for (; (i < len) && (text[off + i] != '='); i++) - { - ; - } - - // get the property name - String name = readUTF(text, off, i); - if (name == null) - { - props.clear(); - break; - } - if (i == len) - { - props.put(name, NO_VALUE); - } - else - { - byte value[] = new byte[len - ++i]; - System.arraycopy(text, off + i, value, 0, len - i); - props.put(name, value); - off += len; - } - } - this.props = props; - } - return props; - } - - - /** - * JmDNS callback to update a DNS record. - * @param rec - */ - public void updateRecord(JmDNS jmdns, long now, DNSRecord rec) - { - if ((rec != null) && !rec.isExpired(now)) - { - switch (rec.type) - { - case DNSConstants.TYPE_A: // IPv4 - case DNSConstants.TYPE_AAAA: // IPv6 FIXME [PJYF Oct 14 2004] This has not been tested - if (rec.name.equals(server)) - { - addr = ((DNSRecord.Address) rec).getAddress(); - - } - break; - case DNSConstants.TYPE_SRV: - if (rec.name.equals(getQualifiedName())) - { - DNSRecord.Service srv = (DNSRecord.Service) rec; - server = srv.server; - port = srv.port; - weight = srv.weight; - priority = srv.priority; - addr = null; - // changed to use getCache() instead - jeffs - // updateRecord(jmdns, now, (DNSRecord)jmdns.cache.get(server, TYPE_A, CLASS_IN)); - updateRecord(jmdns, - now, - (DNSRecord) jmdns.getCache().get( - server, - DNSConstants.TYPE_A, - DNSConstants.CLASS_IN)); - } - break; - case DNSConstants.TYPE_TXT: - if (rec.name.equals(getQualifiedName())) - { - DNSRecord.Text txt = (DNSRecord.Text) rec; - text = txt.text; - } - break; - } - // Future Design Pattern - // This is done, to notify the wait loop in method - // JmDNS.getServiceInfo(type, name, timeout); - if (hasData() && dns != null) - { - dns.handleServiceResolved(this); - dns = null; - } - synchronized (this) - { - notifyAll(); - } - } - } - - /** - * Returns true if the service info is filled with data. - */ - boolean hasData() - { - return server != null && addr != null && text != null; - } - - - // State machine - /** - * Sets the state and notifies all objects that wait on the ServiceInfo. - */ - synchronized void advanceState() - { - state = state.advance(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on the ServiceInfo. - */ - synchronized void revertState() - { - state = state.revert(); - notifyAll(); - } - - /** - * Sets the state and notifies all objects that wait on the ServiceInfo. - */ - synchronized void cancel() - { - state = DNSState.CANCELED; - notifyAll(); - } - - /** - * Returns the current state of this info. - */ - DNSState getState() - { - return state; - } - - - @Override - public int hashCode() - { - return getQualifiedName().hashCode(); - } - - @Override - public boolean equals(Object obj) - { - return (obj instanceof ServiceInfo) && - getQualifiedName().equals(((ServiceInfo) obj).getQualifiedName()); - } - - public String getNiceTextString() - { - StringBuffer buf = new StringBuffer(); - for (int i = 0, len = text.length; i < len; i++) - { - if (i >= 20) - { - buf.append("..."); - break; - } - int ch = text[i] & 0xFF; - if ((ch < ' ') || (ch > 127)) - { - buf.append("\\0"); - buf.append(Integer.toString(ch, 8)); - } - else - { - buf.append((char) ch); - } - } - return buf.toString(); - } - - @Override - public String toString() - { - StringBuffer buf = new StringBuffer(); - buf.append("service["); - buf.append(getQualifiedName()); - buf.append(','); - buf.append(getAddress()); - buf.append(':'); - buf.append(port); - buf.append(','); - buf.append(getNiceTextString()); - buf.append(']'); - return buf.toString(); - } - - /** - * SC-Bonjour Implementation: Method used to set the properties of an existing ServiceInfo. - * This is used in the implementation of Bonjour in SIP Communicator to be able to replace - * old properties of the service we've declared to announce the local user with new properties - * (for example in case of a status change). - * @param props Hashtable containing all the new properties to set - */ - public void setProps(Map<String, Object> props) - { - if (props != null) - { - try - { - ByteArrayOutputStream out = new ByteArrayOutputStream(256); - for (Map.Entry<String, Object> prop : props.entrySet()) - { - String key = prop.getKey(); - Object val = prop.getValue(); - - ByteArrayOutputStream out2 = new ByteArrayOutputStream(100); - writeUTF(out2, key); - if (val instanceof String) - { - out2.write('='); - writeUTF(out2, (String) val); - } - else - { - if (val instanceof byte[]) - { - out2.write('='); - byte[] bval = (byte[]) val; - out2.write(bval, 0, bval.length); - } - else - { - if (val != NO_VALUE) - { - throw new IllegalArgumentException( - "invalid property value: " + val); - } - } - } - byte data[] = out2.toByteArray(); - out.write(data.length); - out.write(data, 0, data.length); - } - this.text = out.toByteArray(); - } - catch (IOException e) - { - throw new RuntimeException("unexpected exception: " + e); - } - } - } -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java deleted file mode 100644 index 1c34adf..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceListener.java +++ /dev/null @@ -1,57 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; - -/** - * Listener for service updates. - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer - */ -public interface ServiceListener extends EventListener -{ - /** - * A service has been added. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - - void serviceAdded(ServiceEvent event); - - /** - * A service has been removed. - * - * @param event The ServiceEvent providing the name and fully qualified type - * of the service. - */ - void serviceRemoved(ServiceEvent event); - - /** - * A service has been resolved. Its details are now available in the - * ServiceInfo record. - * - * @param event The ServiceEvent providing the name, the fully qualified - * type of the service, and the service info record, - * or null if the service could not be resolved. - */ - - void serviceResolved(ServiceEvent event); -} diff --git a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java b/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java deleted file mode 100644 index 84e5c59..0000000 --- a/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/ServiceTypeListener.java +++ /dev/null @@ -1,37 +0,0 @@ -/* - * Jitsi, the OpenSource Java VoIP and Instant Messaging client. - * - * Copyright 2003-2005 Arthur van Hoff Rick Blair - * - * Licensed under the Apache License, Version 2.0 (the "License"); - * you may not use this file except in compliance with the License. - * You may obtain a copy of the License at - * - * http://www.apache.org/licenses/LICENSE-2.0 - * - * Unless required by applicable law or agreed to in writing, software - * distributed under the License is distributed on an "AS IS" BASIS, - * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. - * See the License for the specific language governing permissions and - * limitations under the License. - */ -package net.java.sip.communicator.impl.protocol.zeroconf.jmdns; - -import java.util.*; - -/** - * Listener for service types. - * - * @version %I%, %G% - * @author Arthur van Hoff, Werner Randelshofer - */ -public interface ServiceTypeListener extends EventListener -{ - /** - * A new service type was discovered. - * - * @param event The service event providing the fully qualified type of - * the service. - */ - void serviceTypeAdded(ServiceEvent event); -} |