AMQ-4056: Upgraded jmDNS to 3.4.1, and removed old obsolte activemq-jmdns module that is no longer needed.

git-svn-id: https://svn.apache.org/repos/asf/activemq/trunk@1387052 13f79535-47bb-0310-9956-ffa450edef68
This commit is contained in:
Claus Ibsen 2012-09-18 08:44:42 +00:00
parent 1802116df8
commit 9a32e38627
25 changed files with 30 additions and 5716 deletions

View File

@ -38,6 +38,7 @@
javax.annotation*;resolution:=optional,
javax.transaction*;resolution:=optional,
javax.security*;resolution:=optional,
javax.jmdns*;resolution:=optional,
org.codehaus.jettison*;resolution:=optional,
org.apache.activeio*;resolution:=optional,
org.apache.camel*;resolution:=optional,
@ -219,8 +220,8 @@
<scope>test</scope>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-jmdns_1.0</artifactId>
<groupId>javax.jmdns</groupId>
<artifactId>jmdns</artifactId>
<optional>true</optional>
</dependency>
<dependency>

View File

@ -21,7 +21,7 @@ import java.net.InetAddress;
import java.util.HashMap;
import java.util.Map;
import java.util.concurrent.atomic.AtomicInteger;
import org.apache.activemq.jmdns.JmDNS;
import javax.jmdns.JmDNS;
public final class JmDNSFactory {
@ -39,20 +39,14 @@ public final class JmDNSFactory {
UsageTracker tracker = registry.get(address);
if (tracker == null) {
tracker = new UsageTracker();
tracker.jmDNS = new JmDNS(address) {
public void close() {
if (onClose(address)) {
super.close();
}
}
};
tracker.jmDNS = JmDNS.create(address);
registry.put(address, tracker);
}
tracker.count.incrementAndGet();
return tracker.jmDNS;
}
static synchronized boolean onClose(InetAddress address) {
static synchronized boolean onClose(InetAddress address, JmDNS dns) {
UsageTracker tracker = registry.get(address);
if (tracker != null) {
if (tracker.count.decrementAndGet() == 0) {

View File

@ -23,11 +23,10 @@ import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;
import java.util.concurrent.CopyOnWriteArrayList;
import org.apache.activemq.jmdns.JmDNS;
import org.apache.activemq.jmdns.ServiceEvent;
import org.apache.activemq.jmdns.ServiceInfo;
import org.apache.activemq.jmdns.ServiceListener;
import javax.jmdns.JmDNS;
import javax.jmdns.ServiceEvent;
import javax.jmdns.ServiceInfo;
import javax.jmdns.ServiceListener;
import org.apache.activemq.command.DiscoveryEvent;
import org.apache.activemq.transport.discovery.DiscoveryAgent;
@ -46,7 +45,7 @@ import org.slf4j.LoggerFactory;
public class ZeroconfDiscoveryAgent implements DiscoveryAgent, ServiceListener {
private static final Logger LOG = LoggerFactory.getLogger(ZeroconfDiscoveryAgent.class);
private static final String TYPE_SUFFIX = "ActiveMQ-4.";
private static final String TYPE_SUFFIX = "ActiveMQ-5.";
private JmDNS jmdns;
private InetAddress localAddress;
@ -92,7 +91,11 @@ public class ZeroconfDiscoveryAgent implements DiscoveryAgent, ServiceListener {
final JmDNS closeTarget = jmdns;
Thread thread = new Thread() {
public void run() {
closeTarget.close();
try {
JmDNSFactory.onClose(getLocalAddress(), closeTarget);
} catch (IOException e) {
LOG.debug("Error closing JmDNS " + getLocalhost() + ". This exception will be ignored.", e);
}
}
};
@ -200,7 +203,7 @@ public class ZeroconfDiscoveryAgent implements DiscoveryAgent, ServiceListener {
if (LOG.isDebugEnabled()) {
LOG.debug("Registering service type: " + type + " name: " + name + " details: " + map);
}
return new ServiceInfo(type, name + "." + type, port, weight, priority, "");
return ServiceInfo.create(type, name + "." + type, port, weight, priority, "");
}
protected JmDNS createJmDNS() throws IOException {

View File

@ -1,46 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You 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.
-->
<project xmlns="http://maven.apache.org/POM/4.0.0" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/maven-v4_0_0.xsd">
<parent>
<artifactId>activemq-parent</artifactId>
<groupId>org.apache.activemq</groupId>
<version>5.7-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>activemq-jmdns_1.0</artifactId>
<name>ActiveMQ :: jmdns 1.0</name>
<description>
The activemq-jmdns_1.0 source is derived from http://repo1.maven.org/maven2/jmdns/jmdns/1.0/jmdns-1.0-sources.jar
Changes to apache activemq version:
- renamed package javax.jmdns to org.apache.activemq.jmdns
- removed classes with lgpl source headers, leaving only the org.apache.activemq.jmdns package.
</description>
<build>
<plugins>
<plugin>
<artifactId>maven-surefire-plugin</artifactId>
<configuration>
<skip>true</skip>
</configuration>
</plugin>
</plugins>
</build>
</project>

View File

@ -1,270 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.logging.Logger;
/**
* 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 hashtable;
/**
* Cache nodes are used to implement storage of multiple DNSEntry's of the
* same name in the cache.
*/
public static class CacheNode
{
private static Logger logger = Logger.getLogger(CacheNode.class.toString());
private DNSEntry value;
private CacheNode next;
public CacheNode(DNSEntry value)
{
this.value = value;
}
public CacheNode next()
{
return next;
}
public DNSEntry getValue()
{
return value;
}
}
/**
* Create a table with a given initial size.
*/
public DNSCache(final int size)
{
hashtable = new HashMap(size);
}
/**
* Clears the cache.
*/
public synchronized void clear()
{
hashtable.clear();
size = 0;
}
/**
* Adds an entry to the table.
*/
public synchronized void add(final DNSEntry entry)
{
//logger.log("DNSCache.add("+entry.getName()+")");
CacheNode newValue = new CacheNode(entry);
CacheNode node = (CacheNode) 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. Returns true if the
* entry was found.
*/
public synchronized boolean remove(DNSEntry entry)
{
CacheNode node = (CacheNode) 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).
* 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.
*/
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.
*/
public Iterator iterator()
{
return Collections.unmodifiableCollection(hashtable.values()).iterator();
}
/**
* Iterate only over items with matching name.
* Returns an instance of DNSCache.CacheNode or null.
* 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.
*/
public synchronized CacheNode find(String name)
{
return (CacheNode) hashtable.get(name);
}
/**
* List all entries for debugging.
*/
public synchronized void print()
{
for (Iterator i = iterator(); i.hasNext();)
{
for (CacheNode n = (CacheNode) i.next(); n != null; n = n.next)
{
System.out.println(n.value);
}
}
}
public synchronized String toString()
{
StringBuffer aLog = new StringBuffer();
aLog.append("\t---- cache ----");
for (Iterator i = iterator(); i.hasNext();)
{
for (CacheNode n = (CacheNode) i.next(); n != null; n = n.next)
{
aLog.append("\n\t\t" + n.value);
}
}
return aLog.toString();
}
}

View File

@ -1,138 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
/**
* DNS constants.
*
* @version %I%, %G%
* @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer, Pierre Frisch, Rick Blair
*/
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;
final static int DNS_TTL = 60 * 60; // default one hour TTL
// final static int DNS_TTL = 120 * 60; // two hour TTL (draft-cheshire-dnsext-multicastdns.txt ch 13)
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
final static int FLAGS_AA = 0x0400; // Authorative answer
final static int FLAGS_TC = 0x0200; // Truncated
final static int FLAGS_RD = 0x0100; // Recursion desired
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 int CLASS_IN = 1; // Final Static Internet
final static int CLASS_CS = 2; // CSNET
final static int CLASS_CH = 3; // CHAOS
final static int CLASS_HS = 4; // Hesiod
final static int CLASS_NONE = 254; // Used in DNS UPDATE [RFC 2136]
final static int CLASS_ANY = 255; // Not a DNS class, but a DNS query class, meaning "all classes"
final static int CLASS_MASK = 0x7FFF; // Multicast DNS uses the bottom 15 bits to identify the record class...
final static int CLASS_UNIQUE = 0x8000; // ... and the top bit indicates that all other cached records are now invalid
final static int TYPE_IGNORE = 0; // This is a hack to stop further processing
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
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]
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
final static int SHARED_QUERY_TIME = 20; //milliseconds before send shared query
final static int QUERY_WAIT_INTERVAL = 225; //milliseconds between query loops.
final static int PROBE_WAIT_INTERVAL = 250; //milliseconds between probe loops.
final static int RESPONSE_MIN_WAIT_INTERVAL = 20; //minimal wait interval for response.
final static int RESPONSE_MAX_WAIT_INTERVAL = 115; //maximal wait interval for response
final static int PROBE_CONFLICT_INTERVAL = 1000; //milliseconds to wait after conflict.
final static int PROBE_THROTTLE_COUNT = 10; //After x tries go 1 time a sec. on probes.
final static int PROBE_THROTTLE_COUNT_INTERVAL = 5000; //We only increment the throttle count, if
// the previous increment is inside this interval.
final static int ANNOUNCE_WAIT_INTERVAL = 1000; //milliseconds between Announce loops.
final static int RECORD_REAPER_INTERVAL = 10000; //milliseconds between cache cleanups.
final static int KNOWN_ANSWER_TTL = 120;
final static int ANNOUNCED_RENEWAL_TTL_INTERVAL = DNS_TTL * 500; // 50% of the TTL in milliseconds
}

View File

@ -1,161 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.util.logging.Logger;
/**
* 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
*/
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;
}
/**
* Check if two entries have exactly the same name, type, and class.
*/
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;
}
/**
* Overriden, to return a value which is consistent with the value returned
* by equals(Object).
*/
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 + "]" : "]");
}
}

View File

@ -1,478 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.io.IOException;
import java.net.DatagramPacket;
import java.util.ArrayList;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* Parse an incoming DNS message into its components.
*
* @version %I%, %G%
* @author Arthur van Hoff, Werner Randelshofer, Pierre Frisch
*/
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 questions;
List answers;
/**
* Parse a message from a datagram packet.
*/
DNSIncoming(DatagramPacket packet) throws IOException
{
this.packet = packet;
this.data = packet.getData();
this.len = packet.getLength();
this.off = packet.getOffset();
this.questions = Collections.EMPTY_LIST;
this.answers = Collections.EMPTY_LIST;
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(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)
{
answers = Collections.synchronizedList(new ArrayList(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:
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 iterator = questions.iterator(); iterator.hasNext();)
{
buf.append(" ques:" + iterator.next() + "\n");
}
int count = 0;
for (Iterator 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();
}
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())
{
this.questions.addAll(that.questions);
this.numQuestions += that.numQuestions;
if (Collections.EMPTY_LIST.equals(answers))
{
answers = Collections.synchronizedList(new ArrayList());
}
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);
}
}

View File

@ -1,37 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.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.
*/
void updateRecord(JmDNS jmdns, long now, DNSRecord record);
}

