/*
* Created on Jan 31, 2008
* Created by Paul Gardner
*
* Copyright 2008 Vuze, Inc. 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; version 2 of the License only.
*
* 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.
*/
package com.aelitis.azureus.plugins.net.netstatus.swt;
import java.net.Inet6Address;
import java.net.InetAddress;
import java.util.*;
import org.gudy.azureus2.core3.util.Debug;
import org.gudy.azureus2.ui.swt.shells.CoreWaiterSWT;
import com.aelitis.azureus.core.AzureusCore;
import com.aelitis.azureus.core.AzureusCoreRunningListener;
import com.aelitis.azureus.core.networkmanager.admin.*;
import com.aelitis.azureus.plugins.net.netstatus.NetStatusPlugin;
import com.aelitis.azureus.plugins.net.netstatus.NetStatusProtocolTesterBT;
import com.aelitis.azureus.plugins.net.netstatus.NetStatusProtocolTesterListener;
public class
NetStatusPluginTester
{
public static final int TEST_PING_ROUTE = 0x00000001;
public static final int TEST_NAT_PROXIES = 0x00000002;
public static final int TEST_OUTBOUND = 0x00000004;
public static final int TEST_INBOUND = 0x00000008;
public static final int TEST_BT_CONNECT = 0x00000010;
private static final int ROUTE_TIMEOUT = 120*1000;
private NetStatusPlugin plugin;
private int test_types;
private loggerProvider logger;
private volatile boolean test_cancelled;
public
NetStatusPluginTester(
NetStatusPlugin _plugin,
int _test_types,
loggerProvider _logger )
{
plugin = _plugin;
test_types = _test_types;
logger = _logger;
}
protected boolean
doTest(
int type )
{
return((test_types & type ) != 0 );
}
public void
run(AzureusCore core)
{
final NetworkAdmin admin = NetworkAdmin.getSingleton();
boolean checked_public = false;
Set<InetAddress> public_addresses = new HashSet<InetAddress>();
InetAddress def_pa = admin.getDefaultPublicAddress();
if ( def_pa != null ){
log( "Default public address is " + def_pa.getHostAddress());
addPublicAddress( public_addresses, def_pa );
checked_public = true;
}
if ( doTest( TEST_PING_ROUTE )){
log( "Testing routing for the following interfaces:" );
NetworkAdminNetworkInterface[] interfaces = admin.getInterfaces();
for (int i=0;i<interfaces.length;i++){
NetworkAdminNetworkInterface intf = interfaces[i];
NetworkAdminNetworkInterfaceAddress[] addresses = intf.getAddresses();
String a_str = "";
for (int j=0;j<addresses.length;j++){
NetworkAdminNetworkInterfaceAddress address = addresses[j];
InetAddress ia = address.getAddress();
if ( ia.isLoopbackAddress() || ia instanceof Inet6Address ){
}else{
a_str += (a_str.length()==0?"":",") + ia.getHostAddress();
}
}
if ( a_str.length() > 0 ){
log( " " + intf.getName() + "/" + intf.getDisplayName() + ": " + a_str );
}
}
if ( admin.canPing()){
log( "Running ping tests" );
try{
InetAddress target_address = InetAddress.getByName( plugin.getPingTarget());
final Map active_pings = new HashMap();
admin.pingTargets(
target_address,
ROUTE_TIMEOUT,
new NetworkAdminRoutesListener()
{
private int timeouts;
public boolean
foundNode(
NetworkAdminNetworkInterfaceAddress intf,
NetworkAdminNode[] route,
int distance,
int rtt )
{
if ( test_cancelled ){
return( false );
}
synchronized( active_pings ){
active_pings.put( intf, route );
}
log( " " + intf.getAddress().getHostAddress() + " -> " + route[route.length-1].getAddress().getHostAddress());
return( false );
}
public boolean
timeout(
NetworkAdminNetworkInterfaceAddress intf,
NetworkAdminNode[] route,
int distance )
{
if ( test_cancelled ){
return( false );
}
log( " " + intf.getAddress().getHostAddress() + " - timeout" );
timeouts++;
if ( timeouts >= 3 ){
return( false );
}
return( true );
}
});
if ( test_cancelled ){
return;
}
int num_routes = active_pings.size();
if ( num_routes == 0 ){
logError( "No active pings found!" );
}else{
log( "Found " + num_routes + " pings(s)" );
Iterator it = active_pings.entrySet().iterator();
while( it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
NetworkAdminNetworkInterfaceAddress address = (NetworkAdminNetworkInterfaceAddress)entry.getKey();
NetworkAdminNode[] route = (NetworkAdminNode[])entry.getValue();
String node_str = "";
for (int i=0;i<route.length;i++){
node_str += (i==0?"":",") + route[i].getAddress().getHostAddress();
}
log( " " + address.getInterface().getName() + "/" + address.getAddress().getHostAddress() + " - " + node_str );
}
}
}catch( Throwable e ){
logError( "Pinging failed: " + Debug.getNestedExceptionMessage(e));
}
}else{
logError( "Can't run ping test as not supported" );
}
if ( test_cancelled ){
return;
}
if ( admin.canTraceRoute()){
log( "Running trace route tests" );
try{
InetAddress target_address = InetAddress.getByName( plugin.getPingTarget());
final Map active_routes = new HashMap();
admin.getRoutes(
target_address,
ROUTE_TIMEOUT,
new NetworkAdminRoutesListener()
{
private String last_as = "";
public boolean
foundNode(
NetworkAdminNetworkInterfaceAddress intf,
NetworkAdminNode[] route,
int distance,
int rtt )
{
if ( test_cancelled ){
return( false );
}
synchronized( active_routes ){
active_routes.put( intf, route );
}
InetAddress ia = route[route.length-1].getAddress();
String as = "";
if ( !ia.isLinkLocalAddress() && !ia.isSiteLocalAddress()){
try{
NetworkAdminASN asn = admin.lookupASN( ia );
as = asn.getString();
if ( as.equals( last_as )){
as = "";
}else{
last_as = as;
}
}catch( Throwable e ){
}
}
log( " " + intf.getAddress().getHostAddress() + " -> " + ia.getHostAddress() + " (hop=" + distance + ")" + (as.length()==0?"":( " - " + as )));
return( true );
}
public boolean
timeout(
NetworkAdminNetworkInterfaceAddress intf,
NetworkAdminNode[] route,
int distance )
{
if ( test_cancelled ){
return( false );
}
log( " " + intf.getAddress().getHostAddress() + " - timeout (hop=" + distance + ")" );
// see if we're getting nowhere
if ( route.length == 0 && distance >= 5 ){
logError( " giving up, no responses" );
return( false );
}
// see if we've got far enough
if ( route.length >= 5 && distance > 6 ){
log( " truncating, sufficient responses" );
return( false );
}
return( true );
}
});
if ( test_cancelled ){
return;
}
int num_routes = active_routes.size();
if ( num_routes == 0 ){
logError( "No active routes found!" );
}else{
log( "Found " + num_routes + " route(s)" );
Iterator it = active_routes.entrySet().iterator();
while( it.hasNext()){
Map.Entry entry = (Map.Entry)it.next();
NetworkAdminNetworkInterfaceAddress address = (NetworkAdminNetworkInterfaceAddress)entry.getKey();
NetworkAdminNode[] route = (NetworkAdminNode[])entry.getValue();
String node_str = "";
for (int i=0;i<route.length;i++){
node_str += (i==0?"":",") + route[i].getAddress().getHostAddress();
}
log( " " + address.getInterface().getName() + "/" + address.getAddress().getHostAddress() + " - " + node_str );
}
}
}catch( Throwable e ){
logError( "Route tracing failed: " + Debug.getNestedExceptionMessage(e));
}
}else{
logError( "Can't run trace route test as not supported" );
}
if ( test_cancelled ){
return;
}
}
if ( doTest( TEST_NAT_PROXIES )){
checked_public = true;
NetworkAdminNATDevice[] nat_devices = admin.getNATDevices(core);
log( nat_devices.length + " NAT device" + (nat_devices.length==1?"":"s") + " found" );
for (int i=0;i<nat_devices.length;i++){
NetworkAdminNATDevice device = nat_devices[i];
InetAddress ext_address = device.getExternalAddress();
addPublicAddress( public_addresses, ext_address );
log( " " + device.getString());
}
NetworkAdminSocksProxy[] socks_proxies = admin.getSocksProxies();
if ( socks_proxies.length == 0 ){
log( "No SOCKS proxy found" );
}else if ( socks_proxies.length == 1 ){
log( "One SOCKS proxy found" );
}else{
log( socks_proxies.length + " SOCKS proxies found" );
}
for (int i=0;i<socks_proxies.length;i++){
NetworkAdminSocksProxy proxy = socks_proxies[i];
log( " " + proxy.getString());
}
NetworkAdminHTTPProxy http_proxy = admin.getHTTPProxy();
if ( http_proxy == null ){
log( "No HTTP proxy found" );
}else{
log( "HTTP proxy found" );
log( " " + http_proxy.getString());
}
}
InetAddress[] bind_addresses = admin.getAllBindAddresses( false );
int num_binds = 0;
for ( int i=0;i<bind_addresses.length;i++ ){
if ( bind_addresses[i] != null ){
num_binds++;
}
}
if ( num_binds == 0 ){
log( "No explicit bind address set" );
}else{
log( num_binds + " bind addresses" );
for ( int i=0;i<bind_addresses.length;i++ ){
if ( bind_addresses[i] != null ){
log( " " + bind_addresses[i].getHostAddress());
}
}
}
if ( doTest( TEST_OUTBOUND )){
checked_public = true;
NetworkAdminProtocol[] outbound_protocols = admin.getOutboundProtocols(core);
if ( outbound_protocols.length == 0 ){
log( "No outbound protocols" );
}else{
for (int i=0;i<outbound_protocols.length;i++){
if ( test_cancelled ){
return;
}
NetworkAdminProtocol protocol = outbound_protocols[i];
log( "Testing " + protocol.getName());
try{
InetAddress public_address =
protocol.test(
null,
new NetworkAdminProgressListener()
{
public void
reportProgress(
String task )
{
log( " " + task );
}
});
logSuccess( " Test successful" );
addPublicAddress( public_addresses, public_address );
}catch( Throwable e ){
logError( " Test failed", e );
}
}
}
}
if ( doTest( TEST_INBOUND )){
checked_public = true;
NetworkAdminProtocol[] inbound_protocols = admin.getInboundProtocols(core);
if ( inbound_protocols.length == 0 ){
log( "No inbound protocols" );
}else{
for (int i=0;i<inbound_protocols.length;i++){
if ( test_cancelled ){
return;
}
NetworkAdminProtocol protocol = inbound_protocols[i];
log( "Testing " + protocol.getName());
try{
InetAddress public_address =
protocol.test(
null,
new NetworkAdminProgressListener()
{
public void
reportProgress(
String task )
{
log( " " + task );
}
});
logSuccess( " Test successful" );
addPublicAddress( public_addresses, public_address );
}catch( Throwable e ){
logError( " Test failed", e );
logInfo( " Check your port forwarding for " + protocol.getTypeString() + " " + protocol.getPort());
}
}
}
}
if ( checked_public ){
if ( public_addresses.size() == 0 ){
log( "No public addresses found" );
}else{
Iterator<InetAddress> it = public_addresses.iterator();
log( public_addresses.size() + " public/external addresses found" );
while( it.hasNext()){
InetAddress pub_address = it.next();
log( " " + pub_address.getHostAddress());
try{
NetworkAdminASN asn = admin.lookupASN(pub_address);
log( " AS details: " + asn.getString());
}catch( Throwable e ){
logError( " failed to lookup AS", e );
}
}
}
}
if ( doTest( TEST_BT_CONNECT )){
log( "Distributed protocol test" );
NetStatusProtocolTesterBT bt_test =
plugin.getProtocolTester().runTest(
new NetStatusProtocolTesterListener()
{
private List sessions = new ArrayList();
public void
complete(
NetStatusProtocolTesterBT tester )
{
log( "Results" );
if ( tester.getOutboundConnects() < 4 ){
log( " insufficient outbound connects for analysis" );
return;
}
int outgoing_seed_ok = 0;
int outgoing_leecher_ok = 0;
int outgoing_seed_bad = 0;
int outgoing_leecher_bad = 0;
int incoming_connect_ok = 0;
for (int i=0;i<sessions.size();i++){
NetStatusProtocolTesterBT.Session session = (NetStatusProtocolTesterBT.Session)sessions.get(i);
if ( session.isOK()){
if ( session.isInitiator()){
if ( session.isSeed()){
outgoing_seed_ok++;
}else{
outgoing_leecher_ok++;
}
}else{
incoming_connect_ok++;
}
}else{
if ( session.isConnected()){
if ( session.isInitiator()){
if ( session.isSeed()){
outgoing_seed_bad++;
}else{
outgoing_leecher_bad++;
}
}else{
incoming_connect_ok++;
}
}
}
log( " " +
( session.isInitiator()?"Outbound":"Inbound" ) + "," +
( session.isSeed()?"Seed":"Leecher") + "," +
session.getProtocolString());
}
if ( incoming_connect_ok == 0 ){
logError( " No incoming connections received, likely NAT problems" );
}
if ( outgoing_leecher_ok > 0 &&
outgoing_seed_ok == 0 &&
outgoing_seed_bad > 0 ){
logError( " Outgoing seed connects appear to be failing while non-seeds succeed" );
}
}
public void
sessionAdded(
NetStatusProtocolTesterBT.Session session )
{
synchronized( sessions ){
sessions.add( session );
}
}
public void
log(
String str )
{
NetStatusPluginTester.this.log( " " + str );
}
public void
logError(
String str )
{
NetStatusPluginTester.this.logError( " " + str );
}
public void
logError(
String str,
Throwable e )
{
NetStatusPluginTester.this.logError( " " + str, e );
}
});
while( !bt_test.waitForCompletion( 5000 )){
if ( isCancelled()){
bt_test.destroy();
break;
}
log( " Status: " + bt_test.getStatus());
}
}
}
protected void
addPublicAddress(
Set<InetAddress> addresses,
InetAddress address )
{
if ( address == null ){
return;
}
if ( address.isAnyLocalAddress() ||
address.isLoopbackAddress() ||
address.isLinkLocalAddress()||
address.isSiteLocalAddress()){
return;
}
addresses.add( address );
}
public void
cancel()
{
test_cancelled = true;
}
public boolean
isCancelled()
{
return( test_cancelled );
}
protected void
log(
String str )
{
logger.log( str );
}
protected void
logSuccess(
String str )
{
logger.logSuccess( str );
}
protected void
logInfo(
String str )
{
logger.logInfo( str );
}
protected void
log(
String str,
Throwable e )
{
logger.log( str + ": " + e.getLocalizedMessage());
}
protected void
logError(
String str )
{
logger.logFailure( str );
}
protected void
logError(
String str,
Throwable e )
{
logger.logFailure( str + ": " + e.getLocalizedMessage());
}
public interface
loggerProvider
{
public void
log(
String str );
public void
logSuccess(
String str );
public void
logInfo(
String str );
public void
logFailure(
String str );
}
}