1// Copyright (c) 1999-2004 Brian Wellington (bwelling@xbill.org)
2
3package org.xbill.DNS;
4
5import java.net.*;
6import 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
15public final class Address {
16
17public static final int IPv4 = 1;
18public static final int IPv6 = 2;
19
20private
21Address() {}
22
23private static byte []
24parseV4(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
72private static byte []
73parseV6(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 */
165public static int []
166toArray(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 */
181public static int []
182toArray(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 */
192public static byte []
193toByteArray(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 */
207public static boolean
208isDottedQuad(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 */
218public static String
219toDottedQuad(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 */
229public static String
230toDottedQuad(int [] addr) {
231	return (addr[0] + "." + addr[1] + "." + addr[2] + "." + addr[3]);
232}
233
234private static Record []
235lookupHostName(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
247private static InetAddress
248addrFromRecord(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 */
259public static InetAddress
260getByName(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 */
275public static InetAddress []
276getAllByName(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 */
296public static InetAddress
297getByAddress(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 */
317public static InetAddress
318getByAddress(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 */
334public static String
335getHostName(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 */
349public static int
350familyOf(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 */
363public static int
364addressLength(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 */
378public static InetAddress
379truncate(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