View File

@ -1,394 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.io.IOException;
import java.util.Hashtable;
import java.util.LinkedList;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 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 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)
{
this.flags = flags;
this.multicast = multicast;
names = new Hashtable();
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 authorativeAnswers = new LinkedList();
/**
* 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 = (Integer) 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, new Integer(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;
}
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();
}
}

View File

@ -1,57 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.util.logging.Logger;
/**
* A DNS question.
*
* @version %I%, %G%
* @author Arthur van Hoff
*/
final class DNSQuestion extends DNSEntry
{
private static Logger logger = Logger.getLogger(DNSQuestion.class.toString());
/**
* Create a question.
*/
DNSQuestion(String name, int type, int clazz)
{
super(name, type, clazz);
}
/**
* 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.
*/
public String toString()
{
return toString("question", null);
}
}

View File

@ -1,686 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.io.ByteArrayOutputStream;
import java.io.DataOutputStream;
import java.io.IOException;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.UnknownHostException;
import java.util.Iterator;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 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();
}
/**
* True if this record is the same as some other record.
*/
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((DNSRecord) other);
}
/**
* True if this record has the same value as some other record.
*/
abstract boolean sameValue(DNSRecord other);
/**
* True if this record has the same type as some other record.
*/
boolean sameType(DNSRecord other)
{
return 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((DNSRecord) 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.
*/
public 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.
*/
public 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;
}
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);
}
}
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);
}
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.
*/
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 (Iterator i = dns.services.values().iterator(); i.hasNext();)
{
ServiceInfo info = (ServiceInfo) i.next();
info.revertState();
}
}
dns.revertState();
return true;
}
}
return false;
}
/**
* Does the necessary actions, when this as a response.
*/
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 (Iterator i = dns.services.values().iterator(); i.hasNext();)
{
ServiceInfo info = (ServiceInfo) i.next();
info.revertState();
}
}
dns.revertState();
return true;
}
}
return false;
}
DNSOutgoing addAnswer(JmDNS dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException
{
return out;
}
public String toString()
{
return toString(" address '" + (addr != null ? addr.getHostAddress() : "null") + "'");
}
}
/**
* Pointer record.
*/
public 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;
}
void write(DNSOutgoing out) throws IOException
{
out.writeName(alias);
}
boolean sameValue(DNSRecord other)
{
return alias.equals(((Pointer) other).alias);
}
boolean handleQuery(JmDNS dns, long expirationTime)
{
// Nothing to do (?)
// I think there is no possibility for conflicts for this record type?
return false;
}
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;
}
DNSOutgoing addAnswer(JmDNS dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException
{
return out;
}
public String toString()
{
return toString(alias);
}
}
public static class Text extends DNSRecord
{
private static Logger logger = Logger.getLogger(Text.class.toString());
final public byte text[];
Text(String name, int type, int clazz, int ttl, byte text[])
{
super(name, type, clazz, ttl);
this.text = text;
}
void write(DNSOutgoing out) throws IOException
{
out.writeBytes(text, 0, text.length);
}
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;
}
boolean handleQuery(JmDNS dns, long expirationTime)
{
// Nothing to do (?)
// I think there is no possibility for conflicts for this record type?
return false;
}
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;
}
DNSOutgoing addAnswer(JmDNS dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException
{
return out;
}
public String toString()
{
return toString((text.length > 10) ? new String(text, 0, 7) + "..." : new String(text));
}
}
/**
* Service record.
*/
public 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;
}
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;
}
boolean sameValue(DNSRecord other)
{
Service s = (Service) other;
return (priority == s.priority) && (weight == s.weight) && (port == s.port) && server.equals(s.server);
}
boolean handleQuery(JmDNS dns, long expirationTime)
{
ServiceInfo info = (ServiceInfo) 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;
}
boolean handleResponse(JmDNS dns)
{
ServiceInfo info = (ServiceInfo) 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;
}
DNSOutgoing addAnswer(JmDNS dns, DNSIncoming in, InetAddress addr, int port, DNSOutgoing out) throws IOException
{
ServiceInfo info = (ServiceInfo) 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;
}
public String toString()
{
return toString(server + ":" + port);
}
}
public String toString(String other)
{
return toString("record", ttl + "/" + getRemainingTTL(System.currentTimeMillis()) + "," + other);
}
}

