/* * Created on Mar 20, 2004 * Created by Alon Rohter * 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 org.gudy.azureus2.core3.peer.util; import java.net.InetAddress; import java.util.*; import org.gudy.azureus2.core3.config.*; import org.gudy.azureus2.core3.peer.PEPeer; import org.gudy.azureus2.core3.util.AENetworkClassifier; import org.gudy.azureus2.core3.util.CRC32C; import org.gudy.azureus2.core3.util.Constants; import org.gudy.azureus2.core3.util.Debug; import org.gudy.azureus2.core3.util.HostNameToIPResolver; import org.gudy.azureus2.core3.util.SystemTime; import org.gudy.azureus2.plugins.peers.Peer; import org.gudy.azureus2.plugins.utils.LocationProvider; import org.gudy.azureus2.pluginsimpl.local.PluginCoreUtils; import com.aelitis.azureus.core.AzureusCoreFactory; import com.aelitis.azureus.core.networkmanager.admin.NetworkAdmin; /** * Varies peer connection utility methods. */ public class PeerUtils { private static final String CONFIG_MAX_CONN_PER_TORRENT = "Max.Peer.Connections.Per.Torrent"; private static final String CONFIG_MAX_CONN_TOTAL = "Max.Peer.Connections.Total"; public static int MAX_CONNECTIONS_PER_TORRENT; public static int MAX_CONNECTIONS_TOTAL; static{ COConfigurationManager.addParameterListener( CONFIG_MAX_CONN_PER_TORRENT, new ParameterListener() { public void parameterChanged( String parameterName ) { MAX_CONNECTIONS_PER_TORRENT = COConfigurationManager.getIntParameter(CONFIG_MAX_CONN_PER_TORRENT); } }); MAX_CONNECTIONS_PER_TORRENT = COConfigurationManager.getIntParameter(CONFIG_MAX_CONN_PER_TORRENT); COConfigurationManager.addParameterListener( CONFIG_MAX_CONN_TOTAL, new ParameterListener() { public void parameterChanged( String parameterName ) { MAX_CONNECTIONS_TOTAL = COConfigurationManager.getIntParameter(CONFIG_MAX_CONN_TOTAL); } }); MAX_CONNECTIONS_TOTAL = COConfigurationManager.getIntParameter(CONFIG_MAX_CONN_TOTAL); } private static final NetworkAdmin network_admin; private static volatile long na_last_ip4_time; private static volatile long na_last_ip6_time; private static volatile byte[] na_last_ip4; private static volatile byte[] na_last_ip6; private static int na_tcp_port; static{ NetworkAdmin temp = null; try{ temp = NetworkAdmin.getSingleton(); }catch( Throwable e ){ } network_admin = temp; COConfigurationManager.addAndFireParameterListener( "TCP.Listen.Port", new ParameterListener() { public void parameterChanged( String parameterName ) { na_tcp_port = COConfigurationManager.getIntParameter( parameterName ); } }); } public static int getPeerPriority( String address, int port ) { if ( network_admin == null ){ return( 0 ); } try{ InetAddress ia = HostNameToIPResolver.syncResolve( address ); if ( ia != null ){ return( getPeerPriority( ia, port )); } }catch( Throwable e ){ } return( 0 ); } public static int getPeerPriority( InetAddress address, int peer_port ) { return( getPeerPriority( address.getAddress(), peer_port )); } public static int getPeerPriority( byte[] peer_address, short peer_port ) { return( getPeerPriority( peer_address, ((int)peer_port)&0xffff )); } public static int getPeerPriority( byte[] peer_address, int peer_port ) { if ( network_admin == null ){ return( 0 ); } if ( peer_address == null ){ return( 0 ); } byte[] my_address = null; long now = SystemTime.getMonotonousTime(); if ( peer_address.length == 4 ){ if ( na_last_ip4 != null && now - na_last_ip4_time < 120*1000 ){ my_address = na_last_ip4; }else{ if ( na_last_ip4_time == 0 || now - na_last_ip4_time > 10*1000 ){ na_last_ip4_time = now; InetAddress ia = network_admin.getDefaultPublicAddress( true ); if ( ia != null ){ byte[] iab = ia.getAddress(); if ( iab != null ){ na_last_ip4 = my_address = ia.getAddress(); } } } } if ( my_address == null ){ my_address = na_last_ip4; } }else if ( peer_address.length == 16 ){ if ( na_last_ip6 != null && now - na_last_ip6_time < 120*1000 ){ my_address = na_last_ip6; }else{ if ( na_last_ip6_time == 0 || now - na_last_ip6_time > 10*1000 ){ na_last_ip6_time = now; InetAddress ia = network_admin.getDefaultPublicAddressV6( true ); if ( ia != null ){ byte[] iab = ia.getAddress(); if ( iab != null ){ na_last_ip6 = my_address = ia.getAddress(); } } } } if ( my_address == null ){ my_address = na_last_ip6; } }else{ return( 0 ); } if ( my_address != null && my_address.length == peer_address.length ){ return( getPeerPriority( my_address, na_tcp_port, peer_address, peer_port )); }else{ return( 0 ); } } private static int getPeerPriority( byte[] a1, int port1, byte[] a2, int port2 ) { // http://www.bittorrent.org/beps/bep_0040.html byte[] a1_masked = new byte[a1.length]; byte[] a2_masked = new byte[a2.length]; /* The formula to be used in prioritizing peers is this: priority = crc32-c(sort(masked_client_ip, masked_peer_ip)) If the IP addresses are the same, the port numbers (16-bit integers) should be used instead: priority = crc32-c(sort(client_port, peer_port)) For an IPv4 address, the mask to be used should be FF.FF.55.55 unless the IP addresses are in the same /16. In that case, the mask to be used should be FF.FF.FF.55. If the IP addresses are in the same /24, the entire address should be used (mask FF.FF.FF.FF). For an IPv6 address, the mask should be derived in the same way, beginning with FFFF:FFFF:FFFF:5555:5555:5555:5555:5555. If the IP addresses are in the same /48, the mask to be used should be FFFF:FFFF:FFFF:FF55:5555:5555:5555:5555. If the IP addresses are in the same /56, the mask to be used should be FFFF:FFFF:FFFF:FFFF:5555:5555:5555:5555, etc... */ int x = a1_masked.length==4?1:5; boolean same = true; int order = 0; for ( int i=0;i<a1_masked.length;i++){ byte a1_byte = a1[i]; byte a2_byte = a2[i]; if ( i < x || same ){ a1_masked[i] = a1_byte; a2_masked[i] = a2_byte; }else{ a1_masked[i] = (byte)(a1_byte&0x55); a2_masked[i] = (byte)(a2_byte&0x55); } if ( i >= x && same ){ same = a1_byte == a2_byte; } if ( order == 0 ){ order = (a1_masked[i]&0xff) - (a2_masked[i]&0xff); } } if ( same ){ a1_masked = new byte[]{ (byte)(port1>>8), (byte)port1 }; a2_masked = new byte[]{ (byte)(port2>>8), (byte)port2 }; order = port1 - port2; } CRC32C crc32 = new CRC32C(); if ( order < 0 ){ crc32.updateWord( a1_masked, true ); crc32.updateWord( a2_masked, true ); }else{ crc32.updateWord( a2_masked, true ); crc32.updateWord( a1_masked, true ); } long res = crc32.getValue(); return( (int)res ); } /** * Get the number of new peer connections allowed for the given data item, * within the configured per-torrent and global connection limits. * @return max number of new connections allowed, or -1 if there is no limit */ public static int numNewConnectionsAllowed( PeerIdentityDataID data_id, int specific_max ) { int curConnPerTorrent = PeerIdentityManager.getIdentityCount( data_id ); int curConnTotal = PeerIdentityManager.getTotalIdentityCount(); // specific max here will default to the global per-torrent default if not explicitly set // so we don't need to consider CONFIG_MAX_CONN_PER_TORRENT seperately int PER_TORRENT_LIMIT = specific_max; int perTorrentAllowed = -1; //default unlimited if ( PER_TORRENT_LIMIT != 0 ) { //if limited int allowed = PER_TORRENT_LIMIT - curConnPerTorrent; if ( allowed < 0 ) allowed = 0; perTorrentAllowed = allowed; } int totalAllowed = -1; //default unlimited if ( MAX_CONNECTIONS_TOTAL != 0 ) { //if limited int allowed = MAX_CONNECTIONS_TOTAL - curConnTotal; if ( allowed < 0 ) allowed = 0; totalAllowed = allowed; } int allowed = -1; //default unlimited if ( perTorrentAllowed > -1 && totalAllowed > -1 ) { //if both limited allowed = Math.min( perTorrentAllowed, totalAllowed ); } else if ( perTorrentAllowed == -1 || totalAllowed == -1 ) { //if either unlimited allowed = Math.max( perTorrentAllowed, totalAllowed ); } return allowed; } private static Set<Integer> ignore_peer_ports = new HashSet<Integer>(); static{ COConfigurationManager.addParameterListener( "Ignore.peer.ports", new ParameterListener() { public void parameterChanged( String parameterName ) { readIgnorePeerPorts(); } }); readIgnorePeerPorts(); } private static void readIgnorePeerPorts() { // XXX Optimize me for ranges!! String str = COConfigurationManager.getStringParameter( "Ignore.peer.ports" ).trim(); ignore_peer_ports.clear(); if ( str.length() > 0 ){ String[] ports = str.split("\\;"); if (ports != null && ports.length > 0) { for (int i = 0; i < ports.length; i++) { String port = ports[i]; int spreadPos = port.indexOf('-'); if (spreadPos > 0 && spreadPos < port.length() - 1) { try { int iMin = Integer.parseInt(port.substring(0, spreadPos).trim()); int iMax = Integer.parseInt(port.substring(spreadPos + 1).trim()); iMin = Math.max( 0, iMin ); iMax = Math.min(65535, iMax ); for (int j = iMin; j <= iMax; j++) { ignore_peer_ports.add(Integer.valueOf(j)); } } catch (Throwable e) { Debug.out( "Invalid ignore-port entry: " + port ); } } else { try{ ignore_peer_ports.add(Integer.parseInt(port.trim())); } catch (Throwable e) { Debug.out( "Invalid ignore-port entry: " + port ); } } } } } } public static boolean ignorePeerPort( int port ) { return( ignore_peer_ports.contains( port )); } static final String chars = "abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789"; public static byte[] createPeerID() { byte[] peerId = new byte[20]; byte[] version = Constants.VERSION_ID; for (int i = 0; i < 8; i++) { peerId[i] = version[i]; } for (int i = 8; i < 20; i++) { int pos = (int) ( Math.random() * chars.length()); peerId[i] = (byte)chars.charAt(pos); } // System.out.println( "generated new peer id:" + ByteFormatter.nicePrint(peerId)); return( peerId ); } public static byte[] createWebSeedPeerID() { byte[] peerId = new byte[20]; peerId[0] = '-'; peerId[1] = 'W'; peerId[2] = 'S'; for (int i = 3; i < 20; i++) { int pos = (int) ( Math.random() * chars.length()); peerId[i] = (byte)chars.charAt(pos); } // System.out.println( "generated new peer id:" + ByteFormatter.nicePrint(peerId)); return( peerId ); } private static volatile LocationProvider country_provider; private static long country_provider_last_check; private static Object country_key = new Object(); private static LocationProvider getCountryProvider() { if ( country_provider != null ){ if ( country_provider.isDestroyed()){ country_provider = null; country_provider_last_check = 0; } } if ( country_provider == null ){ long now = SystemTime.getMonotonousTime(); if ( country_provider_last_check == 0 || now - country_provider_last_check > 20*1000 ){ country_provider_last_check = now; java.util.List<LocationProvider> providers = AzureusCoreFactory.getSingleton().getPluginManager().getDefaultPluginInterface().getUtilities().getLocationProviders(); for ( LocationProvider provider: providers ){ if ( provider.hasCapabilities( LocationProvider.CAP_ISO3166_BY_IP | LocationProvider.CAP_COUNTY_BY_IP )){ country_provider = provider; } } } } return( country_provider ); } public static String[] getCountryDetails( Peer peer ) { return( getCountryDetails( PluginCoreUtils.unwrap( peer ))); } public static String[] getCountryDetails( PEPeer peer ) { if ( peer == null ){ return( null ); } String[] details = (String[])peer.getUserData( country_key ); if ( details == null ){ LocationProvider lp = getCountryProvider(); if ( lp != null ){ try{ String ip = peer.getIp(); if ( HostNameToIPResolver.isDNSName( ip )){ InetAddress peer_address = HostNameToIPResolver.syncResolve( ip ); String code = lp.getISO3166CodeForIP( peer_address ); String name = lp.getCountryNameForIP( peer_address, Locale.getDefault()); if ( code != null && name != null ){ details = new String[]{ code, name }; }else{ details = new String[0]; } }else{ String cat = AENetworkClassifier.categoriseAddress( ip ); if ( cat != AENetworkClassifier.AT_PUBLIC ){ details = new String[]{ cat, cat }; }else{ details = new String[0]; } } peer.setUserData( country_key, details ); }catch( Throwable e ){ } } } return( details ); } /* public static void main( String[] args ) { // If the client is 123.213.32.10 and the peer is 98.76.54.32, the hash that they should both arrive at is crc32-c(624C14007BD50000) or BB97323E. // If the client is 123.213.32.10 and the peer is 123.213.32.234, the hash that they should both arrive at is crc32-c[(7BD5200A7BD520EA) or C816B840. CRC32C crc = new CRC32C(); crc.update("The quick brown fox jumps over the lazy dog".getBytes()); System.out.println( Long.toString( crc.getValue(), 16 )); try{ if ( getPeerPriority( InetAddress.getByName( "123.213.32.10" ).getAddress(), 10, InetAddress.getByName( "98.76.54.32" ).getAddress(), 10 ) != 0xBB97323E ){ System.out.println( "derp1" ); } if ( getPeerPriority( InetAddress.getByName( "123.213.32.10" ).getAddress(), 10, InetAddress.getByName( "123.213.32.234" ).getAddress(), 10 ) != 0xC816B840 ){ System.out.println( "derp2" ); } if ( getPeerPriority( InetAddress.getByName( "123.213.32.10" ).getAddress(), 10, InetAddress.getByName( "123.213.32.10" ).getAddress(), 20 ) != 1879809021 ){ System.out.println( "derp3" ); } }catch( Throwable e ){ e.printStackTrace(); } } */ }