Discovery.java :  » Game » rugl » net » sbbi » upnp » Java Open Source

Java Open Source » Game » rugl 
rugl » net » sbbi » upnp » Discovery.java
/*
 * ====================================================================
 * ======== The Apache Software License, Version 1.1
 * ==================
 * ==========================================================
 * Copyright (C) 2002 The Apache Software Foundation. All rights
 * reserved. Redistribution and use in source and binary forms, with
 * or without modifica- tion, are permitted provided that the
 * following conditions are met: 1. Redistributions of source code
 * must retain the above copyright notice, this list of conditions and
 * the following disclaimer. 2. Redistributions in binary form must
 * reproduce the above copyright notice, this list of conditions and
 * the following disclaimer in the documentation and/or other
 * materials provided with the distribution. 3. The end-user
 * documentation included with the redistribution, if any, must
 * include the following acknowledgment: "This product includes
 * software developed by SuperBonBon Industries
 * (http://www.sbbi.net/)." Alternately, this acknowledgment may
 * appear in the software itself, if and wherever such third-party
 * acknowledgments normally appear. 4. The names "UPNPLib" and
 * "SuperBonBon Industries" must not be used to endorse or promote
 * products derived from this software without prior written
 * permission. For written permission, please contact info@sbbi.net.
 * 5. Products derived from this software may not be called
 * "SuperBonBon Industries", nor may "SBBI" appear in their name,
 * without prior written permission of SuperBonBon Industries. THIS
 * SOFTWARE IS PROVIDED ``AS IS'' AND ANY EXPRESSED OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES
 * OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 * DISCLAIMED. IN NO EVENT SHALL THE APACHE SOFTWARE FOUNDATION OR ITS
 * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT,INCIDENTAL,
 * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLU- DING, BUT NOT
 * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF
 * USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
 * ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY,
 * OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT
 * OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
 * SUCH DAMAGE. This software consists of voluntary contributions made
 * by many individuals on behalf of SuperBonBon Industries. For more
 * information on SuperBonBon Industries, please see
 * <http://www.sbbi.net/>.
 */

package net.sbbi.upnp;

import java.io.IOException;
import java.net.DatagramPacket;
import java.net.Inet4Address;
import java.net.InetAddress;
import java.net.InetSocketAddress;
import java.net.NetworkInterface;
import java.net.URL;
import java.util.Enumeration;
import java.util.HashMap;
import java.util.Iterator;
import java.util.Map;

import net.sbbi.upnp.devices.RootDevice;

/**
 * Class to discover an UPNP device on the network.</br> A multicast
 * socket will be created to discover devices, the binding port for
 * this socket is set to 1901, if this is causing a problem you can
 * use the net.sbbi.upnp.Discovery.bindPort system property to specify
 * another port. The discovery methods only accept matching device
 * description and broadcast message response IP to avoid a security
 * flaw with the protocol. If you are not happy with such behaviour
 * you can set the net.sbbi.upnp.ddos.matchip system property to false
 * to avoid this check.
 * 
 * @author <a href="mailto:superbonbon@sbbi.net">SuperBonBon</a>
 * @version 1.0
 */

public class Discovery
{
  /***/
  public final static String ROOT_DEVICES = "upnp:rootdevice";

  /***/
  public final static String ALL_DEVICES = "ssdp:all";

  /***/
  public static final int DEFAULT_MX = 3;

  /***/
  public static final int DEFAULT_TTL = 4;

  /***/
  public static final int DEFAULT_TIMEOUT = 1500;

  /***/
  public static final String DEFAULT_SEARCH = ALL_DEVICES;

  /***/
  public static final int DEFAULT_SSDP_SEARCH_PORT = 1901;

  /***/
  public final static String SSDP_IP = "239.255.255.250";

  /***/
  public final static int SSDP_PORT = 1900;

