📄 upnpimpl.java
字号:
/*
* Created on 14-Jun-2004
* Created by Paul Gardner
* Copyright (C) 2004, 2005, 2006 Aelitis, All Rights Reserved.
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License
* as published by the Free Software Foundation; either version 2
* of the License, or (at your option) any later version.
* This program 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 General Public License for more details.
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
*
* AELITIS, SAS au capital de 46,603.30 euros
* 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France.
*
*/
package com.aelitis.net.upnp.impl;
/**
* @author parg
*
*/
import java.util.*;
import java.net.*;
import java.io.*;
import org.gudy.azureus2.core3.util.AEMonitor;
import org.gudy.azureus2.core3.util.AERunnable;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.core3.util.ThreadPool;
import org.gudy.azureus2.plugins.utils.resourcedownloader.ResourceDownloader;
import org.gudy.azureus2.plugins.utils.resourcedownloader.ResourceDownloaderAdapter;
import org.gudy.azureus2.plugins.utils.resourcedownloader.ResourceDownloaderException;
import org.gudy.azureus2.plugins.utils.resourcedownloader.ResourceDownloaderFactory;
import org.gudy.azureus2.plugins.utils.xml.simpleparser.SimpleXMLParserDocument;
import org.gudy.azureus2.plugins.utils.xml.simpleparser.SimpleXMLParserDocumentException;
import com.aelitis.net.upnp.*;
import com.aelitis.net.upnp.impl.device.*;
import com.aelitis.net.upnp.services.UPnPWANConnectionPortMapping;
import com.aelitis.net.upnp.services.UPnPWANIPConnection;
public class
UPnPImpl
extends ResourceDownloaderAdapter
implements UPnP, SSDPIGDListener
{
public static final String NL = "\r\n";
private static UPnPImpl singleton;
private static AEMonitor class_mon = new AEMonitor( "UPnP:class" );
public static UPnP
getSingleton(
UPnPAdapter adapter,
String[] selected_interfaces )
throws UPnPException
{
try{
class_mon.enter();
if ( singleton == null ){
singleton = new UPnPImpl( adapter, selected_interfaces );
}
return( singleton );
}finally{
class_mon.exit();
}
}
private UPnPAdapter adapter;
private SSDPIGD ssdp;
private Map root_locations = new HashMap();
private List log_listeners = new ArrayList();
private List log_history = new ArrayList();
private List log_alert_history = new ArrayList();
private List rd_listeners = new ArrayList();
private AEMonitor rd_listeners_mon = new AEMonitor( "UPnP:L" );
private int http_calls_ok = 0;
private int direct_calls_ok = 0;
private int trace_index = 0;
private String secondary_route_log = "";
private ThreadPool device_dispatcher = new ThreadPool("UPnPDispatcher", 1, true );
protected AEMonitor this_mon = new AEMonitor( "UPnP" );
protected
UPnPImpl(
UPnPAdapter _adapter,
String[] _selected_interfaces )
throws UPnPException
{
adapter = _adapter;
ssdp = SSDPIGDFactory.create( this, _selected_interfaces );
ssdp.addListener(this);
ssdp.start();
}
public void
rootDiscovered(
final NetworkInterface network_interface,
final InetAddress local_address,
final String usn,
final URL location )
{
// we need to take this operation off the main thread as it can take some time. This is a single
// concurrency queued thread pool so things get done serially in the right order
device_dispatcher.run(
new AERunnable()
{
public void
runSupport()
{
final UPnPRootDeviceImpl old_root_device;
try{
rd_listeners_mon.enter();
old_root_device = (UPnPRootDeviceImpl)root_locations.get( usn );
}finally{
rd_listeners_mon.exit();
}
if ( old_root_device != null ){
// we remember one route to the device - if the network interfaces change
// we do a full reset so we don't need to deal with that here
if ( !old_root_device.getNetworkInterface().getName().equals( network_interface.getName())){
return;
}
// check that the device's location is the same
if ( old_root_device.getLocation().equals( location )){
return;
}
}
if ( old_root_device != null ){
// something changed, resetablish everything
try{
// not the best "atomic" code here but it'll do as the code that adds roots (this)
// is single threaded via the dispatcher
rd_listeners_mon.enter();
root_locations.remove( usn );
}finally{
rd_listeners_mon.exit();
}
old_root_device.destroy( true );
}
log( "UPnP: root discovered: usn=" + usn + ", location=" + location + ", ni=" + network_interface.getName() + ",local=" + local_address.toString() );
List listeners;
try{
rd_listeners_mon.enter();
listeners = new ArrayList( rd_listeners );
}finally{
rd_listeners_mon.exit();
}
for (int i=0;i<listeners.size();i++){
try{
if ( !((UPnPListener)listeners.get(i)).deviceDiscovered( usn, location )){
return;
}
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
try{
UPnPRootDeviceImpl new_root_device = new UPnPRootDeviceImpl( UPnPImpl.this, network_interface, local_address, usn, location );
try{
rd_listeners_mon.enter();
root_locations.put( usn, new_root_device );
listeners = new ArrayList( rd_listeners );
}finally{
rd_listeners_mon.exit();
}
for (int i=0;i<listeners.size();i++){
try{
((UPnPListener)listeners.get(i)).rootDeviceFound( new_root_device );
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
}catch( UPnPException e ){
log( e.toString());
adapter.log(e);
}
}
});
}
public void
rootAlive(
String usn,
URL location )
{
UPnPRootDeviceImpl root_device = (UPnPRootDeviceImpl)root_locations.get( usn );
if ( root_device == null ){
ssdp.searchNow();
}
}
public void
rootLost(
final InetAddress local_address,
final String usn )
{
// we need to take this operation off the main thread as it can take some time
device_dispatcher.run(
new AERunnable()
{
public void
runSupport()
{
UPnPRootDeviceImpl root_device = null;
try{
rd_listeners_mon.enter();
root_device = (UPnPRootDeviceImpl)root_locations.remove( usn );
}finally{
rd_listeners_mon.exit();
}
if ( root_device == null ){
return;
}
log( "UPnP: root lost: usn=" + usn + ", location=" + root_device.getLocation() + ", ni=" + root_device.getNetworkInterface().getName() + ",local=" + root_device.getLocalAddress().toString());
root_device.destroy( false );
}
});
}
public void
interfaceChanged(
NetworkInterface network_interface )
{
reset();
}
public void
reset()
{
log( "UPnP: reset" );
List roots;
try{
rd_listeners_mon.enter();
roots = new ArrayList(root_locations.values());
root_locations.clear();
}finally{
rd_listeners_mon.exit();
}
for (int i=0;i<roots.size();i++){
((UPnPRootDeviceImpl)roots.get(i)).destroy( true );
}
ssdp.searchNow();
}
public SimpleXMLParserDocument
parseXML(
InputStream _is )
throws SimpleXMLParserDocumentException, IOException
{
// ASSUME UTF-8
ByteArrayOutputStream baos = null;
try{
baos = new ByteArrayOutputStream(1024);
byte[] buffer = new byte[8192];
while(true){
int len = _is.read( buffer );
if ( len <= 0 ){
break;
}
baos.write( buffer, 0, len );
}
}finally{
baos.close();
}
byte[] bytes_in = baos.toByteArray();
InputStream is = new ByteArrayInputStream( bytes_in );
// Gudy's router was returning trailing nulls which then stuffed up the
// XML parser. Hence this code to try and strip them
try{
StringBuffer data = new StringBuffer(1024);
LineNumberReader lnr = new LineNumberReader( new InputStreamReader( is, "UTF-8" ));
Set ignore_map = null;
while( true ){
String line = lnr.readLine();
if ( line == null ){
break;
}
// remove any obviously invalid characters - I've seen some routers generate stuff like
// 0x18 which stuffs the xml parser with "invalid unicode character"
for (int i=0;i<line.length();i++){
char c = line.charAt(i);
if ( c < 0x20 && c != '\r' && c != '\t' ){
data.append( ' ' );
if ( ignore_map == null ){
ignore_map = new HashSet();
}
Character cha = new Character(c);
if ( !ignore_map.contains( cha )){
ignore_map.add( cha );
adapter.trace( " ignoring character(s) " + (int)c + " in xml response" );
}
}else{
data.append( c );
}
}
data.append( "\n" );
}
String data_str = data.toString();
adapter.trace( "UPnP:Response:" + data_str );
return( adapter.parseXML( data_str ));
}catch( Throwable e ){
try{
FileOutputStream trace = new FileOutputStream( getTraceFile());
trace.write( bytes_in );
trace.close();
}catch( Throwable f ){
adapter.log(f);
}
if ( e instanceof SimpleXMLParserDocumentException ){
throw((SimpleXMLParserDocumentException)e);
}
throw( new SimpleXMLParserDocumentException(e ));
}
}
public SimpleXMLParserDocument
downloadXML(
URL url )
throws UPnPException
{
try{
if ( forceDirect()){
Socket socket = new Socket(url.getHost(), url.getPort());
try{
PrintWriter pw = new PrintWriter(new OutputStreamWriter( socket.getOutputStream(), "UTF8" ));
String url_target = url.toString();
int p1 = url_target.indexOf( "://" ) + 3;
p1 = url_target.indexOf( "/", p1 );
url_target = url_target.substring( p1 );
pw.print( "GET " + url_target + " HTTP/1.1" + NL );
pw.print( "User-Agent: Azureus (UPnP/1.0)" + NL );
pw.print( "Host: " + url.getHost() + NL );
pw.print( "Connection: Close" + NL );
pw.print( "Pragma: no-cache" + NL + NL );
pw.flush();
InputStream is = socket.getInputStream();
String reply_header = "";
while(true){
byte[] buffer = new byte[1];
if ( is.read( buffer ) <= 0 ){
throw( new IOException( "Premature end of input stream" ));
}
reply_header += (char)buffer[0];
if ( reply_header.endsWith( NL+NL )){
break;
}
}
p1 = reply_header.indexOf( NL );
String first_line = reply_header.substring( 0, p1 ).trim();
if ( first_line.indexOf( "200" ) == -1 ){
throw( new IOException( "HTTP request failed:" + first_line ));
}
return( parseXML( is ));
}finally{
try{
socket.close();
}catch( Throwable e ){
Debug.printStackTrace(e);
}
}
}else{
ResourceDownloaderFactory rdf = adapter.getResourceDownloaderFactory();
ResourceDownloader rd = rdf.getRetryDownloader( rdf.create( url ), 3 );
rd.addListener( this );
InputStream data = rd.download();
return( parseXML( data ));
⌨️ 快捷键说明
复制代码
Ctrl + C
搜索代码
Ctrl + F
全屏模式
F11
切换主题
Ctrl + Shift + D
显示快捷键
?
增大字号
Ctrl + =
减小字号
Ctrl + -