1 /* 2 * Created on 04-Jan-2006 3 * Created by Paul Gardner 4 * Copyright (C) Azureus Software, Inc, All Rights Reserved. 5 * 6 * This program is free software; you can redistribute it and/or 7 * modify it under the terms of the GNU General Public License 8 * as published by the Free Software Foundation; either version 2 9 * of the License, or (at your option) any later version. 10 * This program is distributed in the hope that it will be useful, 11 * but WITHOUT ANY WARRANTY; without even the implied warranty of 12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 13 * GNU General Public License for more details. 14 * You should have received a copy of the GNU General Public License 15 * along with this program; if not, write to the Free Software 16 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA. 17 * 18 */ 19 20 package org.gudy.azureus2.core3.util; 21 22 import java.net.Inet6Address; 23 import java.net.InetAddress; 24 import java.net.InetSocketAddress; 25 import java.net.URL; 26 import java.net.UnknownHostException; 27 import java.security.MessageDigest; 28 import java.util.ArrayList; 29 import java.util.HashMap; 30 import java.util.List; 31 import java.util.Locale; 32 import java.util.Map; 33 34 import org.gudy.azureus2.core3.config.COConfigurationManager; 35 import org.gudy.azureus2.core3.config.ParameterListener; 36 import org.gudy.bouncycastle.util.encoders.Base64; 37 38 import com.aelitis.azureus.core.AzureusCoreFactory; 39 import com.aelitis.azureus.core.instancemanager.AZInstance; 40 import com.aelitis.azureus.core.instancemanager.AZInstanceManager; 41 import com.aelitis.azureus.core.proxy.AEProxyFactory; 42 43 public class 44 AddressUtils 45 { 46 public static final byte LAN_LOCAL_MAYBE = 0; 47 public static final byte LAN_LOCAL_YES = 1; 48 public static final byte LAN_LOCAL_NO = 2; 49 50 private static boolean i2p_is_lan_limit; 51 52 static{ 53 COConfigurationManager.addAndFireParameterListener( 54 "Plugin.azi2phelper.azi2phelper.rates.use.lan", 55 new ParameterListener() 56 { 57 public void 58 parameterChanged( 59 String parameterName ) 60 { 61 i2p_is_lan_limit = COConfigurationManager.getBooleanParameter( "Plugin.azi2phelper.azi2phelper.rates.use.lan", false ); 62 } 63 }); 64 } 65 66 private static AZInstanceManager instance_manager; 67 68 private static Map host_map = null; 69 70 public static URL adjustURL( URL url )71 adjustURL( 72 URL url ) 73 { 74 url = AEProxyFactory.getAddressMapper().internalise( url ); 75 76 if ( host_map != null ){ 77 78 String rewrite = (String)host_map.get( url.getHost()); 79 80 if ( rewrite != null ){ 81 82 String str = url.toExternalForm(); 83 84 try{ 85 int pos = str.indexOf( "//" ) + 2; 86 87 int pos2 = str.indexOf( "/", pos ); 88 89 String host_bit = str.substring( pos, pos2 ); 90 91 int pos3 = host_bit.indexOf(':'); 92 93 String port_bit; 94 95 if ( pos3 == -1 ){ 96 97 port_bit = ""; 98 99 }else{ 100 101 port_bit = host_bit.substring(pos3); 102 } 103 104 String new_str = str.substring(0,pos) + rewrite + port_bit + str.substring( pos2 ); 105 106 url = new URL( new_str ); 107 108 }catch( Throwable e ){ 109 110 Debug.printStackTrace(e); 111 } 112 } 113 } 114 115 return( url ); 116 } 117 118 public static synchronized void addHostRedirect( String from_host, String to_host )119 addHostRedirect( 120 String from_host, 121 String to_host ) 122 { 123 System.out.println( "AddressUtils::addHostRedirect - " + from_host + " -> " + to_host ); 124 125 Map new_map; 126 127 if ( host_map == null ){ 128 129 new_map = new HashMap(); 130 }else{ 131 132 new_map = new HashMap( host_map ); 133 } 134 135 new_map.put( from_host, to_host ); 136 137 host_map = new_map; 138 } 139 140 public static InetSocketAddress adjustTCPAddress( InetSocketAddress address, boolean ext_to_lan )141 adjustTCPAddress( 142 InetSocketAddress address, 143 boolean ext_to_lan ) 144 { 145 return( adjustAddress( address, ext_to_lan, AZInstanceManager.AT_TCP )); 146 } 147 148 public static InetSocketAddress adjustUDPAddress( InetSocketAddress address, boolean ext_to_lan )149 adjustUDPAddress( 150 InetSocketAddress address, 151 boolean ext_to_lan ) 152 { 153 return( adjustAddress( address, ext_to_lan, AZInstanceManager.AT_UDP )); 154 } 155 156 public static InetSocketAddress adjustDHTAddress( InetSocketAddress address, boolean ext_to_lan )157 adjustDHTAddress( 158 InetSocketAddress address, 159 boolean ext_to_lan ) 160 { 161 return( adjustAddress( address, ext_to_lan, AZInstanceManager.AT_UDP_NON_DATA )); 162 } 163 164 private static InetSocketAddress adjustAddress( InetSocketAddress address, boolean ext_to_lan, int port_type )165 adjustAddress( 166 InetSocketAddress address, 167 boolean ext_to_lan, 168 int port_type ) 169 { 170 if ( instance_manager == null ){ 171 172 try{ 173 instance_manager = AzureusCoreFactory.getSingleton().getInstanceManager(); 174 175 }catch( Throwable e ){ 176 177 // Debug.printStackTrace(e); 178 } 179 } 180 181 if ( instance_manager == null || !instance_manager.isInitialized()){ 182 183 return( address ); 184 } 185 186 InetSocketAddress adjusted_address; 187 188 if ( ext_to_lan ){ 189 190 adjusted_address = instance_manager.getLANAddress( address, port_type ); 191 192 }else{ 193 194 adjusted_address = instance_manager.getExternalAddress( address, port_type ); 195 } 196 197 if ( adjusted_address == null ){ 198 199 adjusted_address = address; 200 201 }else{ 202 203 // System.out.println( "adj: " + address + "/" + ext_to_lan + " -> " + adjusted_address ); 204 } 205 206 return( adjusted_address ); 207 } 208 209 public static List getLANAddresses( String address )210 getLANAddresses( 211 String address ) 212 { 213 List result = new ArrayList(); 214 215 result.add( address ); 216 217 try{ 218 InetAddress ad = InetAddress.getByName( address ); 219 220 if ( isLANLocalAddress( address ) != LAN_LOCAL_NO ){ 221 222 if ( instance_manager == null ){ 223 224 try{ 225 instance_manager = AzureusCoreFactory.getSingleton().getInstanceManager(); 226 227 }catch( Throwable e ){ 228 229 //Debug.printStackTrace(e); 230 } 231 } 232 233 if ( instance_manager == null || !instance_manager.isInitialized()){ 234 235 return( result ); 236 } 237 238 AZInstance[] instances = instance_manager.getOtherInstances(); 239 240 for (int i=0;i<instances.length;i++){ 241 242 AZInstance instance = instances[i]; 243 244 List addresses = instance.getInternalAddresses(); 245 246 if ( addresses.contains( ad )){ 247 248 for ( int j=0; j<addresses.size();j++){ 249 250 InetAddress ia = (InetAddress)addresses.get(j); 251 252 String str = ia.getHostAddress(); 253 254 if ( !result.contains( str )){ 255 256 result.add( str ); 257 } 258 } 259 } 260 } 261 } 262 }catch( Throwable e ){ 263 264 } 265 266 return( result ); 267 } 268 269 public static byte isLANLocalAddress( InetSocketAddress socket_address )270 isLANLocalAddress( 271 InetSocketAddress socket_address ) 272 { 273 InetAddress address = socket_address.getAddress(); 274 275 return( isLANLocalAddress( address )); 276 } 277 278 public static byte isLANLocalAddress( InetAddress address )279 isLANLocalAddress( 280 InetAddress address ) 281 282 { 283 // if someone passes us an unresolved address then handle sensibly 284 285 if ( address == null ){ 286 287 return( LAN_LOCAL_NO ); 288 } 289 290 if ( instance_manager == null ){ 291 292 if ( AzureusCoreFactory.isCoreAvailable()){ 293 294 try{ 295 instance_manager = AzureusCoreFactory.getSingleton().getInstanceManager(); 296 297 }catch( Throwable e ){ 298 299 // Debug.printStackTrace(e); 300 } 301 } 302 } 303 304 if ( instance_manager == null || !instance_manager.isInitialized()){ 305 306 return( LAN_LOCAL_MAYBE ); 307 } 308 309 return( instance_manager.isLANAddress( address )? LAN_LOCAL_YES:LAN_LOCAL_NO); 310 } 311 312 313 public static byte isLANLocalAddress( String address )314 isLANLocalAddress( 315 String address ) 316 { 317 byte is_lan_local = LAN_LOCAL_MAYBE; 318 319 try { 320 is_lan_local = isLANLocalAddress( HostNameToIPResolver.syncResolve( address )); 321 322 }catch( UnknownHostException e ){ 323 324 }catch( Throwable t ){ 325 326 t.printStackTrace(); 327 } 328 329 return is_lan_local; 330 } 331 332 public static boolean applyLANRateLimits( InetSocketAddress address )333 applyLANRateLimits( 334 InetSocketAddress address ) 335 { 336 if ( i2p_is_lan_limit ){ 337 338 if ( address.isUnresolved()){ 339 340 return( AENetworkClassifier.categoriseAddress( address ) == AENetworkClassifier.AT_I2P ); 341 } 342 } 343 344 return( false ); 345 } 346 /** 347 * checks if the provided address is a global-scope ipv6 unicast address 348 */ isGlobalAddressV6(InetAddress addr)349 public static boolean isGlobalAddressV6(InetAddress addr) { 350 return addr instanceof Inet6Address && !addr.isAnyLocalAddress() && !addr.isLinkLocalAddress() && !addr.isLoopbackAddress() && !addr.isMulticastAddress() && !addr.isSiteLocalAddress() && !((Inet6Address)addr).isIPv4CompatibleAddress(); 351 } 352 isTeredo(InetAddress addr)353 public static boolean isTeredo(InetAddress addr) 354 { 355 if(!(addr instanceof Inet6Address)) 356 return false; 357 byte[] bytes = addr.getAddress(); 358 // check for the 2001:0000::/32 prefix, i.e. teredo 359 return bytes[0] == 0x20 && bytes[1] == 0x01 && bytes[2] == 0x00 && bytes[3] == 0x00; 360 } 361 is6to4(InetAddress addr)362 public static boolean is6to4(InetAddress addr) 363 { 364 if(!(addr instanceof Inet6Address)) 365 return false; 366 byte[] bytes = addr.getAddress(); 367 // check for the 2002::/16 prefix, i.e. 6to4 368 return bytes[0] == 0x20 && bytes[1] == 0x02; 369 } 370 371 /** 372 * picks 1 global-scoped address out of a list based on the heuristic 373 * "true" ipv6/tunnel broker > 6to4 > teredo 374 * 375 * @return null if no proper v6 address is found, best one otherwise 376 */ pickBestGlobalV6Address(List<InetAddress> addrs)377 public static InetAddress pickBestGlobalV6Address(List<InetAddress> addrs) 378 { 379 InetAddress bestPick = null; 380 int currentRanking = 0; 381 for(InetAddress addr : addrs) 382 { 383 if(!isGlobalAddressV6(addr)) 384 continue; 385 int ranking = 3; 386 if(isTeredo(addr)) 387 ranking = 1; 388 else if(is6to4(addr)) 389 ranking = 2; 390 391 if(ranking > currentRanking) 392 { 393 bestPick = addr; 394 currentRanking = ranking; 395 } 396 } 397 398 return bestPick; 399 } 400 401 public static InetAddress getByName( String host )402 getByName( 403 String host ) 404 405 throws UnknownHostException 406 { 407 if ( AENetworkClassifier.categoriseAddress( host ) == AENetworkClassifier.AT_PUBLIC ){ 408 409 return( InetAddress.getByName( host )); 410 } 411 412 throw( new UnknownHostException( host )); 413 } 414 415 public static InetAddress[] getAllByName( String host )416 getAllByName( 417 String host ) 418 419 throws UnknownHostException 420 { 421 if ( AENetworkClassifier.categoriseAddress( host ) == AENetworkClassifier.AT_PUBLIC ){ 422 423 return( InetAddress.getAllByName( host )); 424 } 425 426 throw( new UnknownHostException( host )); 427 } 428 429 public static byte[] getAddressBytes( InetSocketAddress address )430 getAddressBytes( 431 InetSocketAddress address ) 432 { 433 if ( address.isUnresolved()){ 434 435 try{ 436 return( address.getHostName().getBytes( "ISO8859-1" )); 437 438 }catch( Throwable e ){ 439 440 Debug.out( e ); 441 442 return( null ); 443 } 444 }else{ 445 446 return( address.getAddress().getAddress()); 447 } 448 } 449 450 public static String getHostAddress( InetSocketAddress address )451 getHostAddress( 452 InetSocketAddress address ) 453 { 454 if ( address.isUnresolved()){ 455 456 return( address.getHostName()); 457 458 }else{ 459 460 return( address.getAddress().getHostAddress()); 461 } 462 } 463 464 public static String getHostNameNoResolve( InetSocketAddress address )465 getHostNameNoResolve( 466 InetSocketAddress address ) 467 { 468 InetAddress i_address = address.getAddress(); 469 470 if ( i_address == null ){ 471 472 return( address.getHostName()); 473 474 }else{ 475 476 // only way I can see (short of reflection) of getting access to unresolved host name 477 // toString returns (hostname or "")/getHostAddress() 478 479 String str = i_address.toString(); 480 481 int pos = str.indexOf( '/' ); 482 483 if ( pos == -1 ){ 484 485 // darn it, borkage 486 487 System.out.println( "InetAddress::toString not returning expected result: " + str ); 488 489 return( i_address.getHostAddress()); 490 } 491 492 if ( pos > 0 ){ 493 494 return( str.substring( 0, pos )); 495 496 }else{ 497 498 return( str.substring( pos+1 )); 499 } 500 } 501 } 502 503 public static String convertToShortForm( String address )504 convertToShortForm( 505 String address ) 506 { 507 int address_length = address.length(); 508 509 if ( address_length > 256 ){ 510 511 String to_decode; 512 513 if ( address.endsWith( ".i2p" )){ 514 515 to_decode = address.substring( 0, address.length() - 4 ); 516 517 }else if ( address.indexOf( '.' ) == -1 ){ 518 519 to_decode = address; 520 521 }else{ 522 523 return( address ); 524 } 525 526 try{ 527 // unfortunately we have an incompatible base64 standard in i2p, they replaced / with ~ and + with - 528 529 char[] encoded = to_decode.toCharArray(); 530 531 for ( int i=0;i<encoded.length;i++){ 532 533 char c = encoded[i]; 534 535 if ( c == '~' ){ 536 encoded[i] = '/'; 537 }else if( c == '-' ){ 538 encoded[i] = '+'; 539 } 540 } 541 542 byte[] decoded = Base64.decode( encoded ); 543 544 byte[] hash = MessageDigest.getInstance( "SHA-256" ).digest( decoded ); 545 546 return( Base32.encode( hash ).toLowerCase( Locale.US ) + ".b32.i2p" ); 547 548 }catch( Throwable e ){ 549 550 return( null ); 551 } 552 } 553 554 return( address ); 555 } 556 } 557