Home | History | Annotate | Download | only in DNS
      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