View File

@ -1,125 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.util.ArrayList;
import java.util.logging.Logger;
/**
* 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
{
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 sequence = new ArrayList();
private DNSState(String name)
{
this.name = name;
sequence.add(this);
}
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.
*/
public final DNSState advance()
{
return (isProbing() || isAnnouncing()) ? (DNSState) sequence.get(ordinal + 1) : this;
}
/**
* Returns to the next reverted state.
* All states except CANCELED revert to PROBING_1.
* Status CANCELED does not revert.
*/
public final DNSState revert()
{
return (this == CANCELED) ? this : PROBING_1;
}
/**
* 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.
*/
public boolean isAnnouncing()
{
return compareTo(ANNOUNCING_1) >= 0 && compareTo(ANNOUNCING_2) <= 0;
}
/**
* 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 &lt; PROBING_2 &lt; PROBING_3 &lt; ANNOUNCING_1 &lt;
* ANNOUNCING_2 &lt; RESPONDING &lt; ANNOUNCED &lt; CANCELED.
*/
public int compareTo(Object o)
{
return ordinal - ((DNSState) o).ordinal;
}
}

View File

@ -1,150 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.net.*;
import java.util.logging.Level;
import java.util.logging.Logger;
/**
* 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();
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;
}
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();
}
}

View File

@ -1,113 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.util.EventObject;
import java.util.logging.Logger;
/**
* 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;
}
/**
* Returns the JmDNS instance which originated the event.
*/
public JmDNS getDNS()
{
return (JmDNS) getSource();
}
/**
* 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.
*/
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.
*/
public ServiceInfo getInfo()
{
return info;
}
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();
}
}