  /**
   * Devices discovering on all network interfaces with default
   * values, all root devices will be searched
   * 
   * @return an array of UPNP Root device or null if nothing found
   *         with the default timeout. Null does NOT means that no
   *         UPNP device is available on the network. It only means
   *         that for this default timeout no devices responded or
   *         that effectively no devices are available at all.
   * @throws IOException
   *            if some IOException occurs during discovering
   */
  public static RootDevice[] discover() throws IOException
  {
    return discover( DEFAULT_TIMEOUT, DEFAULT_TTL, DEFAULT_MX, DEFAULT_SEARCH );
  }

  /**
   * Devices discovering on all network interfaces with a given root
   * device to search
   * 
   * @param searchTarget
   *           the device URI to search
   * @return an array of UPNP Root device that matches the search or
   *         null if nothing found with the default timeout. Null
   *         does NOT means that no UPNP device is available on the
   *         network. It only means that for this given timeout no
   *         devices responded or that effectively no devices are
   *         available at all.
   * @throws IOException
   *            if some IOException occurs during discovering
   */
  public static RootDevice[] discover( String searchTarget ) throws IOException
  {
    return discover( DEFAULT_TIMEOUT, DEFAULT_TTL, DEFAULT_MX, searchTarget );
  }

  /**
   * Devices discovering on all network interfaces with a given
   * timeout and a given root device to search
   * 
   * @param timeOut
   *           the time allowed for a device to give a response
   * @param searchTarget
   *           the device URI to search
   * @return an array of UPNP Root device that matches the search or
   *         null if nothing found with the given timeout. Null does
   *         NOT means that no UPNP device is available on the
   *         network. It only means that for this given timeout no
   *         devices responded or that effectively no devices are
   *         available at all.
   * @throws IOException
   *            if some IOException occurs during discovering
   */
  public static RootDevice[] discover( int timeOut, String searchTarget )
      throws IOException
  {
    return discover( timeOut, DEFAULT_TTL, DEFAULT_MX, searchTarget );
  }

  /**
   * Devices discovering on all network interfaces with a given
   * timeout and a given root device to search, as well as a ttl and
   * mx param
   * 
   * @param timeOut
   *           the timeout for the a device to give a reponse
   * @param ttl
   *           the UDP socket packets time to live
   * @param mx
   *           discovery message mx http header field value
   * @param searchTarget
   *           the device URI to search
   * @return an array of UPNP Root device that matches the search or
   *         null if nothing found within the given timeout. Null
   *         return does NOT means that no UPNP device is available
   *         on the network. It only means that for this given
   *         timeout no devices responded or that effectively no
   *         devices are available at all.
   * @throws IOException
   *            if some IOException occurs during discovering
   */
  public static RootDevice[] discover( int timeOut, int ttl, int mx, String searchTarget )
      throws IOException
  {
    return discoverDevices( timeOut, ttl, mx, searchTarget, null );
  }

  /**
   * Devices discovering with a given timeout and a given root device
   * to search on an given network interface, as well as a ttl and mx
   * param
   * 
   * @param timeOut
   *           the timeout for the a device to give a reponse
   * @param ttl
   *           the UDP socket packets time to live
   * @param mx
   *           discovery message mx http header field value
   * @param searchTarget
   *           the device URI to search
   * @param ni
   *           the networkInterface where to search devices, null to
   *           lookup all interfaces
   * @return an array of UPNP Root device that matches the search or
   *         null if nothing found within the given timeout. Null
   *         return does NOT means that no UPNP device is available
   *         on the network. It only means that for this given
   *         timeout no devices responded or that effectively no
   *         devices are available at all.
   * @throws IOException
   *            if some IOException occurs during discovering
   */
  public static RootDevice[] discover( int timeOut, int ttl, int mx,
      String searchTarget, NetworkInterface ni ) throws IOException
  {
    return discoverDevices( timeOut, ttl, mx, searchTarget, ni );
  }

