1 // Copyright (c) 1999-2004 Brian Wellington (bwelling@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
Address()21 Address() {}
22
23 private static byte []
parseV4(String s)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 []
parseV6(String s)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 []
toArray(String s, int family)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 []
toArray(String s)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 []
toByteArray(String s, int family)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
isDottedQuad(String s)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
toDottedQuad(byte [] addr)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
toDottedQuad(int [] addr)230 toDottedQuad(int [] addr) {
231 return (addr[0] + "." + addr[1] + "." + addr[2] + "." + addr[3]);
232 }
233
234 private static Record []
lookupHostName(String name)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
addrFromRecord(String name, Record r)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
getByName(String name)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 []
getAllByName(String name)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
getByAddress(String addr)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
getByAddress(String addr, int family)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
getHostName(InetAddress addr)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
familyOf(InetAddress address)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
addressLength(int family)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
truncate(InetAddress address, int maskLength)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