View File

@ -1,673 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.io.OutputStream;
import java.net.InetAddress;
import java.util.Enumeration;
import java.util.Hashtable;
import java.util.TimerTask;
import java.util.Vector;
import java.util.logging.Logger;
/**
* JmDNS service information.
*
* @version %I%, %G%
* @author Arthur van Hoff, Jeff Sonstein, Werner Randelshofer
*/
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[];
Hashtable 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, Hashtable props)
{
this(type, name, port, weight, priority, new byte[0]);
if (props != null)
{
try
{
ByteArrayOutputStream out = new ByteArrayOutputStream(256);
for (Enumeration e = props.keys(); e.hasMoreElements();)
{
String key = (String) e.nextElement();
Object val = props.get(key);
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;
}
/**
* 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> .
*/
public String getType()
{
return type;
}
/**
* Unqualified service instance name, such as <code>foobar</code> .
*/
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> .
*/
public String getQualifiedName()
{
return name + "." + type;
}
/**
* Get the name of the server.
*/
public String getServer()
{
return server;
}
/**
* Get the host address of the service (ie X.X.X.X).
*/
public String getHostAddress()
{
return (addr != null ? addr.getHostAddress() : "");
}
public InetAddress getAddress()
{
return addr;
}
/**
* Get the InetAddress of the service.
*/
public InetAddress getInetAddress()
{
return addr;
}
/**
* Get the port for the service.
*/
public int getPort()
{
return port;
}
/**
* Get the priority of the service.
*/
public int getPriority()
{
return priority;
}
/**
* Get the weight of the service.
*/
public int getWeight()
{
return weight;
}
/**
* Get 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.
*/
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.
*/
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.
*/
public String getURL(String protocol)
{
String url = protocol + "://" + getAddress() + ":" + 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.
*/
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.
*/
public synchronized String getPropertyString(String name)
{
byte data[] = (byte[]) getProperties().get(name);
if (data == null)
{
return null;
}
if (data == NO_VALUE)
{
return "true";
}
return readUTF(data, 0, data.length);
}
/**
* Enumeration of the property names.
*/
public Enumeration getPropertyNames()
{
Hashtable props = getProperties();
return (props != null) ? props.keys() : new Vector().elements();
}
/**
* 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 Hashtable getProperties()
{
if ((props == null) && (text != null))
{
Hashtable props = new Hashtable();
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;
}
// REMIND: Oops, this shouldn't be public!
/**
* JmDNS callback to update a DNS record.
*/
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;
}
public int hashCode()
{
return getQualifiedName().hashCode();
}
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();
}
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();
}
}

