1 /*
2  * Created on 29-Jun-2004
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.InetAddress;
23 import java.net.UnknownHostException;
24 import java.util.ArrayList;
25 import java.util.List;
26 
27 /**
28  * @author parg
29  *
30  */
31 
32 public class
33 HostNameToIPResolver
34 {
35 	static protected AEThread2		resolver_thread;
36 
37 	static protected List			request_queue		= new ArrayList();
38 
39 	static protected AEMonitor		request_queue_mon	= new AEMonitor( "HostNameToIPResolver" );
40 
41 	static protected AESemaphore	request_semaphore	= new AESemaphore("HostNameToIPResolver");
42 
43 	public static boolean
isDNSName( String host )44 	isDNSName(
45 		String	host )
46 	{
47 		return( AENetworkClassifier.categoriseAddress( host ) == AENetworkClassifier.AT_PUBLIC );
48 	}
49 
50 	public static boolean
isNonDNSName( String host )51 	isNonDNSName(
52 		String	host )
53 	{
54 		return( AENetworkClassifier.categoriseAddress( host ) != AENetworkClassifier.AT_PUBLIC );
55 	}
56 
57 	public static InetAddress
syncResolve( String host )58 	syncResolve(
59 		String	host )
60 
61 		throws UnknownHostException
62 	{
63 		if ( isNonDNSName( host )){
64 
65 			throw( new HostNameToIPResolverException( "non-DNS name '" + host + "'", true ));
66 		}
67 
68 			// handle any raw addresses up front
69 
70 		byte[]	bytes = textToNumericFormat( host );
71 
72 		if ( bytes != null ){
73 
74 			return( InetAddress.getByAddress( bytes ));
75 		}
76 
77 			// filter out partially complete raw addresses by applying the basic rule in ftp://ftp.is.co.za/rfc/rfc3696.txt
78 			// at least one dot + not all numeric
79 
80 		char[]	chars = host.toCharArray();
81 
82 		boolean	resolve = false;
83 
84 		for (int i=0;i<chars.length;i++){
85 
86 			if ( !( chars[i] == '.' || Character.isDigit(chars[i]))){
87 
88 				resolve	= true;
89 
90 				break;
91 			}
92 		}
93 
94 		if ( resolve && host.startsWith( "websocket." )){
95 
96 			resolve = false;
97 
98 			for (int i=10;i<chars.length;i++){
99 
100 				if ( !Character.isDigit(chars[i])){
101 
102 					resolve = true;
103 
104 					break;
105 				}
106 			}
107 		}
108 
109 		if ( resolve ){
110 
111 			return( InetAddress.getByName( host));
112 
113 		}else{
114 
115 			throw( new UnknownHostException( "Host '" + host + "' doesn't obey minimal validation rules"));
116 		}
117 	}
118 
119 	public static void
addResolverRequest( String host, HostNameToIPResolverListener l )120 	addResolverRequest(
121 		String							host,
122 		HostNameToIPResolverListener	l )
123 	{
124 		byte[]	bytes = textToNumericFormat( host );
125 
126 		if ( bytes != null ){
127 
128 			try{
129 				l.hostNameResolutionComplete( InetAddress.getByAddress( host, bytes ));
130 
131 				return;
132 
133 			}catch( UnknownHostException e ){
134 			}
135 		}
136 
137 		try{
138 			request_queue_mon.enter();
139 
140 			request_queue.add( new request( host, l ));
141 
142 			request_semaphore.release();
143 
144 			if ( resolver_thread == null ){
145 
146 				resolver_thread =
147 					new AEThread2("HostNameToIPResolver",true)
148 					{
149 						public void
150 						run()
151 						{
152 							while(true){
153 
154 								try{
155 									request_semaphore.reserve(30000);
156 
157 									request	req;
158 
159 									try{
160 										request_queue_mon.enter();
161 
162 										if ( request_queue.isEmpty()){
163 
164 											resolver_thread = null;
165 
166 											break;
167 										}
168 
169 										req	= (request)request_queue.remove(0);
170 
171 									}finally{
172 
173 										request_queue_mon.exit();
174 									}
175 
176 									try{
177 										InetAddress addr = syncResolve( req.getHost());
178 
179 										req.getListener().hostNameResolutionComplete( addr );
180 
181 									}catch( Throwable e ){
182 
183 										req.getListener().hostNameResolutionComplete( null );
184 
185 									}
186 								}catch( Throwable e ){
187 
188 									Debug.printStackTrace( e );
189 								}
190 							}
191 						}
192 					};
193 
194 				resolver_thread.start();
195 			}
196 		}finally{
197 
198 			request_queue_mon.exit();
199 		}
200 	}
201 
202 	public static InetAddress
hostAddressToInetAddress( String host )203 	hostAddressToInetAddress(
204 		String		host )
205 	{
206 		byte[]	bytes = hostAddressToBytes( host );
207 
208 		if ( bytes != null ){
209 
210 			try{
211 				return( InetAddress.getByAddress( bytes ));
212 
213 			}catch( Throwable e ){
214 
215 				return( null );
216 			}
217 		}
218 
219 		return( null );
220 	}
221 
222 	public static byte[]
hostAddressToBytes( String host )223 	hostAddressToBytes(
224 		String		host )
225 	{
226 		byte[] res = textToNumericFormat( host );
227 
228 		return( res );
229 	}
230 
231 	final static int INADDRSZ	= 4;
232 
233 	static byte[]
textToNumericFormat( String src )234 	textToNumericFormat(
235 		String src )
236 	{
237 		if (src.length() == 0) {
238 		    return null;
239 		}
240 
241 		if ( src.indexOf(':') != -1 ){
242 
243 				// v6
244 			try{
245 				return( InetAddress.getByName(src).getAddress());
246 
247 			}catch( Throwable e ){
248 
249 				return( null );
250 			}
251 		}
252 
253 		int octets;
254 		char ch;
255 		byte[] dst = new byte[INADDRSZ];
256 
257 		char[] srcb = src.toCharArray();
258 		boolean saw_digit = false;
259 
260 		octets = 0;
261 		int i = 0;
262 		int cur = 0;
263 		while (i < srcb.length) {
264 		    ch = srcb[i++];
265 		    if (Character.isDigit(ch)) {
266 			// note that Java byte is signed, so need to convert to int
267 			int sum = (dst[cur] & 0xff)*10
268 			    + (Character.digit(ch, 10) & 0xff);
269 
270 			if (sum > 255)
271 			    return null;
272 
273 			dst[cur] = (byte)(sum & 0xff);
274 			if (! saw_digit) {
275 			    if (++octets > INADDRSZ)
276 				return null;
277 			    saw_digit = true;
278 			}
279 		    } else if (ch == '.' && saw_digit) {
280 			if (octets == INADDRSZ)
281 			    return null;
282 			cur++;
283 			dst[cur] = 0;
284 			saw_digit = false;
285 		    } else
286 			return null;
287 		}
288 		if (octets < INADDRSZ)
289 		    return null;
290 		return dst;
291 	}
292 
293 
294 
295 	protected static class
296 	request
297 	{
298 		protected String						host;
299 		protected HostNameToIPResolverListener	listener;
300 
301 		protected
request( String _host, HostNameToIPResolverListener _listener )302 		request(
303 			String							_host,
304 			HostNameToIPResolverListener	_listener )
305 		{
306 			host			= _host;
307 			listener		= _listener;
308 		}
309 
310 		protected String
getHost()311 		getHost()
312 		{
313 			return( host );
314 		}
315 
316 		protected HostNameToIPResolverListener
getListener()317 		getListener()
318 		{
319 			return( listener );
320 		}
321 	}
322 }
323