1 // Copyright (c) 1999-2004 Brian Wellington (bwelling (at) xbill.org) 2 3 package org.xbill.DNS; 4 5 import java.net.*; 6 import java.net.Inet6Address; 7 8 /** 9 * Routines dealing with IP addresses. Includes functions similar to 10 * those in the java.net.InetAddress class. 11 * 12 * @author Brian Wellington 13 */ 14 15 public final class Address { 16 17 public static final int IPv4 = 1; 18 public static final int IPv6 = 2; 19 20 private 21 Address() {} 22 23 private static byte [] 24 parseV4(String s) { 25 int numDigits; 26 int currentOctet; 27 byte [] values = new byte[4]; 28 int currentValue; 29 int length = s.length(); 30 31 currentOctet = 0; 32 currentValue = 0; 33 numDigits = 0; 34 for (int i = 0; i < length; i++) { 35 char c = s.charAt(i); 36 if (c >= '0' && c <= '9') { 37 /* Can't have more than 3 digits per octet. */ 38 if (numDigits == 3) 39 return null; 40 /* Octets shouldn't start with 0, unless they are 0. */ 41 if (numDigits > 0 && currentValue == 0) 42 return null; 43 numDigits++; 44 currentValue *= 10; 45 currentValue += (c - '0'); 46 /* 255 is the maximum value for an octet. */ 47 if (currentValue > 255) 48 return null; 49 } else if (c == '.') { 50 /* Can't have more than 3 dots. */ 51 if (currentOctet == 3) 52 return null; 53 /* Two consecutive dots are bad. */ 54 if (numDigits == 0) 55 return null; 56 values[currentOctet++] = (byte) currentValue; 57 currentValue = 0; 58 numDigits = 0; 59 } else 60 return null; 61 } 62 /* Must have 4 octets. */ 63 if (currentOctet != 3) 64 return null; 65 /* The fourth octet can't be empty. */ 66 if (numDigits == 0) 67 return null; 68 values[currentOctet] = (byte) currentValue; 69 return values; 70 } 71 72 private static byte [] 73 parseV6(String s) { 74 int range = -1; 75 byte [] data = new byte[16]; 76 77 String [] tokens = s.split(":", -1); 78 79 int first = 0; 80 int last = tokens.length - 1; 81 82 if (tokens[0].length() == 0) { 83 // If the first two tokens are empty, it means the string 84 // started with ::, which is fine. If only the first is 85 // empty, the string started with :, which is bad. 86 if (last - first > 0 && tokens[1].length() == 0) 87 first++; 88 else 89 return null; 90 } 91 92 if (tokens[last].length() == 0) { 93 // If the last two tokens are empty, it means the string 94 // ended with ::, which is fine. If only the last is 95 // empty, the string ended with :, which is bad. 96 if (last - first > 0 && tokens[last - 1].length() == 0) 97 last--; 98 else 99 return null; 100 } 101 102 if (last - first + 1 > 8) 103 return null; 104 105 int i, j; 106 for (i = first, j = 0; i <= last; i++) { 107 if (tokens[i].length() == 0) { 108 if (range >= 0) 109 return null; 110 range = j; 111 continue; 112 } 113 114 if (tokens[i].indexOf('.') >= 0) { 115 // An IPv4 address must be the last component 116 if (i < last) 117 return null; 118 // There can't have been more than 6 components. 119 if (i > 6) 120 return null; 121 byte [] v4addr = Address.toByteArray(tokens[i], IPv4); 122 if (v4addr == null) 123 return null; 124 for (int k = 0; k < 4; k++) 125 data[j++] = v4addr[k]; 126 break; 127 } 128 129 try { 130 for (int k = 0; k < tokens[i].length(); k++) { 131 char c = tokens[i].charAt(k); 132 if (Character.digit(c, 16) < 0) 133 return null; 134 } 135 int x = Integer.parseInt(tokens[i], 16); 136 if (x > 0xFFFF || x < 0) 137 return null; 138 data[j++] = (byte)(x >>> 8); 139 data[j++] = (byte)(x & 0xFF); 140 } 141 catch (NumberFormatException e) { 142 return null; 143 } 144 } 145 146 if (j < 16 && range < 0) 147 return null; 148 149 if (range >= 0) { 150 int empty = 16 - j; 151 System.arraycopy(data, range, data, range + empty, j - range); 152 for (i = range; i < range + empty; i++) 153 data[i] = 0; 154 } 155 156 return data; 157 } 158 159 /** 160 * Convert a string containing an IP address to an array of 4 or 16 integers. 161 * @param s The address, in text format. 162 * @param family The address family. 163 * @return The address 164 */ 165 public static int [] 166 toArray(String s, int family) { 167 byte [] byteArray = toByteArray(s, family); 168 if (byteArray == null) 169 return null; 170 int [] intArray = new int[byteArray.length]; 171 for (int i = 0; i < byteArray.length; i++) 172 intArray[i] = byteArray[i] & 0xFF; 173 return intArray; 174 } 175 176 /** 177 * Convert a string containing an IPv4 address to an array of 4 integers. 178 * @param s The address, in text format. 179 * @return The address 180 */ 181 public static int [] 182 toArray(String s) { 183 return toArray(s, IPv4); 184 } 185 186 /** 187 * Convert a string containing an IP address to an array of 4 or 16 bytes. 188 * @param s The address, in text format. 189 * @param family The address family. 190 * @return The address 191 */ 192 public static byte [] 193 toByteArray(String s, int family) { 194 if (family == IPv4) 195 return parseV4(s); 196 else if (family == IPv6) 197 return parseV6(s); 198 else 199 throw new IllegalArgumentException("unknown address family"); 200 } 201 202 /** 203 * Determines if a string contains a valid IP address. 204 * @param s The string 205 * @return Whether the string contains a valid IP address 206 */ 207 public static boolean 208 isDottedQuad(String s) { 209 byte [] address = Address.toByteArray(s, IPv4); 210 return (address != null); 211 } 212 213 /** 214 * Converts a byte array containing an IPv4 address into a dotted quad string. 215 * @param addr The array 216 * @return The string representation 217 */ 218 public static String 219 toDottedQuad(byte [] addr) { 220 return ((addr[0] & 0xFF) + "." + (addr[1] & 0xFF) + "." + 221 (addr[2] & 0xFF) + "." + (addr[3] & 0xFF)); 222 } 223 224 /** 225 * Converts an int array containing an IPv4 address into a dotted quad string. 226 * @param addr The array 227 * @return The string representation 228 */ 229 public static String 230 toDottedQuad(int [] addr) { 231 return (addr[0] + "." + addr[1] + "." + addr[2] + "." + addr[3]); 232 } 233 234 private static Record [] 235 lookupHostName(String name) throws UnknownHostException { 236 try { 237 Record [] records = new Lookup(name).run(); 238 if (records == null) 239 throw new UnknownHostException("unknown host"); 240 return records; 241 } 242 catch (TextParseException e) { 243 throw new UnknownHostException("invalid name"); 244 } 245 } 246 247 private static InetAddress 248 addrFromRecord(String name, Record r) throws UnknownHostException { 249 ARecord a = (ARecord) r; 250 return InetAddress.getByAddress(name, a.getAddress().getAddress()); 251 } 252 253 /** 254 * Determines the IP address of a host 255 * @param name The hostname to look up 256 * @return The first matching IP address 257 * @exception UnknownHostException The hostname does not have any addresses 258 */ 259 public static InetAddress 260 getByName(String name) throws UnknownHostException { 261 try { 262 return getByAddress(name); 263 } catch (UnknownHostException e) { 264 Record [] records = lookupHostName(name); 265 return addrFromRecord(name, records[0]); 266 } 267 } 268 269 /** 270 * Determines all IP address of a host 271 * @param name The hostname to look up 272 * @return All matching IP addresses 273 * @exception UnknownHostException The hostname does not have any addresses 274 */ 275 public static InetAddress [] 276 getAllByName(String name) throws UnknownHostException { 277 try { 278 InetAddress addr = getByAddress(name); 279 return new InetAddress[] {addr}; 280 } catch (UnknownHostException e) { 281 Record [] records = lookupHostName(name); 282 InetAddress [] addrs = new InetAddress[records.length]; 283 for (int i = 0; i < records.length; i++) 284 addrs[i] = addrFromRecord(name, records[i]); 285 return addrs; 286 } 287 } 288 289 /** 290 * Converts an address from its string representation to an IP address. 291 * The address can be either IPv4 or IPv6. 292 * @param addr The address, in string form 293 * @return The IP addresses 294 * @exception UnknownHostException The address is not a valid IP address. 295 */ 296 public static InetAddress 297 getByAddress(String addr) throws UnknownHostException { 298 byte [] bytes; 299 bytes = toByteArray(addr, IPv4); 300 if (bytes != null) 301 return InetAddress.getByAddress(addr, bytes); 302 bytes = toByteArray(addr, IPv6); 303 if (bytes != null) 304 return InetAddress.getByAddress(addr, bytes); 305 throw new UnknownHostException("Invalid address: " + addr); 306 } 307 308 /** 309 * Converts an address from its string representation to an IP address in 310 * a particular family. 311 * @param addr The address, in string form 312 * @param family The address family, either IPv4 or IPv6. 313 * @return The IP addresses 314 * @exception UnknownHostException The address is not a valid IP address in 315 * the specified address family. 316 */ 317 public static InetAddress 318 getByAddress(String addr, int family) throws UnknownHostException { 319 if (family != IPv4 && family != IPv6) 320 throw new IllegalArgumentException("unknown address family"); 321 byte [] bytes; 322 bytes = toByteArray(addr, family); 323 if (bytes != null) 324 return InetAddress.getByAddress(addr, bytes); 325 throw new UnknownHostException("Invalid address: " + addr); 326 } 327 328 /** 329 * Determines the hostname for an address 330 * @param addr The address to look up 331 * @return The associated host name 332 * @exception UnknownHostException There is no hostname for the address 333 */ 334 public static String 335 getHostName(InetAddress addr) throws UnknownHostException { 336 Name name = ReverseMap.fromAddress(addr); 337 Record [] records = new Lookup(name, Type.PTR).run(); 338 if (records == null) 339 throw new UnknownHostException("unknown address"); 340 PTRRecord ptr = (PTRRecord) records[0]; 341 return ptr.getTarget().toString(); 342 } 343 344 /** 345 * Returns the family of an InetAddress. 346 * @param address The supplied address. 347 * @return The family, either IPv4 or IPv6. 348 */ 349 public static int 350 familyOf(InetAddress address) { 351 if (address instanceof Inet4Address) 352 return IPv4; 353 if (address instanceof Inet6Address) 354 return IPv6; 355 throw new IllegalArgumentException("unknown address family"); 356 } 357 358 /** 359 * Returns the length of an address in a particular family. 360 * @param family The address family, either IPv4 or IPv6. 361 * @return The length of addresses in that family. 362 */ 363 public static int 364 addressLength(int family) { 365 if (family == IPv4) 366 return 4; 367 if (family == IPv6) 368 return 16; 369 throw new IllegalArgumentException("unknown address family"); 370 } 371 372 /** 373 * Truncates an address to the specified number of bits. For example, 374 * truncating the address 10.1.2.3 to 8 bits would yield 10.0.0.0. 375 * @param address The source address 376 * @param maskLength The number of bits to truncate the address to. 377 */ 378 public static InetAddress 379 truncate(InetAddress address, int maskLength) 380 { 381 int family = familyOf(address); 382 int maxMaskLength = addressLength(family) * 8; 383 if (maskLength < 0 || maskLength > maxMaskLength) 384 throw new IllegalArgumentException("invalid mask length"); 385 if (maskLength == maxMaskLength) 386 return address; 387 byte [] bytes = address.getAddress(); 388 for (int i = maskLength / 8 + 1; i < bytes.length; i++) 389 bytes[i] = 0; 390 int maskBits = maskLength % 8; 391 int bitmask = 0; 392 for (int i = 0; i < maskBits; i++) 393 bitmask |= (1 << (7 - i)); 394 bytes[maskLength / 8] &= bitmask; 395 try { 396 return InetAddress.getByAddress(bytes); 397 } catch (UnknownHostException e) { 398 throw new IllegalArgumentException("invalid address"); 399 } 400 } 401 402 } 403