/* * Created on 1 Nov 2006 * Created by Paul Gardner * Copyright (C) 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 63.529,40 euros * 8 Allee Lenotre, La Grille Royale, 78600 Le Mesnil le Roi, France. * */ package com.aelitis.azureus.core.networkmanager.admin.impl; import java.io.IOException; import java.io.PrintWriter; import java.net.*; import java.nio.channels.ServerSocketChannel; import java.nio.channels.UnsupportedAddressTypeException; import java.util.*; import java.util.regex.Pattern; import org.gudy.azureus2.core3.config.COConfigurationManager; import org.gudy.azureus2.core3.config.ParameterListener; import org.gudy.azureus2.core3.internat.MessageText; import org.gudy.azureus2.core3.logging.LogAlert; import org.gudy.azureus2.core3.logging.LogEvent; import org.gudy.azureus2.core3.logging.LogIDs; import org.gudy.azureus2.core3.logging.Logger; import org.gudy.azureus2.core3.util.*; import org.gudy.azureus2.platform.PlatformManager; import org.gudy.azureus2.platform.PlatformManagerCapabilities; import org.gudy.azureus2.platform.PlatformManagerFactory; import org.gudy.azureus2.platform.PlatformManagerPingCallback; import org.gudy.azureus2.plugins.PluginInterface; import org.gudy.azureus2.plugins.platform.PlatformManagerException; import org.gudy.azureus2.plugins.ui.UIManager; import org.gudy.azureus2.plugins.ui.UIManagerEvent; import org.gudy.azureus2.plugins.utils.StaticUtilities; import org.gudy.azureus2.plugins.utils.Utilities; import org.gudy.azureus2.pluginsimpl.local.PluginInitializer; import com.aelitis.azureus.core.AzureusCore; import com.aelitis.azureus.core.AzureusCoreFactory; import com.aelitis.azureus.core.AzureusCoreRunningListener; import com.aelitis.azureus.core.instancemanager.AZInstance; import com.aelitis.azureus.core.instancemanager.AZInstanceManager; import com.aelitis.azureus.core.instancemanager.AZInstanceManagerListener; import com.aelitis.azureus.core.instancemanager.AZInstanceTracked; import com.aelitis.azureus.core.networkmanager.NetworkConnectionBase; import com.aelitis.azureus.core.networkmanager.NetworkManager; import com.aelitis.azureus.core.networkmanager.Transport; import com.aelitis.azureus.core.networkmanager.TransportBase; import com.aelitis.azureus.core.networkmanager.TransportStartpoint; import com.aelitis.azureus.core.networkmanager.admin.*; import com.aelitis.azureus.core.networkmanager.impl.http.HTTPNetworkManager; import com.aelitis.azureus.core.networkmanager.impl.tcp.TCPNetworkManager; import com.aelitis.azureus.core.networkmanager.impl.udp.UDPNetworkManager; import com.aelitis.azureus.core.proxy.AEProxySelectorFactory; import com.aelitis.azureus.core.proxy.socks.AESocksProxy; import com.aelitis.azureus.core.proxy.socks.AESocksProxyFactory; import com.aelitis.azureus.core.util.CopyOnWriteList; import com.aelitis.azureus.core.util.NetUtils; import com.aelitis.azureus.plugins.upnp.UPnPPlugin; import com.aelitis.azureus.plugins.upnp.UPnPPluginService; public class NetworkAdminImpl extends NetworkAdmin implements AEDiagnosticsEvidenceGenerator { private static final LogIDs LOGID = LogIDs.NWMAN; private static final boolean FULL_INTF_PROBE = false; private static InetAddress anyLocalAddress; private static InetAddress anyLocalAddressIPv4; private static InetAddress anyLocalAddressIPv6; private static InetAddress localhostV4; private static InetAddress localhostV6; static { try { anyLocalAddressIPv4 = InetAddress.getByAddress(new byte[] { 0,0,0,0 }); anyLocalAddressIPv6 = InetAddress.getByAddress(new byte[] {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,0}); anyLocalAddress = new InetSocketAddress(0).getAddress(); localhostV4 = InetAddress.getByAddress(new byte[] {127,0,0,1}); localhostV6 = InetAddress.getByAddress(new byte[] {0,0,0,0, 0,0,0,0, 0,0,0,0, 0,0,0,1}); } catch (UnknownHostException e) { e.printStackTrace(); } } private static final int INTERFACE_CHECK_MILLIS = 15*1000; private static final int ROUTE_CHECK_MILLIS = 60*1000; private static final int ROUTE_CHECK_TICKS = ROUTE_CHECK_MILLIS / INTERFACE_CHECK_MILLIS; private Set<NetworkInterface> old_network_interfaces; private Map<String,AddressHistoryRecord> address_history = new HashMap<String,AddressHistoryRecord>(); private long address_history_update_time; private InetAddress[] currentBindIPs = new InetAddress[] { null }; private boolean forceBind = false; private boolean supportsIPv6withNIO = true; private boolean supportsIPv6 = true; private boolean supportsIPv4 = true; private boolean IPv6_enabled; { COConfigurationManager.addAndFireParameterListener( "IPV6 Enable Support", new ParameterListener() { public void parameterChanged( String parameterName ) { setIPv6Enabled( COConfigurationManager.getBooleanParameter("IPV6 Enable Support")); } }); COConfigurationManager.addResetToDefaultsListener( new COConfigurationManager.ResetToDefaultsListener() { public void reset() { clearMaybeVPNs(); } }); } private int roundRobinCounterV4 = 0; private int roundRobinCounterV6 = 0; private boolean logged_bind_force_issue; private CopyOnWriteList listeners = new CopyOnWriteList(); private NetworkAdminRouteListener trace_route_listener = new NetworkAdminRouteListener() { private int node_count = 0; public boolean foundNode( NetworkAdminNode node, int distance, int rtt ) { node_count++; return( true ); } public boolean timeout( int distance ) { if ( distance == 3 && node_count == 0 ){ return( false ); } return( true ); } }; private static final int ASN_MIN_CHECK = 30*60*1000; private long last_asn_lookup_time; private List asn_ips_checked = new ArrayList(0); private List as_history = new ArrayList(); private AsyncDispatcher async_asn_dispacher = new AsyncDispatcher(); private static final int MAX_ASYNC_ASN_LOOKUPS = 1024; private Map<InetAddress, NetworkAdminASN> async_asn_history = new LinkedHashMap<InetAddress, NetworkAdminASN>(256,0.75f,true) { protected boolean removeEldestEntry( Map.Entry<InetAddress, NetworkAdminASN> eldest) { return size() > 256; } }; private boolean initialised; public NetworkAdminImpl() { COConfigurationManager.addParameterListener( new String[] {"Bind IP","Enforce Bind IP"}, new ParameterListener() { public void parameterChanged( String parameterName ) { checkDefaultBindAddress( false ); } }); SimpleTimer.addPeriodicEvent( "NetworkAdmin:checker", INTERFACE_CHECK_MILLIS, new TimerEventPerformer() { private int tick_count; public void perform( TimerEvent event ) { tick_count++; boolean changed = checkNetworkInterfaces( false, false ); if ( changed || tick_count % ROUTE_CHECK_TICKS == 0 ){ checkConnectionRoutes(); } } }); // populate initial values checkNetworkInterfaces( true, true ); checkDefaultBindAddress( true ); AEDiagnostics.addEvidenceGenerator( this ); AzureusCoreFactory.addCoreRunningListener( new AzureusCoreRunningListener() { public void azureusCoreRunning( AzureusCore core ) { try{ Class.forName( "com.aelitis.azureus.core.networkmanager.admin.impl.swt.NetworkAdminSWTImpl" ).getConstructor( new Class[]{ AzureusCore.class, NetworkAdminImpl.class }).newInstance( new Object[]{ core, NetworkAdminImpl.this }); }catch( Throwable e ){ } } }); initialised = true; } protected void setIPv6Enabled( boolean enabled ) { IPv6_enabled = enabled; supportsIPv6withNIO = enabled; supportsIPv6 = enabled; if ( initialised ){ checkNetworkInterfaces( false, true ); checkDefaultBindAddress( false ); } } public boolean isIPV6Enabled() { return( IPv6_enabled ); } private List<NetworkInterface> last_getni_result; private Object getni_lock = new Object(); protected boolean checkNetworkInterfaces( boolean first_time, boolean force ) { boolean changed = false; try{ List<NetworkInterface> x = NetUtils.getNetworkInterfaces(); boolean fire_stuff = false; synchronized( getni_lock ){ if ( last_getni_result != x ){ last_getni_result = x; if ( x.size() == 0 && old_network_interfaces == null ){ }else if ( x.size() == 0 ){ old_network_interfaces = null; changed = true; }else if ( old_network_interfaces == null ){ Set<NetworkInterface> new_network_interfaces = new HashSet<NetworkInterface>(); new_network_interfaces.addAll( x ); old_network_interfaces = new_network_interfaces; changed = true; }else{ Set<NetworkInterface> new_network_interfaces = new HashSet<NetworkInterface>(); for ( NetworkInterface ni: x ){ // NetworkInterface's "equals" method is based on ni name + addresses if ( !old_network_interfaces.contains( ni )){ changed = true; } new_network_interfaces.add( ni ); } if ( old_network_interfaces.size() != new_network_interfaces.size()){ changed = true; } old_network_interfaces = new_network_interfaces; } if ( changed || force ){ boolean newV6 = false; boolean newV4 = false; Set<NetworkInterface> interfaces = old_network_interfaces; long now = SystemTime.getMonotonousTime(); List<AddressHistoryRecord> a_history = new ArrayList<AddressHistoryRecord>(); if (interfaces != null) { Iterator<NetworkInterface> it = interfaces.iterator(); while (it.hasNext()) { NetworkInterface ni = it.next(); Enumeration addresses = ni.getInetAddresses(); while (addresses.hasMoreElements()) { InetAddress ia = (InetAddress) addresses.nextElement(); a_history.add( new AddressHistoryRecord( ni, ia, now )); if (ia.isLoopbackAddress()){ continue; } if (ia instanceof Inet6Address && !ia.isLinkLocalAddress()){ if ( IPv6_enabled ){ newV6 = true; } }else if (ia instanceof Inet4Address){ newV4 = true; } } } } synchronized( address_history ){ address_history_update_time = now; for ( AddressHistoryRecord entry: a_history ){ String name = entry.getAddress().getHostAddress(); AddressHistoryRecord existing = address_history.get( name ); if ( existing == null ){ address_history.put( name, entry ); }else{ existing.setLastSeen( now ); } } Iterator<AddressHistoryRecord> it = address_history.values().iterator(); while( it.hasNext()){ AddressHistoryRecord entry = it.next(); long age = now - entry.getLastSeen(); if ( age > 10*60*1000 ){ it.remove(); } } } supportsIPv4 = newV4; supportsIPv6 = newV6; Logger.log(new LogEvent(LOGID, "NetworkAdmin: ipv4 supported: "+supportsIPv4+"; ipv6: "+supportsIPv6+"; probing v6+nio functionality")); if(newV6){ ServerSocketChannel channel = ServerSocketChannel.open(); try { channel.configureBlocking(false); channel.socket().bind(new InetSocketAddress(anyLocalAddressIPv6, 0)); Logger.log(new LogEvent(LOGID, "NetworkAdmin: testing nio + ipv6 bind successful")); supportsIPv6withNIO = true; } catch (Exception e) { Logger.log(new LogEvent(LOGID,LogEvent.LT_WARNING, "nio + ipv6 test failed",e)); supportsIPv6withNIO = false; } channel.close(); } else supportsIPv6withNIO = false; if ( !first_time ){ Logger.log( new LogEvent(LOGID, "NetworkAdmin: network interfaces have changed" )); } fire_stuff = true; } } } if ( fire_stuff ){ firePropertyChange( NetworkAdmin.PR_NETWORK_INTERFACES ); checkDefaultBindAddress( first_time ); } }catch( Throwable e ){ } return( changed ); } public InetAddress getMultiHomedOutgoingRoundRobinBindAddress(InetAddress target) { InetAddress[] addresses = currentBindIPs; boolean v6 = target instanceof Inet6Address; int previous = (v6 ? roundRobinCounterV6 : roundRobinCounterV4) % addresses.length; InetAddress toReturn = null; int i = previous; do { i++;i%= addresses.length; if (target == null || (v6 && addresses[i] instanceof Inet6Address) || (!v6 && addresses[i] instanceof Inet4Address)) { toReturn = addresses[i]; break; } else if(!v6 && addresses[i].isAnyLocalAddress()) { toReturn = anyLocalAddressIPv4; break; } } while(i!=previous); if(v6) roundRobinCounterV6 = i; else roundRobinCounterV4 = i; return toReturn != null ? toReturn : (v6 ? localhostV6 : localhostV4); } public InetAddress[] getMultiHomedServiceBindAddresses(boolean nio) { InetAddress[] bindIPs = currentBindIPs; for(int i=0;i<bindIPs.length;i++) { if(bindIPs[i].isAnyLocalAddress()) return new InetAddress[] {nio && !supportsIPv6withNIO && bindIPs[i] instanceof Inet6Address ? anyLocalAddressIPv4 : bindIPs[i]}; } return bindIPs; } public InetAddress getSingleHomedServiceBindAddress(int proto) { InetAddress[] addrs = currentBindIPs; if(proto == IP_PROTOCOL_VERSION_AUTO){ return addrs[0]; }else{ for( InetAddress addr: addrs ){ if( (proto == IP_PROTOCOL_VERSION_REQUIRE_V4 && addr instanceof Inet4Address || addr.isAnyLocalAddress()) || (proto == IP_PROTOCOL_VERSION_REQUIRE_V6 && addr instanceof Inet6Address) ){ if ( addr.isAnyLocalAddress()){ if ( proto == IP_PROTOCOL_VERSION_REQUIRE_V4 ){ return( anyLocalAddressIPv4 ); }else{ return( anyLocalAddressIPv6 ); } }else{ return( addr ); } } } } throw new UnsupportedAddressTypeException(); } public InetAddress[] getAllBindAddresses( boolean include_wildcard ) { if ( include_wildcard ){ return( currentBindIPs ); }else{ List<InetAddress> res = new ArrayList<InetAddress>(); InetAddress[] bind_ips = currentBindIPs; for ( InetAddress ip: bind_ips ){ if( !ip.isAnyLocalAddress()){ res.add( ip ); } } return( res.toArray( new InetAddress[ res.size()])); } } private InetAddress[] calcBindAddresses(final String addressString, boolean enforceBind) { ArrayList<InetAddress> addrs = new ArrayList<InetAddress>(); Pattern addressSplitter = Pattern.compile(";"); Pattern interfaceSplitter = Pattern.compile("[\\]\\[]"); String[] tokens = addressSplitter.split(addressString); addressLoop: for(int i=0;i<tokens.length;i++) { String currentAddress = tokens[i]; currentAddress = currentAddress.trim(); if ( currentAddress.length() == 0 ){ continue; } InetAddress parsedAddress = null; try { // literal ipv4 or ipv6 address if(currentAddress.indexOf('.') != -1 || currentAddress.indexOf(':') != -1) parsedAddress = InetAddress.getByName(currentAddress); } catch (Exception e) { // ignore, could be an interface name containing a ':' } if(parsedAddress != null) { try { // allow wildcard address as 1st address, otherwise only interface addresses if((!parsedAddress.isAnyLocalAddress() || addrs.size() > 0) && NetworkInterface.getByInetAddress(parsedAddress) == null) continue; } catch (SocketException e) { Debug.printStackTrace(e); continue; } addrs.add(parsedAddress); continue; } // interface name String[] ifaces = interfaceSplitter.split(currentAddress); NetworkInterface netInterface = null; try { netInterface = NetworkInterface.getByName(ifaces[0]); } catch (SocketException e) { e.printStackTrace(); // should not happen } if(netInterface == null) continue; Enumeration interfaceAddresses = netInterface.getInetAddresses(); if(ifaces.length != 2) while(interfaceAddresses.hasMoreElements()) addrs.add((InetAddress)interfaceAddresses.nextElement()); else { int selectedAddress = 0; try { selectedAddress = Integer.parseInt(ifaces[1]); } catch (NumberFormatException e) {} // ignore, user could by typing atm for(int j=0;interfaceAddresses.hasMoreElements();j++,interfaceAddresses.nextElement()) if(j==selectedAddress) { addrs.add((InetAddress)interfaceAddresses.nextElement()); continue addressLoop; } } } if ( !IPv6_enabled ){ Iterator<InetAddress> it = addrs.iterator(); while( it.hasNext()){ if ( it.next() instanceof Inet6Address ){ it.remove(); } } } if(addrs.size() < 1){ return new InetAddress[] {enforceBind ? localhostV4 : (hasIPV6Potential() ? anyLocalAddressIPv6 : anyLocalAddressIPv4)}; } return( addrs.toArray(new InetAddress[addrs.size()])); } private String checkBindAddresses( boolean log_alerts ) { Pattern addressSplitter = Pattern.compile(";"); Pattern interfaceSplitter = Pattern.compile("[\\]\\[]"); String bind_ips = COConfigurationManager.getStringParameter("Bind IP", "").trim(); boolean enforceBind = COConfigurationManager.getBooleanParameter("Enforce Bind IP"); if ( enforceBind && bind_ips.length() == 0 ){ if ( log_alerts ){ Logger.log( new LogAlert( true, LogAlert.AT_WARNING, "'Enforce IP Bindings' is selected but no bindings have been specified\n\nSee Tools->Options->Connection->Advanced Network Settings" )); } } String[] tokens = addressSplitter.split( bind_ips ); String failed_entries = ""; for ( int i=0;i<tokens.length;i++ ){ String currentAddress = tokens[i]; currentAddress = currentAddress.trim(); if ( currentAddress.length() == 0 ){ continue; } boolean ok = false; InetAddress parsedAddress = null; try{ if ( currentAddress.indexOf('.') != -1 || currentAddress.indexOf(':') != -1 ){ parsedAddress = InetAddress.getByName(currentAddress); } }catch ( Throwable e){ } if ( parsedAddress != null ){ try{ if ( parsedAddress.isAnyLocalAddress() || NetworkInterface.getByInetAddress( parsedAddress ) != null ){ ok = true; } }catch( Throwable e ){ } }else{ // interface name String[] ifaces = interfaceSplitter.split( currentAddress ); NetworkInterface netInterface = null; try{ netInterface = NetworkInterface.getByName( ifaces[0] ); }catch( Throwable e ){ } if ( netInterface != null ){ Enumeration interfaceAddresses = netInterface.getInetAddresses(); if ( ifaces.length != 2 ){ ok = interfaceAddresses.hasMoreElements(); }else{ try{ int selectedAddress = Integer.parseInt(ifaces[1]); for( int j=0; interfaceAddresses.hasMoreElements(); j++, interfaceAddresses.nextElement()){ if (j == selectedAddress ){ ok = true; break; } } }catch( Throwable e ){ } } } } if ( !ok ){ failed_entries += (failed_entries.length()==0?"":", " ) + currentAddress; } } if ( failed_entries.length() > 0 ){ if ( log_alerts ){ Logger.log( new LogAlert( true, LogAlert.AT_WARNING, "Bind IPs not resolved: " + failed_entries + "\n\nSee Tools->Options->Connection->Advanced Network Settings" )); } return( failed_entries ); } return( null ); } protected void checkDefaultBindAddress(boolean first_time) { boolean changed = false; String bind_ip = COConfigurationManager.getStringParameter("Bind IP", "").trim(); boolean enforceBind = COConfigurationManager.getBooleanParameter("Enforce Bind IP"); if ( enforceBind ){ if ( bind_ip.length() == 0 ){ if ( !logged_bind_force_issue ){ logged_bind_force_issue = true; Debug.out( "'Enforce IP Bindings' is selected but no bindings have been specified - ignoring force request!" ); } enforceBind = false; }else{ logged_bind_force_issue = false; } } forceBind = enforceBind; InetAddress[] addrs = calcBindAddresses(bind_ip, enforceBind); changed = !Arrays.equals(currentBindIPs, addrs); if(changed){ currentBindIPs = addrs; if (!first_time){ String logmsg = "NetworkAdmin: default bind ip has changed to '"; for(int i=0;i<addrs.length;i++) logmsg+=(addrs[i] == null ? "none" : addrs[i].getHostAddress()) + (i<addrs.length? ";" : ""); logmsg+="'"; Logger.log(new LogEvent(LOGID, logmsg)); // if the user has removed a previous bind enforcement then re-activate the maybe-vpn // logic as they may be switching VPN if ( bind_ip.length() == 0 ){ clearMaybeVPNs(); } } firePropertyChange(NetworkAdmin.PR_DEFAULT_BIND_ADDRESS); } } public String getNetworkInterfacesAsString() { Set interfaces = old_network_interfaces; if (interfaces == null){ return (""); } Iterator it = interfaces.iterator(); StringBuilder sb = new StringBuilder( 1024 ); while (it.hasNext()) { NetworkInterface ni = (NetworkInterface) it.next(); Enumeration addresses = ni.getInetAddresses(); sb.append( ni.getName()); sb.append( "\t(" ); sb.append( ni.getDisplayName()); sb.append( ")\n" ); int i = 0; while(addresses.hasMoreElements()){ InetAddress address = (InetAddress)addresses.nextElement(); sb.append( "\t" ); sb.append( ni.getName()); sb.append("[" ); sb.append( i++ ); sb.append( "]\t" ); sb.append((address).getHostAddress()); sb.append("\n"); } } return (sb.toString()); } public boolean hasIPV4Potential() { return supportsIPv4; } public boolean hasIPV6Potential(boolean nio) { return nio ? supportsIPv6withNIO : supportsIPv6; } public InetAddress[] getBindableAddresses() { return( getBindableAddresses( false, false )); } private InetAddress[] getBindableAddresses( boolean ignore_loopback, boolean ignore_link_local ) { List<InetAddress> bindable = new ArrayList<InetAddress>(); NetworkAdminNetworkInterface[] interfaces = NetworkAdmin.getSingleton().getInterfaces(); for ( NetworkAdminNetworkInterface intf: interfaces ){ NetworkAdminNetworkInterfaceAddress[] addresses = intf.getAddresses(); for ( NetworkAdminNetworkInterfaceAddress address: addresses ){ InetAddress a = address.getAddress(); if ( ( ignore_loopback && a.isLoopbackAddress()) || ( ignore_link_local && a.isLinkLocalAddress())){ }else{ if ( canBind( a )){ bindable.add( a ); } } } } return( bindable.toArray( new InetAddress[ bindable.size()])); } protected boolean canBind( InetAddress bind_ip ) { ServerSocketChannel ssc = null; try{ ssc = ServerSocketChannel.open(); ssc.socket().bind( new InetSocketAddress( bind_ip, 0 ), 16 ); return( true ); }catch( Throwable e ){ return( false ); }finally{ if ( ssc != null ){ try{ ssc.close(); }catch( Throwable e ){ Debug.out( e ); } } } } public int getBindablePort( int prefer_port ) throws IOException { final int tries = 1024; Random random = new Random(); for ( int i=1;i<=tries;i++ ){ int port; if ( i == 1 && prefer_port != 0 ){ port = prefer_port; }else{ port = i==tries?0:random.nextInt(20000) + 40000; } ServerSocketChannel ssc = null; try{ ssc = ServerSocketChannel.open(); ssc.socket().setReuseAddress( true ); bind( ssc, null, port ); port = ssc.socket().getLocalPort(); ssc.close(); return( port ); }catch( Throwable e ){ if ( ssc != null ){ try{ ssc.close(); }catch( Throwable f ){ Debug.printStackTrace(e); } ssc = null; } } } throw( new IOException( "No bindable ports found" )); } protected void bind( ServerSocketChannel ssc, InetAddress address, int port ) throws IOException { if ( address == null ){ ssc.socket().bind( new InetSocketAddress( port ), 1024 ); }else{ ssc.socket().bind( new InetSocketAddress( address, port ), 1024 ); } } public InetAddress guessRoutableBindAddress() { try{ // see if we have a choice List local_addresses = new ArrayList(); List non_local_addresses = new ArrayList(); try{ NetworkAdminNetworkInterface[] interfaces = getInterfaces(); List possible = new ArrayList(); for (int i=0;i<interfaces.length;i++){ NetworkAdminNetworkInterface intf = interfaces[i]; NetworkAdminNetworkInterfaceAddress[] addresses = intf.getAddresses(); for (int j=0;j<addresses.length;j++){ NetworkAdminNetworkInterfaceAddress address = addresses[j]; InetAddress ia = address.getAddress(); if ( ia.isLoopbackAddress()){ continue; } if ( ia.isLinkLocalAddress() || ia.isSiteLocalAddress()){ local_addresses.add( ia ); }else{ non_local_addresses.add( ia ); } if ( ( hasIPV4Potential() && ia instanceof Inet4Address ) || ( hasIPV6Potential() && ia instanceof Inet6Address )){ possible.add( ia ); } } } if ( possible.size() == 1 ){ return((InetAddress)possible.get(0)); } }catch( Throwable e ){ } // if we have a socks server then let's use a compatible address for it try{ NetworkAdminSocksProxy[] socks = getSocksProxies(); if ( socks.length > 0 ){ return( mapAddressToBindIP( InetAddress.getByName( socks[0].getHost()))); } }catch( Throwable e ){ } // next, same for nat devices try{ NetworkAdminNATDevice[] nat = getNATDevices(AzureusCoreFactory.getSingleton()); if ( nat.length > 0 ){ return( mapAddressToBindIP( nat[0].getAddress())); } }catch( Throwable e ){ } try{ final AESemaphore sem = new AESemaphore( "NA:conTest" ); final InetAddress[] can_connect = { null }; final int timeout = 10*1000; for (int i=0;i<local_addresses.size();i++){ final InetAddress address = (InetAddress)local_addresses.get(i); new AEThread2( "NA:conTest", true ) { public void run() { if ( canConnectWithBind( address, timeout )){ can_connect[0] = address; sem.release(); } } }.start(); } if ( sem.reserve( timeout )){ return( can_connect[0] ); } }catch( Throwable e ){ } // take a chance on any non local addresses we have if ( non_local_addresses.size() > 0 ){ return( guessAddress( non_local_addresses )); } // lastly, select local one at random if ( local_addresses.size() > 0 ){ return( guessAddress( local_addresses )); } // ho hum return( null ); }catch( Throwable e ){ Debug.printStackTrace(e); return( null ); } } protected boolean canConnectWithBind( InetAddress bind_address, int timeout ) { Socket socket = null; try{ socket = new Socket(); socket.bind( new InetSocketAddress( bind_address, 0 )); socket.setSoTimeout( timeout ); socket.connect( new InetSocketAddress( "www.google.com", 80 ), timeout ); return( true ); }catch( Throwable e ){ return( false ); }finally{ if ( socket != null ){ try{ socket.close(); }catch( Throwable f ){ } } } } protected InetAddress mapAddressToBindIP( InetAddress address ) { boolean[] address_bits = bytesToBits( address.getAddress()); NetworkAdminNetworkInterface[] interfaces = getInterfaces(); InetAddress best_bind_address = null; int best_prefix = 0; for (int i=0;i<interfaces.length;i++){ NetworkAdminNetworkInterface intf = interfaces[i]; NetworkAdminNetworkInterfaceAddress[] addresses = intf.getAddresses(); for (int j=0;j<addresses.length;j++){ NetworkAdminNetworkInterfaceAddress bind_address = addresses[j]; InetAddress ba = bind_address.getAddress(); byte[] bind_bytes = ba.getAddress(); if ( address_bits.length == bind_bytes.length ){ boolean[] bind_bits = bytesToBits( bind_bytes ); for (int k=0;k<bind_bits.length;k++){ if ( address_bits[k] != bind_bits[k] ){ break; } if ( k > best_prefix ){ best_prefix = k; best_bind_address = ba; } } } } } return( best_bind_address ); } protected boolean[] bytesToBits( byte[] bytes ) { boolean[] res = new boolean[bytes.length*8]; for (int i=0;i<bytes.length;i++){ byte b = bytes[i]; for (int j=0;j<8;j++){ res[i*8+j] = (b&(byte)(0x01<<(7-j))) != 0; } } return( res ); } protected InetAddress guessAddress( List addresses ) { // prioritise 192.168.0.* and 192.168.1.* as common // then ipv4 over ipv6 for (int i=0;i<addresses.size();i++){ InetAddress address = (InetAddress)addresses.get(i); String str = address.getHostAddress(); if ( str.startsWith( "192.168.0." ) || str.startsWith( "192.168.1." )){ return( address ); } } for (int i=0;i<addresses.size();i++){ InetAddress address = (InetAddress)addresses.get(i); if ( address instanceof Inet4Address ){ return( address ); } } for (int i=0;i<addresses.size();i++){ InetAddress address = (InetAddress)addresses.get(i); if ( address instanceof Inet6Address ){ return( address ); } } if ( addresses.size() > 0 ){ return((InetAddress)addresses.get(0)); } return( null ); } private static InetAddress[] gdpa_lock = { null }; private static AESemaphore gdpa_sem; private static long gdpa_last_fail; private static long gdpa_last_lookup; private static AESemaphore gdpa_initial_sem = new AESemaphore( "gdpa:init" ); public InetAddress getDefaultPublicAddress() { return( getDefaultPublicAddress( false )); } public InetAddress getDefaultPublicAddress( boolean peek ) { final AESemaphore sem; synchronized( gdpa_lock ){ long now = SystemTime.getMonotonousTime(); if ( gdpa_sem == null ){ boolean do_lookup = true; if ( peek ){ if ( gdpa_last_lookup != 0 && now - gdpa_last_lookup <= 60*1000 ){ do_lookup = false; } } if ( do_lookup ){ gdpa_last_lookup = now; gdpa_sem = sem = new AESemaphore( "getDefaultPublicAddress"); new AEThread2( "getDefaultPublicAddress" ) { public void run() { InetAddress address = null; try{ Utilities utils = PluginInitializer.getDefaultInterface().getUtilities(); address = utils.getPublicAddress(); if ( address == null ){ address = utils.getPublicAddress( true ); } }catch( Throwable e ){ }finally{ synchronized( gdpa_lock ){ gdpa_lock[0] = address; sem.releaseForever(); gdpa_sem = null; gdpa_initial_sem.releaseForever(); } } } }.start(); }else{ sem = null; // no lookup this time around - we're peeking } }else{ sem = gdpa_sem; } if ( gdpa_last_fail != 0 && SystemTime.getMonotonousTime() - gdpa_last_fail < 5*60*1000 ){ return( gdpa_lock[0] ); } } if ( peek ){ // we're doing a peek and don't want to wait gdpa_initial_sem.reserve( 10*1000 ); synchronized( gdpa_lock ){ return( gdpa_lock[0] ); } }else{ // in case things block - yes, they can do :( boolean worked = sem.reserve( 10*1000 ); synchronized( gdpa_lock ){ if ( worked ){ gdpa_last_fail = 0; }else{ gdpa_initial_sem.releaseForever(); gdpa_last_fail = SystemTime.getMonotonousTime(); } return( gdpa_lock[0] ); } } } @Override public InetAddress getDefaultPublicAddressV6() { return( getDefaultPublicAddressV6( false )); } @Override public InetAddress getDefaultPublicAddressV6( boolean peek ) { if(!supportsIPv6) return null; // check bindings first for(InetAddress addr : currentBindIPs) { // found a specific bind address, use that one if(AddressUtils.isGlobalAddressV6(addr)){ return addr; } } for(InetAddress addr : currentBindIPs){ // found v6 any-local address, check interfaces for a best match if(addr instanceof Inet6Address && addr.isAnyLocalAddress()) { ArrayList<InetAddress> addrs = new ArrayList<InetAddress>(); for(NetworkInterface iface : old_network_interfaces) addrs.addAll(Collections.list(iface.getInetAddresses())); return AddressUtils.pickBestGlobalV6Address(addrs); } } return null; } public boolean hasDHTIPV6() { if ( hasIPV6Potential(false)){ InetAddress v6 = getDefaultPublicAddressV6(); if ( v6 == null ){ return( false ); } if ( Constants.IS_CVS_VERSION ){ return( true ); }else{ return( !AddressUtils.isTeredo( v6 )); } } return( false ); } protected void firePropertyChange( String property ) { Iterator it = listeners.iterator(); while( it.hasNext()){ try{ ((NetworkAdminPropertyChangeListener)it.next()).propertyChanged( property ); }catch( Throwable e ){ Debug.printStackTrace(e); } } } public NetworkAdminNetworkInterface[] getInterfaces() { Set interfaces = old_network_interfaces; if ( interfaces == null ){ return( new NetworkAdminNetworkInterface[0] ); } NetworkAdminNetworkInterface[] res = new NetworkAdminNetworkInterface[interfaces.size()]; Iterator it = interfaces.iterator(); int pos = 0; while( it.hasNext()){ NetworkInterface ni = (NetworkInterface)it.next(); res[pos++] = new networkInterface( ni ); } return( res ); } public NetworkAdminProtocol[] getOutboundProtocols( AzureusCore azureus_core) { NetworkAdminProtocol[] res = { new NetworkAdminProtocolImpl( azureus_core, NetworkAdminProtocol.PT_HTTP ), new NetworkAdminProtocolImpl( azureus_core, NetworkAdminProtocol.PT_TCP ), new NetworkAdminProtocolImpl( azureus_core, NetworkAdminProtocol.PT_UDP ), }; return( res ); } public NetworkAdminProtocol createInboundProtocol( AzureusCore azureus_core, int type, int port ) { return( new NetworkAdminProtocolImpl( azureus_core, type, port )); } public NetworkAdminProtocol[] getInboundProtocols( AzureusCore azureus_core) { List protocols = new ArrayList(); TCPNetworkManager tcp_manager = TCPNetworkManager.getSingleton(); if ( tcp_manager.isTCPListenerEnabled()){ protocols.add( new NetworkAdminProtocolImpl( azureus_core, NetworkAdminProtocol.PT_TCP, tcp_manager.getTCPListeningPortNumber())); } UDPNetworkManager udp_manager = UDPNetworkManager.getSingleton(); int done_udp = -1; if ( udp_manager.isUDPListenerEnabled()){ protocols.add( new NetworkAdminProtocolImpl( azureus_core, NetworkAdminProtocol.PT_UDP, done_udp = udp_manager.getUDPListeningPortNumber())); } if ( udp_manager.isUDPNonDataListenerEnabled()){ int port = udp_manager.getUDPNonDataListeningPortNumber(); if ( port != done_udp ){ protocols.add( new NetworkAdminProtocolImpl( azureus_core, NetworkAdminProtocol.PT_UDP, done_udp = udp_manager.getUDPNonDataListeningPortNumber())); } } HTTPNetworkManager http_manager = HTTPNetworkManager.getSingleton(); if ( http_manager.isHTTPListenerEnabled()){ protocols.add( new NetworkAdminProtocolImpl( azureus_core, NetworkAdminProtocol.PT_HTTP, http_manager.getHTTPListeningPortNumber())); } return((NetworkAdminProtocol[])protocols.toArray( new NetworkAdminProtocol[protocols.size()])); } public InetAddress testProtocol( NetworkAdminProtocol protocol ) throws NetworkAdminException { return( protocol.test( null )); } @Override public boolean isSocksActive() { Proxy proxy = AEProxySelectorFactory.getSelector().getActiveProxy(); return( proxy != null && proxy.type() == Proxy.Type.SOCKS ); } public NetworkAdminSocksProxy createSocksProxy( String host, int port, String username, String password ) { return( new NetworkAdminSocksProxyImpl( host, ""+port, username, password )); } public NetworkAdminSocksProxy[] getSocksProxies() { String host = System.getProperty( "socksProxyHost", "" ).trim(); String port = System.getProperty( "socksProxyPort", "" ).trim(); String user = System.getProperty("java.net.socks.username", "" ).trim(); String password = System.getProperty("java.net.socks.password", "").trim(); List res = new ArrayList(); NetworkAdminSocksProxyImpl p1 = new NetworkAdminSocksProxyImpl( host, port, user, password ); if ( p1.isConfigured()){ res.add( p1 ); } if ( COConfigurationManager.getBooleanParameter( "Proxy.Data.Enable" ) && !COConfigurationManager.getBooleanParameter( "Proxy.Data.Same" )){ host = COConfigurationManager.getStringParameter( "Proxy.Data.Host" ); port = COConfigurationManager.getStringParameter( "Proxy.Data.Port" ); user = COConfigurationManager.getStringParameter( "Proxy.Data.Username" ); if ( user.trim().equalsIgnoreCase("<none>")){ user = ""; } password = COConfigurationManager.getStringParameter( "Proxy.Data.Password" ); NetworkAdminSocksProxyImpl p2 = new NetworkAdminSocksProxyImpl( host, port, user, password ); if ( p2.isConfigured()){ res.add( p2 ); } } return((NetworkAdminSocksProxy[])res.toArray(new NetworkAdminSocksProxy[res.size()])); } public NetworkAdminHTTPProxy getHTTPProxy() { NetworkAdminHTTPProxyImpl res = new NetworkAdminHTTPProxyImpl(); if ( !res.isConfigured()){ res = null; } return( res ); } public NetworkAdminNATDevice[] getNATDevices( AzureusCore azureus_core ) { List<NetworkAdminNATDeviceImpl> devices = new ArrayList<NetworkAdminNATDeviceImpl>(); try{ PluginInterface upnp_pi = azureus_core.getPluginManager().getPluginInterfaceByClass( UPnPPlugin.class ); if ( upnp_pi != null ){ UPnPPlugin upnp = (UPnPPlugin)upnp_pi.getPlugin(); UPnPPluginService[] services = upnp.getServices(); for ( UPnPPluginService service: services ){ NetworkAdminNATDeviceImpl dev = new NetworkAdminNATDeviceImpl( service ); boolean same = false; for ( NetworkAdminNATDeviceImpl d: devices ){ if ( d.sameAs( dev )){ same = true; break; } } if ( !same ){ devices.add( dev ); } } } }catch( Throwable e ){ Debug.printStackTrace( e ); } return((NetworkAdminNATDevice[])devices.toArray(new NetworkAdminNATDevice[devices.size()])); } public NetworkAdminASN getCurrentASN() { List asns = COConfigurationManager.getListParameter( "ASN Details", new ArrayList()); if ( asns.size() == 0 ){ // migration from when we only persisted a single AS String as = ""; String asn = ""; String bgp = ""; try{ as = COConfigurationManager.getStringParameter( "ASN AS" ); asn = COConfigurationManager.getStringParameter( "ASN ASN" ); bgp = COConfigurationManager.getStringParameter( "ASN BGP" ); }catch( Throwable e ){ Debug.printStackTrace(e); } COConfigurationManager.removeParameter( "ASN AS" ); COConfigurationManager.removeParameter( "ASN ASN" ); COConfigurationManager.removeParameter( "ASN BGP" ); COConfigurationManager.removeParameter( "ASN Autocheck Performed Time" ); asns.add(ASNToMap(new NetworkAdminASNImpl(as, asn, bgp ))); COConfigurationManager.setParameter( "ASN Details", asns ); } if ( asns.size() > 0 ){ Map m = (Map)asns.get(0); return( ASNFromMap( m )); } return( new NetworkAdminASNImpl( "", "", "" )); } protected Map ASNToMap( NetworkAdminASNImpl x ) { Map m = new HashMap(); byte[] as = new byte[0]; byte[] asn = new byte[0]; byte[] bgp = new byte[0]; try{ as = x.getAS().getBytes("UTF-8"); asn = x.getASName().getBytes("UTF-8"); bgp = x.getBGPPrefix().getBytes("UTF-8"); }catch( Throwable e ){ Debug.printStackTrace(e); } m.put( "as", as ); m.put( "name", asn ); m.put( "bgp", bgp ); return( m ); } protected NetworkAdminASNImpl ASNFromMap( Map m ) { String as = ""; String asn = ""; String bgp = ""; try{ as = new String((byte[])m.get("as"),"UTF-8"); asn = new String((byte[])m.get("name"),"UTF-8"); bgp = new String((byte[])m.get("bgp"),"UTF-8"); }catch( Throwable e ){ Debug.printStackTrace(e); } return( new NetworkAdminASNImpl( as, asn, bgp )); } public NetworkAdminASN lookupCurrentASN( InetAddress address ) throws NetworkAdminException { NetworkAdminASN current = getCurrentASN(); if ( current.matchesCIDR( address )){ return( current ); } List asns = COConfigurationManager.getListParameter( "ASN Details", new ArrayList()); for (int i=0;i<asns.size();i++){ Map m = (Map)asns.get(i); NetworkAdminASN x = ASNFromMap( m ); if ( x.matchesCIDR( address )){ asns.remove(i); asns.add( 0, m ); firePropertyChange( PR_AS ); return( x ); } } if ( asn_ips_checked.contains( address )){ return( current ); } long now = SystemTime.getCurrentTime(); if ( now < last_asn_lookup_time || now - last_asn_lookup_time > ASN_MIN_CHECK ){ last_asn_lookup_time = now; NetworkAdminASNLookupImpl lookup = new NetworkAdminASNLookupImpl( address ); NetworkAdminASNImpl x = lookup.lookup(); asn_ips_checked.add( address ); asns.add( 0, ASNToMap( x )); firePropertyChange( PR_AS ); return( x ); } return( current ); } public NetworkAdminASN lookupASN( InetAddress address ) throws NetworkAdminException { NetworkAdminASN existing = getFromASHistory( address ); if ( existing != null ){ return( existing ); } NetworkAdminASNLookupImpl lookup = new NetworkAdminASNLookupImpl( address ); NetworkAdminASNImpl result = lookup.lookup(); addToASHistory( result ); return( result ); } public void lookupASN( final InetAddress address, final NetworkAdminASNListener listener ) { synchronized( async_asn_history ){ NetworkAdminASN existing = async_asn_history.get( address ); if ( existing != null ){ listener.success( existing ); } } int queue_size = async_asn_dispacher.getQueueSize(); if ( queue_size >= MAX_ASYNC_ASN_LOOKUPS ){ listener.failed( new NetworkAdminException( "Too many outstanding lookups" )); }else{ async_asn_dispacher.dispatch( new AERunnable() { public void runSupport() { synchronized( async_asn_history ){ NetworkAdminASN existing = async_asn_history.get( address ); if ( existing != null ){ listener.success( existing ); return; } } try{ NetworkAdminASNLookupImpl lookup = new NetworkAdminASNLookupImpl( address ); NetworkAdminASNImpl result = lookup.lookup(); synchronized( async_asn_history ){ async_asn_history.put( address, result ); } listener.success( result ); }catch( NetworkAdminException e ){ listener.failed( e ); }catch( Throwable e ){ listener.failed( new NetworkAdminException( "lookup failed", e )); } } }); } } protected void addToASHistory( NetworkAdminASN asn ) { synchronized( as_history ){ boolean found = false; for (int i=0;i<as_history.size();i++){ NetworkAdminASN x = (NetworkAdminASN)as_history.get(i); if ( asn.getAS().equals( x.getAS())){ found = true; break; } } if ( !found ){ as_history.add( asn ); if ( as_history.size() > 256 ){ as_history.remove(0); } } } } protected NetworkAdminASN getFromASHistory( InetAddress address ) { synchronized( as_history ){ for (int i=0;i<as_history.size();i++){ NetworkAdminASN x = (NetworkAdminASN)as_history.get(i); if ( x.matchesCIDR( address )){ return( x ); } } } return( null ); } public void runInitialChecks( AzureusCore azureus_core ) { AZInstanceManager i_man = azureus_core.getInstanceManager(); final AZInstance my_instance = i_man.getMyInstance(); i_man.addListener( new AZInstanceManagerListener() { private InetAddress external_address; public void instanceFound( AZInstance instance ) { } public void instanceChanged( AZInstance instance ) { if ( instance == my_instance ){ InetAddress address = instance.getExternalAddress(); if ( external_address == null || !external_address.equals( address )){ external_address = address; try{ lookupCurrentASN( address ); }catch( Throwable e ){ Debug.printStackTrace(e); } } } } public void instanceLost( AZInstance instance ) { } public void instanceTracked( AZInstanceTracked instance ) { } }); if ( COConfigurationManager.getBooleanParameter( "Proxy.Check.On.Start" )){ NetworkAdminSocksProxy[] socks = getSocksProxies(); for (int i=0;i<socks.length;i++){ NetworkAdminSocksProxy sock = socks[i]; try{ sock.getVersionsSupported(); }catch( Throwable e ){ Debug.printStackTrace( e ); Logger.log( new LogAlert( true, LogAlert.AT_WARNING, "Socks proxy " + sock.getName() + " check failed: " + Debug.getNestedExceptionMessage( e ))); } } NetworkAdminHTTPProxy http_proxy = getHTTPProxy(); if ( http_proxy != null ){ try{ http_proxy.getDetails(); }catch( Throwable e ){ Debug.printStackTrace( e ); Logger.log( new LogAlert( true, LogAlert.AT_WARNING, "HTTP proxy " + http_proxy.getName() + " check failed: " + Debug.getNestedExceptionMessage( e ))); } } } if ( COConfigurationManager.getBooleanParameter( "Check Bind IP On Start" )){ checkBindAddresses( true ); } NetworkAdminSpeedTestScheduler nast = NetworkAdminSpeedTestSchedulerImpl.getInstance(); nast.initialise(); } public boolean canTraceRoute() { PlatformManager pm = PlatformManagerFactory.getPlatformManager(); return( pm.hasCapability( PlatformManagerCapabilities.TraceRouteAvailability )); } public NetworkAdminNode[] getRoute( InetAddress interface_address, InetAddress target, final int max_millis, final NetworkAdminRouteListener listener ) throws NetworkAdminException { PlatformManager pm = PlatformManagerFactory.getPlatformManager(); if ( !canTraceRoute()){ throw( new NetworkAdminException( "No trace-route capability on platform" )); } final List nodes = new ArrayList(); try{ pm.traceRoute( interface_address, target, new PlatformManagerPingCallback() { private long start_time = SystemTime.getCurrentTime(); public boolean reportNode( int distance, InetAddress address, int millis ) { boolean timeout = false; if ( max_millis >= 0 ){ long now = SystemTime.getCurrentTime(); if ( now < start_time ){ start_time = now; } if ( now - start_time >= max_millis ){ timeout = true; } } NetworkAdminNode node = null; if ( address != null ){ node = new networkNode( address, distance, millis ); nodes.add( node ); } boolean result; if ( listener == null ){ result = true; }else{ if ( node == null ){ result = listener.timeout( distance ); }else{ result = listener.foundNode( node, distance, millis ); } } return( result && !timeout ); } }); }catch( PlatformManagerException e ){ throw( new NetworkAdminException( "trace-route failed", e )); } return((NetworkAdminNode[])nodes.toArray( new NetworkAdminNode[nodes.size()])); } public boolean canPing() { PlatformManager pm = PlatformManagerFactory.getPlatformManager(); return( pm.hasCapability( PlatformManagerCapabilities.PingAvailability )); } public NetworkAdminNode pingTarget( InetAddress interface_address, InetAddress target, final int max_millis, final NetworkAdminRouteListener listener ) throws NetworkAdminException { PlatformManager pm = PlatformManagerFactory.getPlatformManager(); if ( !canPing()){ throw( new NetworkAdminException( "No ping capability on platform" )); } final NetworkAdminNode[] nodes = { null }; try{ pm.ping( interface_address, target, new PlatformManagerPingCallback() { private long start_time = SystemTime.getCurrentTime(); public boolean reportNode( int distance, InetAddress address, int millis ) { boolean timeout = false; if ( max_millis >= 0 ){ long now = SystemTime.getCurrentTime(); if ( now < start_time ){ start_time = now; } if ( now - start_time >= max_millis ){ timeout = true; } } NetworkAdminNode node = null; if ( address != null ){ node = new networkNode( address, distance, millis ); nodes[0] = node; } boolean result; if ( listener == null ){ result = false; }else{ if ( node == null ){ result = listener.timeout( distance ); }else{ result = listener.foundNode( node, distance, millis ); } } return( result && !timeout ); } }); }catch( PlatformManagerException e ){ throw( new NetworkAdminException( "ping failed", e )); } return( nodes[0] ); } public void getRoutes( final InetAddress target, final int max_millis, final NetworkAdminRoutesListener listener ) throws NetworkAdminException { final List sems = new ArrayList(); final List traces = new ArrayList(); NetworkAdminNetworkInterface[] interfaces = getInterfaces(); for (int i=0;i<interfaces.length;i++){ NetworkAdminNetworkInterface interf = (NetworkAdminNetworkInterface)interfaces[i]; NetworkAdminNetworkInterfaceAddress[] addresses = interf.getAddresses(); for (int j=0;j<addresses.length;j++){ final NetworkAdminNetworkInterfaceAddress address = addresses[j]; InetAddress ia = address.getAddress(); if ( ia.isLoopbackAddress() || ia instanceof Inet6Address ){ // ignore }else{ final AESemaphore sem = new AESemaphore( "parallelRouter" ); final List trace = new ArrayList(); sems.add( sem ); traces.add( trace ); new AEThread2( "parallelRouter", true ) { public void run() { try{ address.getRoute( target, 30000, new NetworkAdminRouteListener() { public boolean foundNode( NetworkAdminNode node, int distance, int rtt ) { trace.add( node ); NetworkAdminNode[] route = new NetworkAdminNode[trace.size()]; trace.toArray( route ); return( listener.foundNode( address, route, distance, rtt) ); } public boolean timeout( int distance ) { NetworkAdminNode[] route = new NetworkAdminNode[trace.size()]; trace.toArray( route ); return( listener.timeout( address, route, distance )); } }); }catch( Throwable e ){ e.printStackTrace(); }finally{ sem.release(); } } }.start(); } } } for (int i=0;i<sems.size();i++){ ((AESemaphore)sems.get(i)).reserve(); } } public void pingTargets( final InetAddress target, final int max_millis, final NetworkAdminRoutesListener listener ) throws NetworkAdminException { final List sems = new ArrayList(); final List traces = new ArrayList(); NetworkAdminNetworkInterface[] interfaces = getInterfaces(); for (int i=0;i<interfaces.length;i++){ NetworkAdminNetworkInterface interf = (NetworkAdminNetworkInterface)interfaces[i]; NetworkAdminNetworkInterfaceAddress[] addresses = interf.getAddresses(); for (int j=0;j<addresses.length;j++){ final NetworkAdminNetworkInterfaceAddress address = addresses[j]; InetAddress ia = address.getAddress(); if ( ia.isLoopbackAddress() || ia instanceof Inet6Address ){ // ignore }else{ final AESemaphore sem = new AESemaphore( "parallelPinger" ); final List trace = new ArrayList(); sems.add( sem ); traces.add( trace ); new AEThread2( "parallelPinger", true ) { public void run() { try{ address.pingTarget( target, 30000, new NetworkAdminRouteListener() { public boolean foundNode( NetworkAdminNode node, int distance, int rtt ) { trace.add( node ); NetworkAdminNode[] route = new NetworkAdminNode[trace.size()]; trace.toArray( route ); return( listener.foundNode( address, route, distance, rtt) ); } public boolean timeout( int distance ) { NetworkAdminNode[] route = new NetworkAdminNode[trace.size()]; trace.toArray( route ); return( listener.timeout( address, route, distance )); } }); }catch( Throwable e ){ e.printStackTrace(); }finally{ sem.release(); } } }.start(); } } } for (int i=0;i<sems.size();i++){ ((AESemaphore)sems.get(i)).reserve(); } } @Override public boolean mustBind() { return( forceBind ); } public boolean hasMissingForcedBind() { Object[] status = getBindingStatus(); return( (Integer)status[0] == BS_ERROR ); } // for the icon public static final int BS_INACTIVE = 0; public static final int BS_OK = 1; public static final int BS_WARNING = 2; public static final int BS_ERROR = 3; private long bs_last_calc = 0; private Object[] bs_last_value = null; public Object[] getBindingStatus() { long now = SystemTime.getMonotonousTime(); if ( bs_last_value != null && now - bs_last_calc < 30*1000 ){ return( bs_last_value ); } String bind_ips = COConfigurationManager.getStringParameter("Bind IP", "").trim(); if ( bind_ips.length() == 0 ){ return( new Object[]{ BS_INACTIVE, "" }); } boolean enforceBind = COConfigurationManager.getBooleanParameter( "Enforce Bind IP" ); String missing = checkBindAddresses( false ); InetAddress[] binds = getAllBindAddresses( false ); List<InetAddress> bindable = new ArrayList<InetAddress>(); List<InetAddress> unbindable = new ArrayList<InetAddress>(); for ( InetAddress b: binds ){ if ( canBind( b )){ bindable.add( b ); }else{ if ( !( b.isLoopbackAddress() || b.isLinkLocalAddress())){ unbindable.add( b ); } } } Set<NetworkConnectionBase> connections = NetworkManager.getSingleton().getConnections(); Map<InetAddress,int[]> lookup_map = new HashMap<InetAddress, int[]>(); for ( NetworkConnectionBase connection: connections ){ TransportBase tb = connection.getTransportBase(); if ( tb instanceof Transport ){ Transport transport = (Transport)tb; TransportStartpoint start = transport.getTransportStartpoint(); if ( start != null ){ InetSocketAddress socket_address = start.getProtocolStartpoint().getAddress(); if ( socket_address != null ){ InetAddress address = socket_address.getAddress(); int[] counts = lookup_map.get( address ); if ( counts == null ){ counts = new int[2]; lookup_map.put( address, counts ); } if ( connection.isIncoming()){ counts[0]++; }else{ counts[1]++; } } } } } int status = BS_OK; if ( unbindable.size() > 0 || missing != null ){ status = enforceBind?BS_ERROR:BS_WARNING; } String str = MessageText.getString( "network.admin.binding.state", new String[]{ getString( bindable ), MessageText.getString( enforceBind?"GeneralView.yes":"GeneralView.no" ).toLowerCase()}); if ( unbindable.size() > 0 ){ str += "\nUnbindable: " + getString( unbindable ); } if ( missing != null ){ str += "\n" + MessageText.getString( "label.missing" ) + ": " + missing; } boolean unbound_connections = false; if ( lookup_map.size() == 0 ){ str += "\n" + MessageText.getString( "label.no.connections" ); }else{ String con_str = ""; for ( Map.Entry<InetAddress,int[]> entry: lookup_map.entrySet()){ InetAddress address = entry.getKey(); int[] counts = entry.getValue(); String s; if ( address.isAnyLocalAddress()){ s = "*"; unbound_connections = true; }else{ s = address.getHostAddress(); if ( !bindable.contains( address )){ unbound_connections = true; } } s += " - " + MessageText.getString( "label.in" ) + "=" + counts[0] + ", " + MessageText.getString( "label.out" ) + "=" + counts[1]; con_str += (con_str.length()==0?"":"; ") + s.toLowerCase(); } str += "\n" + MessageText.getString( "label.connections" ) + ": " + con_str; } if ( unbound_connections ){ status = enforceBind?BS_ERROR:BS_WARNING; } bs_last_value = new Object[]{ status, str }; bs_last_calc = now; return( bs_last_value ); } private String getString( List<InetAddress> addresses ) { if ( addresses.size() == 0 ){ return( "<none>" ); } String str = ""; for ( InetAddress address: addresses ){ str += (str.length()==0?"":", ") + address.getHostAddress(); } return( str ); } private void checkConnectionRoutes() { // System.out.println( "Checking connection routes" ); if ( getAllBindAddresses( false ).length > 0 ){ // System.out.println( " Bind IP found" ); return; } Set<NetworkConnectionBase> connections = NetworkManager.getSingleton().getConnections(); // check if all outgoing TCP connections are being routed through the same interface boolean found_wildcard = false; int tcp_found = 0; Map<InetAddress,Object[]> lookup_map = new HashMap<InetAddress, Object[]>(); Map<String,Object[]> bind_map = new HashMap<String, Object[]>(); for ( NetworkConnectionBase connection: connections ){ if ( !connection.isIncoming()){ TransportBase tb = connection.getTransportBase(); if ( tb instanceof Transport ){ Transport transport = (Transport)tb; if ( transport.isTCP()){ TransportStartpoint start = transport.getTransportStartpoint(); if ( start != null ){ InetSocketAddress socket_address = start.getProtocolStartpoint().getAddress(); if ( socket_address != null ){ tcp_found++; InetAddress address = socket_address.getAddress(); if ( address.isAnyLocalAddress()){ found_wildcard = true; }else{ Object[] details = lookup_map.get( address ); if ( details == null ){ if ( !lookup_map.containsKey( address )){ details = getInterfaceForAddress( address ); lookup_map.put( address, details ); } } if ( details != null && details[0] instanceof NetworkInterface ){ NetworkInterface intf = (NetworkInterface)details[0]; InetAddress intf_address = details.length==1?null:(InetAddress)details[1]; String key = intf.getName() + "/" + intf_address; Object[] entry = bind_map.get( key ); if ( entry == null ){ entry = new Object[]{ new int[1], details }; bind_map.put( key, entry ); } ((int[])entry[0])[0]++; } } } } } } } } if ( tcp_found > 8 ){ if ( found_wildcard && bind_map.size() == 0 ){ // unfortunately we don't always get access to the locally bound // interfaces, so for the case where we have nothing useful look // to see if there are more than one interface and if one looks like // a vpn. I did try an explicit 'connect with bind' to see if I could // work out if just one was routing but unfortunately this didn't // work (with open-vpn at least) as it still permits explicit connections // through non-vpn interface - grrr! // System.out.println( "Everything wildcard" ); InetAddress[] bindable_addresses = getBindableAddresses( true, true ); // System.out.println( "Bindable=" + bindable_addresses.length ); if ( bindable_addresses.length > 1 ){ Map<String, NetworkInterface> intf_map = new HashMap<String, NetworkInterface>(); for ( InetAddress address: bindable_addresses ){ Object[] details = getInterfaceForAddress( address ); if ( details != null && details[0] instanceof NetworkInterface ){ NetworkInterface intf = (NetworkInterface)details[0]; intf_map.put( intf.getName(), intf ); } } if ( intf_map.size() > 1 ){ int eth_like = 0; Map<String,NetworkInterface> vpn_like = new HashMap<String, NetworkInterface>(); for ( Map.Entry<String,NetworkInterface> entry: intf_map.entrySet()){ int type = categoriseIntf( entry.getValue()); if ( type == 1 ){ eth_like++; }else if ( type == 2 ){ vpn_like.put( entry.getKey(), entry.getValue()); } } if ( vpn_like.size() == 1 && eth_like > 0 ){ maybeVPN( vpn_like.values().iterator().next()); } } } }else if ( !found_wildcard && bind_map.size() == 1 ){ Object[] bound_details = (Object[])bind_map.values().iterator().next()[1]; NetworkInterface bound_intf = (NetworkInterface)bound_details[0]; // System.out.println( "All outgoing TCP bound to same: " + bound_intf ); //maybeVPN( bound_intf ); int bound_type = categoriseIntf( bound_intf ); if ( bound_type == 2 ){ if ( !maybeVPNDone( bound_intf )){ InetAddress[] bindable_addresses = getBindableAddresses( true, true ); if ( bindable_addresses.length > 1 ){ int eth_like = 0; int vpn_like = 0; for ( InetAddress address: bindable_addresses ){ Object[] details = getInterfaceForAddress( address ); if ( details != null && details[0] instanceof NetworkInterface ){ NetworkInterface intf = (NetworkInterface)details[0]; if ( intf != bound_intf ){ int type = categoriseIntf( intf); if ( type == 1 ){ eth_like++; }else if ( type == 2 ){ vpn_like++; } } } } if ( vpn_like == 0 && eth_like > 0 ){ maybeVPN( bound_intf ); } } } } } } } private void clearMaybeVPNs() { Set<String> keys = COConfigurationManager.getDefinedParameters(); for ( String key: keys ){ if ( key.startsWith( "network.admin.maybe.vpn.done." )){ COConfigurationManager.removeParameter( key ); } } } private boolean maybeVPNDone( NetworkInterface intf ) { return( COConfigurationManager.getBooleanParameter( "network.admin.maybe.vpn.done." + getConfigKey( intf ), false )); } private void maybeVPN( final NetworkInterface intf ) { final UIManager ui_manager = StaticUtilities.getUIManager( 5*1000 ); if ( ui_manager == null ){ return; } // might have done in the meantime if ( maybeVPNDone( intf )){ return; } COConfigurationManager.setParameter( "network.admin.maybe.vpn.done." + getConfigKey( intf ), true ); new AEThread2( "NetworkAdmin:vpn?" ) { public void run() { String msg_details = MessageText.getString( "network.admin.maybe.vpn.msg", new String[]{ intf.getName() + " - " + intf.getDisplayName() }); long res = ui_manager.showMessageBox( "network.admin.maybe.vpn.title", "!" + msg_details + "!", UIManagerEvent.MT_YES | UIManagerEvent.MT_NO_DEFAULT ); if ( res == UIManagerEvent.MT_YES ){ COConfigurationManager.setParameter( "User Mode", 2 ); COConfigurationManager.setParameter( "Bind IP", intf.getName()); COConfigurationManager.setParameter( "Enforce Bind IP", true ); COConfigurationManager.setParameter( "Check Bind IP On Start", true ); COConfigurationManager.save(); try{ Set<NetworkConnectionBase> connections = NetworkManager.getSingleton().getConnections(); Map<InetAddress,Object[]> lookup_map = new HashMap<InetAddress, Object[]>(); for ( NetworkConnectionBase connection: connections ){ TransportBase tb = connection.getTransportBase(); if ( tb instanceof Transport ){ boolean ok = false; Transport transport = (Transport)tb; if ( transport.isTCP()){ TransportStartpoint start = transport.getTransportStartpoint(); if ( start != null ){ InetSocketAddress socket_address = start.getProtocolStartpoint().getAddress(); if ( socket_address != null ){ InetAddress address = socket_address.getAddress(); Object[] details = lookup_map.get( address ); if ( details == null ){ if ( !lookup_map.containsKey( address )){ details = getInterfaceForAddress( address ); lookup_map.put( address, details ); } if ( details[0] == intf ){ ok = true; } } } } } if ( !ok ){ transport.close( "Explicit bind IP set, disconnecting incompatible connections" ); } } } }catch( Throwable e ){ Debug.out( e); } bs_last_calc = 0; ui_manager.showMessageBox( "settings.updated.title", "settings.updated.msg", UIManagerEvent.MT_OK ); } } }.start(); } private String getConfigKey( NetworkInterface intf ) { try{ return( Base32.encode( intf.getName().getBytes( "UTF-8" ))); }catch( Throwable e ){ Debug.out( e ); return( "derp" ); } } private int categoriseIntf( NetworkInterface intf ) { String name = intf.getName().toLowerCase(); String desc = intf.getDisplayName().toLowerCase(); if ( desc.startsWith( "tap-" ) || desc.contains( "vpn" )){ return( 2 ); }else if ( name.startsWith( "ppp" )){ return( 2 ); }else if ( name.startsWith( "tun" )){ return( 2 ); }else if ( name.startsWith( "eth" ) || name.startsWith( "en" )){ return( 1 ); }else{ return( 0 ); } } public String classifyRoute( InetAddress address ) { synchronized( address_history ){ if ( address_history_update_time == 0 ){ return( "Initializing" ); } byte[] address_bytes = address.getAddress(); AddressHistoryRecord best_entry = null; int best_prefix = 0; for ( AddressHistoryRecord entry: address_history.values()){ InetAddress other_address = entry.getAddress(); byte[] other_bytes = other_address.getAddress(); if ( other_bytes.length == address_bytes.length ){ int prefix_len = 0; for ( int i=0;i<other_bytes.length;i++){ byte b1 = address_bytes[i]; byte b2 = other_bytes[i]; if ( b1 == b2 ){ prefix_len += 8; }else{ for ( int j=7;j>=1;j--){ if ( (( b1>>j ) & 0x01 ) == (( b2>>j ) & 0x01 )){ prefix_len++; }else{ break; } } break; } } if ( prefix_len > best_prefix ){ best_prefix = prefix_len; best_entry = entry; } } } if ( best_entry == null ){ return( "Unknown" ); } return( best_entry.getName( address_history_update_time )); } } public Object[] getInterfaceForAddress( InetAddress address ) { byte[] address_bytes = address.getAddress(); Set<NetworkInterface> interfaces = old_network_interfaces; if ( interfaces == null ){ return( null ); } NetworkInterface best_intf = null; InetAddress best_addr = null; int best_prefix = 0; for ( NetworkInterface intf: interfaces ){ Enumeration<InetAddress> addresses = intf.getInetAddresses(); int num_addresses = 0; InetAddress derp = null; while( addresses.hasMoreElements()){ InetAddress other_address = addresses.nextElement(); byte[] other_bytes = other_address.getAddress(); if ( other_bytes.length == address_bytes.length ){ num_addresses++; int prefix_len = 0; for ( int i=0;i<other_bytes.length;i++){ byte b1 = address_bytes[i]; byte b2 = other_bytes[i]; if ( b1 == b2 ){ prefix_len += 8; }else{ for ( int j=7;j>=1;j--){ if ( (( b1>>j ) & 0x01 ) == (( b2>>j ) & 0x01 )){ prefix_len++; }else{ break; } } break; } } if ( prefix_len > best_prefix ){ best_prefix = prefix_len; best_intf = intf; best_addr = null; derp = other_address; } } } if ( derp != null && num_addresses > 1 ){ best_addr = derp; } } if ( best_addr != null ){ return( new Object[]{ best_intf, best_addr }); }else if ( best_intf != null ){ return( new Object[]{ best_intf }); }else{ return( new Object[]{ address }); } } public void addPropertyChangeListener( NetworkAdminPropertyChangeListener listener ) { listeners.add( listener ); } public void addAndFirePropertyChangeListener( NetworkAdminPropertyChangeListener listener ) { listeners.add( listener ); for (int i=0;i<NetworkAdmin.PR_NAMES.length;i++){ try{ listener.propertyChanged( PR_NAMES[i] ); }catch( Throwable e ){ Debug.printStackTrace(e); } } } public void removePropertyChangeListener( NetworkAdminPropertyChangeListener listener ) { listeners.remove( listener ); } public void generate( IndentWriter writer ) { writer.println( "Network Admin" ); try{ writer.indent(); try{ writer.println( "Binding Details" ); writer.indent(); boolean enforceBind = COConfigurationManager.getBooleanParameter("Enforce Bind IP"); writer.println( "bind to: " + getString( getAllBindAddresses( false )) + ", enforce=" + enforceBind ); writer.println( "bindable: " + getString( getBindableAddresses())); writer.println( "ipv6_enabled=" + IPv6_enabled ); writer.println( "ipv4_potential=" + hasIPV4Potential()); writer.println( "ipv6_potential=" + hasIPV6Potential(false) + "/" + hasIPV6Potential(true)); try{ writer.println( "single homed: " + getSingleHomedServiceBindAddress()); }catch( Throwable e ){ writer.println( "single homed: none" ); } try{ writer.println( "single homed (4): " + getSingleHomedServiceBindAddress( IP_PROTOCOL_VERSION_REQUIRE_V4 )); }catch( Throwable e ){ writer.println( "single homed (4): none" ); } try{ writer.println( "single homed (6): " + getSingleHomedServiceBindAddress( IP_PROTOCOL_VERSION_REQUIRE_V6 )); }catch( Throwable e ){ writer.println( "single homed (6): none" ); } writer.println( "multi homed, nio=false: " + getString( getMultiHomedServiceBindAddresses( false ))); writer.println( "multi homed, nio=true: " + getString( getMultiHomedServiceBindAddresses( true ))); }finally{ writer.exdent(); } NetworkAdminHTTPProxy proxy = getHTTPProxy(); if ( proxy == null ){ writer.println( "HTTP proxy: none" ); }else{ writer.println( "HTTP proxy: " + proxy.getName()); try{ NetworkAdminHTTPProxy.Details details = proxy.getDetails(); writer.println( " name: " + details.getServerName()); writer.println( " resp: " + details.getResponse()); writer.println( " auth: " + details.getAuthenticationType()); }catch( NetworkAdminException e ){ writer.println( " failed: " + e.getLocalizedMessage()); } } NetworkAdminSocksProxy[] socks = getSocksProxies(); if ( socks.length == 0 ){ writer.println( "Socks proxy: none" ); }else{ for (int i=0;i<socks.length;i++){ NetworkAdminSocksProxy sock = socks[i]; writer.println( "Socks proxy: " + sock.getName()); try{ String[] versions = sock.getVersionsSupported(); String str = ""; for (int j=0;j<versions.length;j++){ str += (j==0?"":",") + versions[j]; } writer.println( " version: " + str ); }catch( NetworkAdminException e ){ writer.println( " failed: " + e.getLocalizedMessage()); } } } try { NetworkAdminNATDevice[] nat_devices = getNATDevices(AzureusCoreFactory.getSingleton()); writer.println( "NAT Devices: " + nat_devices.length ); for (int i=0;i<nat_devices.length;i++){ NetworkAdminNATDevice device = nat_devices[i]; writer.println( " " + device.getName() + ",address=" + device.getAddress().getHostAddress() + ":" + device.getPort() + ",ext=" + device.getExternalAddress()); } }catch (Exception e){ writer.println( "Nat Devices: Can't get -> " + e.toString()); } writer.println( "Interfaces" ); writer.println( " " + getNetworkInterfacesAsString()); }finally{ writer.exdent(); } } private String getString( InetAddress[] addresses ) { String str = ""; for ( InetAddress address: addresses ){ str += (str.length()==0?"":", ") + address.getHostAddress(); } return( str ); } public void generateDiagnostics( final IndentWriter iw ) { Set public_addresses = new HashSet(); NetworkAdminHTTPProxy proxy = getHTTPProxy(); if ( proxy == null ){ iw.println( "HTTP proxy: none" ); }else{ iw.println( "HTTP proxy: " + proxy.getName()); try{ NetworkAdminHTTPProxy.Details details = proxy.getDetails(); iw.println( " name: " + details.getServerName()); iw.println( " resp: " + details.getResponse()); iw.println( " auth: " + details.getAuthenticationType()); }catch( NetworkAdminException e ){ iw.println( " failed: " + e.getLocalizedMessage()); } } NetworkAdminSocksProxy[] socks = getSocksProxies(); if ( socks.length == 0 ){ iw.println( "Socks proxy: none" ); }else{ for (int i=0;i<socks.length;i++){ NetworkAdminSocksProxy sock = socks[i]; iw.println( "Socks proxy: " + sock.getName()); try{ String[] versions = sock.getVersionsSupported(); String str = ""; for (int j=0;j<versions.length;j++){ str += (j==0?"":",") + versions[j]; } iw.println( " version: " + str ); }catch( NetworkAdminException e ){ iw.println( " failed: " + e.getLocalizedMessage()); } } } try { NetworkAdminNATDevice[] nat_devices = getNATDevices(AzureusCoreFactory.getSingleton()); iw.println( "NAT Devices: " + nat_devices.length ); for (int i=0;i<nat_devices.length;i++){ NetworkAdminNATDevice device = nat_devices[i]; iw.println( " " + device.getName() + ",address=" + device.getAddress().getHostAddress() + ":" + device.getPort() + ",ext=" + device.getExternalAddress()); public_addresses.add( device.getExternalAddress()); } } catch (Exception e) { iw.println( "Nat Devices: Can't get -> " + e.toString()); } iw.println( "Interfaces" ); NetworkAdminNetworkInterface[] interfaces = getInterfaces(); if ( FULL_INTF_PROBE ){ if ( interfaces.length > 0 ){ if ( interfaces.length > 1 || interfaces[0].getAddresses().length > 1 ){ for (int i=0;i<interfaces.length;i++){ networkInterface interf = (networkInterface)interfaces[i]; iw.indent(); try{ interf.generateDiagnostics( iw, public_addresses ); }finally{ iw.exdent(); } } }else{ if ( interfaces[0].getAddresses().length > 0 ){ networkInterface.networkAddress address = (networkInterface.networkAddress)interfaces[0].getAddresses()[0]; try{ NetworkAdminNode[] nodes = address.getRoute( InetAddress.getByName("www.google.com"), 30000, trace_route_listener ); for (int i=0;i<nodes.length;i++){ networkNode node = (networkNode)nodes[i]; iw.println( node.getString()); } }catch( Throwable e ){ iw.println( "Can't resolve host for route trace - " + e.getMessage()); } } } } }else{ try{ pingTargets( InetAddress.getByName( "www.google.com" ), 30000, new NetworkAdminRoutesListener() { private int timeouts = 0; public boolean foundNode( NetworkAdminNetworkInterfaceAddress intf, NetworkAdminNode[] route, int distance, int rtt ) { iw.println( intf.getAddress().getHostAddress() + ": " + route[route.length-1].getAddress().getHostAddress() + " (" + distance + ")" ); return( false ); } public boolean timeout( NetworkAdminNetworkInterfaceAddress intf, NetworkAdminNode[] route, int distance ) { iw.println( intf.getAddress().getHostAddress() + ": timeout (dist=" + distance + ")" ); timeouts++; return( timeouts < 3 ); } }); }catch( Throwable e ){ iw.println( "getRoutes failed: " + Debug.getNestedExceptionMessage( e )); } } iw.println( "Inbound protocols: default routing" ); if (AzureusCoreFactory.isCoreRunning()) { AzureusCore azureus_core = AzureusCoreFactory.getSingleton(); NetworkAdminProtocol[] protocols = getInboundProtocols(azureus_core); for (int i=0;i<protocols.length;i++){ NetworkAdminProtocol protocol = protocols[i]; try{ InetAddress ext_addr = testProtocol( protocol ); if ( ext_addr != null ){ public_addresses.add( ext_addr ); } iw.println( " " + protocol.getName() + " - " + ext_addr ); }catch( NetworkAdminException e ){ iw.println( " " + protocol.getName() + " - " + Debug.getNestedExceptionMessage(e)); } } iw.println( "Outbound protocols: default routing" ); protocols = getOutboundProtocols(azureus_core); for (int i=0;i<protocols.length;i++){ NetworkAdminProtocol protocol = protocols[i]; try{ InetAddress ext_addr = testProtocol( protocol ); if ( ext_addr != null ){ public_addresses.add( ext_addr ); } iw.println( " " + protocol.getName() + " - " + ext_addr ); }catch( NetworkAdminException e ){ iw.println( " " + protocol.getName() + " - " + Debug.getNestedExceptionMessage(e)); } } } Iterator it = public_addresses.iterator(); iw.println( "Public Addresses" ); while( it.hasNext()){ InetAddress pub_address = (InetAddress)it.next(); try{ NetworkAdminASN res = lookupCurrentASN( pub_address ); iw.println( " " + pub_address.getHostAddress() + " -> " + res.getAS() + "/" + res.getASName()); }catch( Throwable e ){ iw.println( " " + pub_address.getHostAddress() + " -> " + Debug.getNestedExceptionMessage(e)); } } } protected class networkInterface implements NetworkAdminNetworkInterface { private NetworkInterface ni; protected networkInterface( NetworkInterface _ni ) { ni = _ni; } public String getDisplayName() { return( ni.getDisplayName()); } public String getName() { return( ni.getName()); } public NetworkAdminNetworkInterfaceAddress[] getAddresses() { // BAH NetworkInterface has lots of goodies but is 1.6 Enumeration e = ni.getInetAddresses(); List addresses = new ArrayList(); while( e.hasMoreElements()){ InetAddress address = (InetAddress)e.nextElement(); if ((address instanceof Inet6Address) && !IPv6_enabled ){ continue; } addresses.add( new networkAddress(address)); } return((NetworkAdminNetworkInterfaceAddress[])addresses.toArray( new NetworkAdminNetworkInterfaceAddress[addresses.size()])); } public String getString() { String str = getDisplayName() + "/" + getName() + " ["; NetworkAdminNetworkInterfaceAddress[] addresses = getAddresses(); for (int i=0;i<addresses.length;i++){ networkAddress addr = (networkAddress)addresses[i]; str += (i==0?"":",") + addr.getAddress().getHostAddress(); } return( str + "]" ); } public void generateDiagnostics( IndentWriter iw, Set public_addresses ) { iw.println( getDisplayName() + "/" + getName()); NetworkAdminNetworkInterfaceAddress[] addresses = getAddresses(); for (int i=0;i<addresses.length;i++){ networkAddress addr = (networkAddress)addresses[i]; iw.indent(); try{ addr.generateDiagnostics( iw, public_addresses ); }finally{ iw.exdent(); } } } protected class networkAddress implements NetworkAdminNetworkInterfaceAddress { private InetAddress address; protected networkAddress( InetAddress _address ) { address = _address; } public NetworkAdminNetworkInterface getInterface() { return( networkInterface.this ); } public InetAddress getAddress() { return( address ); } public boolean isLoopback() { return( address.isLoopbackAddress()); } public NetworkAdminNode[] getRoute( InetAddress target, final int max_millis, final NetworkAdminRouteListener listener ) throws NetworkAdminException { return( NetworkAdminImpl.this.getRoute( address, target, max_millis, listener)); } public NetworkAdminNode pingTarget( InetAddress target, final int max_millis, final NetworkAdminRouteListener listener ) throws NetworkAdminException { return( NetworkAdminImpl.this.pingTarget( address, target, max_millis, listener)); } public InetAddress testProtocol( NetworkAdminProtocol protocol ) throws NetworkAdminException { return( protocol.test( this )); } public void generateDiagnostics( IndentWriter iw, Set public_addresses ) { iw.println( "" + getAddress()); try{ iw.println( " Trace route" ); iw.indent(); if ( isLoopback()){ iw.println( "Loopback - ignoring" ); }else{ try{ NetworkAdminNode[] nodes = getRoute( InetAddress.getByName("www.google.com"), 30000, trace_route_listener ); for (int i=0;i<nodes.length;i++){ networkNode node = (networkNode)nodes[i]; iw.println( node.getString()); } }catch( Throwable e ){ iw.println( "Can't resolve host for route trace - " + e.getMessage()); } iw.println( "Outbound protocols: bound" ); AzureusCore azureus_core = AzureusCoreFactory.getSingleton(); NetworkAdminProtocol[] protocols = getOutboundProtocols(azureus_core); for (int i=0;i<protocols.length;i++){ NetworkAdminProtocol protocol = protocols[i]; try{ InetAddress res = testProtocol( protocol ); if ( res != null ){ public_addresses.add( res ); } iw.println( " " + protocol.getName() + " - " + res ); }catch( NetworkAdminException e ){ iw.println( " " + protocol.getName() + " - " + Debug.getNestedExceptionMessage(e)); } } iw.println( "Inbound protocols: bound" ); protocols = getInboundProtocols(azureus_core); for (int i=0;i<protocols.length;i++){ NetworkAdminProtocol protocol = protocols[i]; try{ InetAddress res = testProtocol( protocol ); if ( res != null ){ public_addresses.add( res ); } iw.println( " " + protocol.getName() + " - " + res ); }catch( NetworkAdminException e ){ iw.println( " " + protocol.getName() + " - " + Debug.getNestedExceptionMessage(e)); } } } }finally{ iw.exdent(); } } } } protected class networkNode implements NetworkAdminNode { private InetAddress address; private int distance; private int rtt; protected networkNode( InetAddress _address, int _distance, int _millis ) { address = _address; distance = _distance; rtt = _millis; } public InetAddress getAddress() { return( address ); } public boolean isLocalAddress() { return( address.isLinkLocalAddress() || address.isSiteLocalAddress()); } public int getDistance() { return( distance ); } public int getRTT() { return( rtt ); } protected String getString() { if ( address == null ){ return( "" + distance ); }else{ return( distance + "," + address + "[local=" + isLocalAddress() + "]," + rtt ); } } } protected void generateDiagnostics( IndentWriter iw, NetworkAdminProtocol[] protocols ) { for (int i=0;i<protocols.length;i++){ NetworkAdminProtocol protocol = protocols[i]; iw.println( "Testing " + protocol.getName()); try{ InetAddress ext_addr = testProtocol( protocol ); iw.println( " -> OK, public address=" + ext_addr ); }catch( NetworkAdminException e ){ iw.println( " -> Failed: " + Debug.getNestedExceptionMessage(e)); } } } public void logNATStatus( IndentWriter iw ) { if (AzureusCoreFactory.isCoreRunning()) { generateDiagnostics( iw, getInboundProtocols(AzureusCoreFactory.getSingleton())); } } private static class AddressHistoryRecord { private String ni_name; private boolean ni_has_multiple_addresses; private InetAddress address; private long last_seen; private AddressHistoryRecord( NetworkInterface _ni, InetAddress _a, long _now ) { ni_name = _ni.getName(); address = _a; last_seen = _now; Enumeration<InetAddress> addresses = _ni.getInetAddresses(); int hits = 0; int len = address.getAddress().length; while( addresses.hasMoreElements()){ if ( addresses.nextElement().getAddress().length == len ){ hits++; } } ni_has_multiple_addresses = hits > 1; } private void setLastSeen( long t ) { last_seen = t; } private long getLastSeen() { return( last_seen ); } private InetAddress getAddress() { return( address ); } private String getName( long last_update ) { String result = ni_name; if ( ni_has_multiple_addresses ){ result += "/" + address.getHostAddress(); } if ( last_update > last_seen ){ result += " (disconnected)"; } return( result ); } } public static void main( String[] args ) { boolean TEST_SOCKS_PROXY = false; boolean TEST_HTTP_PROXY = false; try{ if ( TEST_SOCKS_PROXY ){ AESocksProxy proxy = AESocksProxyFactory.create( 4567, 10000, 10000 ); proxy.setAllowExternalConnections( true ); System.setProperty( "socksProxyHost", "localhost" ); System.setProperty( "socksProxyPort", "4567" ); } if ( TEST_HTTP_PROXY ){ System.setProperty("http.proxyHost", "localhost" ); System.setProperty("http.proxyPort", "3128" ); System.setProperty("https.proxyHost", "localhost" ); System.setProperty("https.proxyPort", "3128" ); Authenticator.setDefault( new Authenticator() { protected PasswordAuthentication getPasswordAuthentication() { return( new PasswordAuthentication( "fred", "bill".toCharArray())); } }); } IndentWriter iw = new IndentWriter( new PrintWriter( System.out )); iw.setForce( true ); COConfigurationManager.initialise(); AzureusCoreFactory.create(); NetworkAdmin admin = getSingleton(); //admin.logNATStatus( iw ); admin.generateDiagnostics( iw ); }catch( Throwable e){ e.printStackTrace(); } } }