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