View File

@ -1,56 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.util.EventListener;
/**
* 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);
}

View File

@ -1,38 +0,0 @@
/**
* Copyright 2003-2005 Arthur van Hoff, Rick Blair
*
* Licensed to the Apache Software Foundation (ASF) under one or more
* contributor license agreements. See the NOTICE file distributed with
* this work for additional information regarding copyright ownership.
* The ASF licenses this file to You 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 org.apache.activemq.jmdns;
import java.util.EventListener;
/**
* 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);
}

View File

@ -1,17 +0,0 @@
===============================================================
== NOTICE File for JmDNS ==
===============================================================
Java Multicast Domain Name Server (JmDNS)
This project was originally developed by Arthur van Hoff under the GNU
Lesser General Public License as jRendevous. It was moved to Sourceforge
by Rick Blair and renamed to JmDNS with the Arthur's kind permission.
Currently it has been re-released under the Apache License, Version 2.0.
Details of the Apache License, Version 2.0 can be found at:
http://www.apache.org/licenses/
For other details please see the README.txt file.

View File

@ -1,117 +0,0 @@
// %Z%%M%, %I%, %G%
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
//This library is now licensed under the Apache License Version 2.0 Please
//see the file NOTICE.
Arthur van Hoff
avh@strangeberry.com
Rick Blair
rickblair@mac.com
** JmDNS
This is an implemenation of multi-cast DNS in Java. It currently
supports service discovery and service registration. It is fully
interoperable with Apple's Rendezvous.
** Requirements
jmdns has been tested using the JDK 1.3.1 and JDK 1.4.0
on the following platforms:
Windows 9x, XP, 2000
Linux RedHat 7.3-9.0, Mandrake
Mac OSX
** Running jmdns from the Command Line
GUI browser:
java -jar lib/jmdns.jar -browse
TTY browser for a particular service type:
java -jar lib/jmdns.jar -bs _http._tcp local.
Register a service:
java -jar lib/jmdns.jar -rs foobar _http._tcp local. 1234 path=index.html
List service types:
java -jar lib/jmdns.jar -bt
To print debugging output specify -d as the first argument.
** Sample Code for Service Registration
import javax.jmdns.*;
JmDNS jmdns = new JmDNS();
jmdns.registerService(
new ServiceInfo("_http._tcp.local.", "foo._http._tcp.local.", 1234, 0, 0, "path=index.html")
);
** Sample code for Serivice Discovery
import javax.jmdns.*;
static class SampleListener implements ServiceListener
{
public void addService(JmDNS jmdns, String type, String name)
{
System.out.println("ADD: " + jmdns.getServiceInfo(type, name));
}
public void removeService(JmDNS jmdns, String type, String name)
{
System.out.println("REMOVE: " + name);
}
public void resolveService(JmDNS jmdns, String type, String name, ServiceInfo info)
{
System.out.println("RESOLVED: " + info);
}
}
JmDNS jmdns = new JmDNS();
jmdns.addServiceListener("_http._tcp.local.", new SampleListener());
** Changes since October 2003
- Renamed package com.strangeberry.rendezvous to javax.jmdns
- fixed unicast queries
- fixed property handling
- use the hostname instead of the service name is address resolution
- Added Apache License.
--------------------------------------------------------------------
The activemq-jmdns_1.0 source is derived from http://repo1.maven.org/maven2/jmdns/jmdns/1.0/jmdns-1.0-sources.jar
Changes to apache activemq version:
- renamed package javax.jmdns to org.apache.activemq.jmdns
- removed classes with lgpl source headers, leaving only the org.apache.activemq.jmdns package.

View File

@ -75,6 +75,7 @@
<bundle>mvn:org.apache.httpcomponents/httpcore-osgi/${httpclient-version}</bundle>
<bundle>mvn:org.apache.httpcomponents/httpclient-osgi/${httpclient-version}</bundle>
<bundle>mvn:org.springframework/spring-oxm/${spring-version}</bundle>
<bundle>mvn:javax.jmdns/jmdns/${jmdns-version}</bundle>
</feature>
<feature name="activemq-camel" version="${activemq-version}" resolver="(obr)">

View File

@ -158,8 +158,8 @@
<artifactId>jaxp-api</artifactId>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-jmdns_1.0</artifactId>
<groupId>javax.jmdns</groupId>
<artifactId>jmdns</artifactId>
</dependency>
<dependency>
<groupId>com.thoughtworks.xstream</groupId>

View File

@ -138,7 +138,6 @@
<include>${pom.groupId}:activemq-xmpp</include>
<include>${pom.groupId}:activemq-spring</include>
<include>${pom.groupId}:activeio-core</include>
<include>${pom.groupId}:activemq-jmdns_1.0</include>
<include>commons-beanutils:commons-beanutils</include>
<include>commons-collections:commons-collections</include>
<include>commons-httpclient:commons-httpclient</include>
@ -168,6 +167,7 @@
<include>org.apache.velocity:velocity</include>
<include>org.apache.servicemix.bundles:org.apache.servicemix.bundles.josql</include>
<include>org.jasypt:jasypt</include>
<include>javax.jmdns:jmdns</include>
<!-- leveldb -->
<include>org.fusesource.fuse-extra:fusemq-leveldb</include>
<include>org.fusesource.hawtbuf:hawtbuf</include>

15
pom.xml
View File

@ -73,6 +73,7 @@
<jasypt-version>1.9.0</jasypt-version>
<jdom-version>1.0</jdom-version>
<jetty-version>7.6.5.v20120716</jetty-version>
<jmdns-version>3.4.1</jmdns-version>
<jsp-version>2.1.v20100127</jsp-version>
<jstl-version>1.1.2</jstl-version>
<jettison-version>1.3.2</jettison-version>
@ -204,7 +205,6 @@
<module>activemq-web-console</module>
<module>activemq-xmpp</module>
<module>assembly</module>
<module>activemq-jmdns_1.0</module>
<module>kahadb</module>
</modules>
@ -246,11 +246,6 @@
<artifactId>activemq-jaas</artifactId>
<version>${activemq-version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-jmdns_1.0</artifactId>
<version>${activemq-version}</version>
</dependency>
<dependency>
<groupId>org.apache.activemq</groupId>
<artifactId>activemq-pool</artifactId>
@ -479,6 +474,14 @@
<optional>true</optional>
</dependency>
<!-- zeroconf transport -->
<dependency>
<groupId>javax.jmdns</groupId>
<artifactId>jmdns</artifactId>
<version>${jmdns-version}</version>
<optional>true</optional>
</dependency>
<!-- For jsvc support -->
<dependency>
<groupId>commons-daemon</groupId>