  private static RootDevice[] discoverDevices( int timeOut, int ttl, int mx,
      String searchTarget, NetworkInterface ni ) throws IOException
  {
    if( searchTarget == null || searchTarget.trim().length() == 0 )
    {
      throw new IllegalArgumentException( "Illegal searchTarget" );
    }

    final Map<String, RootDevice> devices = new HashMap<String, RootDevice>();

    DiscoveryResultsHandler handler = new DiscoveryResultsHandler() {

      @Override
      public void discoveredDevice( String usn, String udn, String nt, String maxAge,
          URL location, String firmware )
      {
        synchronized( devices )
        {
          if( !devices.containsKey( usn ) )
          {
            try
            {
              RootDevice device =
                  RootDevice.build( location, maxAge, firmware, usn, udn );
              devices.put( usn, device );
            }
            catch( Exception ex )
            {
              // log.error(
              // "Error occured during upnp root device object creation from location "
              // + location, ex );
              ex.printStackTrace();
            }
          }
        }
      }
    };

    DiscoveryListener.getInstance().registerResultsHandler( handler, searchTarget );
    if( ni == null )
    {
      for( Enumeration e = NetworkInterface.getNetworkInterfaces(); e
          .hasMoreElements(); )
      {
        NetworkInterface intf = ( NetworkInterface ) e.nextElement();
        for( Enumeration adrs = intf.getInetAddresses(); adrs.hasMoreElements(); )
        {
          InetAddress adr = ( InetAddress ) adrs.nextElement();
          if( adr instanceof Inet4Address && !adr.isLoopbackAddress() )
          {
            sendSearchMessage( adr, ttl, mx, searchTarget );
          }
        }
      }
    }
    else
    {
      for( Enumeration adrs = ni.getInetAddresses(); adrs.hasMoreElements(); )
      {
        InetAddress adr = ( InetAddress ) adrs.nextElement();
        if( adr instanceof Inet4Address && !adr.isLoopbackAddress() )
        {
          sendSearchMessage( adr, ttl, mx, searchTarget );
        }
      }
    }

    try
    {
      Thread.sleep( timeOut );
    }
    catch( InterruptedException ex )
    {
      // don't care
    }

    DiscoveryListener.getInstance().unRegisterResultsHandler( handler, searchTarget );

    if( devices.size() == 0 )
    {
      return null;
    }
    int j = 0;
    RootDevice[] rootDevices = new RootDevice[ devices.size() ];
    for( Iterator i = devices.values().iterator(); i.hasNext(); )
    {
      rootDevices[ j++ ] = ( RootDevice ) i.next();
    }
    return rootDevices;

  }

  /**
   * Sends an SSDP search message on the network
   * 
   * @param src
   *           the sender ip
   * @param ttl
   *           the time to live
   * @param mx
   *           the mx field
   * @param searchTarget
   *           the search target
   * @throws IOException
   *            if some IO errors occurs during search
   */
  public static void sendSearchMessage( InetAddress src, int ttl, int mx,
      String searchTarget ) throws IOException
  {

    int bindPort = DEFAULT_SSDP_SEARCH_PORT;
    String port = System.getProperty( "net.sbbi.upnp.Discovery.bindPort" );
    if( port != null )
    {
      bindPort = Integer.parseInt( port );
    }
    InetSocketAddress adr =
        new InetSocketAddress( InetAddress.getByName( Discovery.SSDP_IP ),
            Discovery.SSDP_PORT );

    java.net.MulticastSocket skt = new java.net.MulticastSocket( null );
    skt.bind( new InetSocketAddress( src, bindPort ) );
    skt.setTimeToLive( ttl );
    StringBuilder packet = new StringBuilder();
    packet.append( "M-SEARCH * HTTP/1.1\r\n" );
    packet.append( "HOST: 239.255.255.250:1900\r\n" );
    packet.append( "MAN: \"ssdp:discover\"\r\n" );
    packet.append( "MX: " ).append( mx ).append( "\r\n" );
    packet.append( "ST: " ).append( searchTarget ).append( "\r\n" ).append( "\r\n" );

    String toSend = packet.toString();
    byte[] pk = toSend.getBytes();
    skt.send( new DatagramPacket( pk, pk.length, adr ) );
    skt.disconnect();
    skt.close();
  }

}
java2s.com  | Contact Us | Privacy Policy
Copyright 2009 - 12 Demo Source and Support. All rights reserved.
All other trademarks are property of their respective owners.