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