aboutsummaryrefslogtreecommitdiffstats
path: root/src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java
diff options
context:
space:
mode:
Diffstat (limited to 'src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java')
-rw-r--r--src/net/java/sip/communicator/impl/protocol/zeroconf/jmdns/JmDNS.java3048
1 files changed, 0 insertions, 3048 deletions
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);
- }
-}