1 /*
2 * Copyright 2012 The Netty Project
3 *
4 * The Netty Project licenses this file to you under the Apache License,
5 * version 2.0 (the "License"); you may not use this file except in compliance
6 * with the License. You may obtain a copy of the License at:
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12 * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13 * License for the specific language governing permissions and limitations
14 * under the License.
15 */
16 package org.jboss.netty.util;
17
18 import org.jboss.netty.logging.InternalLogger;
19 import org.jboss.netty.logging.InternalLoggerFactory;
20
21 import java.net.Inet4Address;
22 import java.net.Inet6Address;
23 import java.net.InetAddress;
24 import java.net.UnknownHostException;
25 import java.util.ArrayList;
26 import java.util.StringTokenizer;
27
28 /**
29 * A class that holds a number of network-related constants.
30 * <p/>
31 * This class borrowed some of its methods from a modified fork of the
32 * <a href="http://svn.apache.org/repos/asf/harmony/enhanced/java/branches/java6/classlib/modules/luni/
33 * src/main/java/org/apache/harmony/luni/util/Inet6Util.java">Inet6Util class</a> which was part of Apache Harmony.
34 */
35 public final class NetUtil {
36
37 /**
38 * This defines how many words (represented as ints) are needed to represent an IPv6 address
39 */
40 private static final int IPV6_WORD_COUNT = 8;
41
42 /**
43 * The maximum number of characters for an IPV6 string with no scope
44 */
45 private static final int IPV6_MAX_CHAR_COUNT = 39;
46
47 /**
48 * Number of bytes needed to represent and IPV6 value
49 */
50 private static final int IPV6_BYTE_COUNT = 16;
51
52 /**
53 * Maximum amount of value adding characters in between IPV6 separators
54 */
55 private static final int IPV6_MAX_CHAR_BETWEEN_SEPARATOR = 4;
56
57 /**
58 * Minimum number of separators that must be present in an IPv6 string
59 */
60 private static final int IPV6_MIN_SEPARATORS = 2;
61
62 /**
63 * Maximum number of separators that must be present in an IPv6 string
64 */
65 private static final int IPV6_MAX_SEPARATORS = 8;
66
67 /**
68 * Number of bytes needed to represent and IPV4 value
69 */
70 private static final int IPV4_BYTE_COUNT = 4;
71
72 /**
73 * Maximum amount of value adding characters in between IPV4 separators
74 */
75 private static final int IPV4_MAX_CHAR_BETWEEN_SEPARATOR = 3;
76
77 /**
78 * Number of separators that must be present in an IPv4 string
79 */
80 private static final int IPV4_SEPARATORS = 3;
81
82 /**
83 * The logger being used by this class
84 */
85 private static final InternalLogger logger = InternalLoggerFactory.getInstance(NetUtil.class);
86
87 /**
88 * Creates an byte[] based on an ipAddressString. No error handling is
89 * performed here.
90 */
91 public static byte[] createByteArrayFromIpAddressString(String ipAddressString) {
92
93 if (isValidIpV4Address(ipAddressString)) {
94 StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ".");
95 String token;
96 int tempInt;
97 byte[] byteAddress = new byte[IPV4_BYTE_COUNT];
98 for (int i = 0; i < IPV4_BYTE_COUNT; i ++) {
99 token = tokenizer.nextToken();
100 tempInt = Integer.parseInt(token);
101 byteAddress[i] = (byte) tempInt;
102 }
103
104 return byteAddress;
105 }
106
107 if (isValidIpV6Address(ipAddressString)) {
108 if (ipAddressString.charAt(0) == '[') {
109 ipAddressString = ipAddressString.substring(1, ipAddressString.length() - 1);
110 }
111
112 int percentPos = ipAddressString.indexOf('%');
113 if (percentPos >= 0) {
114 ipAddressString = ipAddressString.substring(0, percentPos);
115 }
116
117 StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ":.", true);
118 ArrayList<String> hexStrings = new ArrayList<String>();
119 ArrayList<String> decStrings = new ArrayList<String>();
120 String token = "";
121 String prevToken = "";
122 int doubleColonIndex = -1; // If a double colon exists, we need to
123 // insert 0s.
124
125 // Go through the tokens, including the seperators ':' and '.'
126 // When we hit a : or . the previous token will be added to either
127 // the hex list or decimal list. In the case where we hit a ::
128 // we will save the index of the hexStrings so we can add zeros
129 // in to fill out the string
130 while (tokenizer.hasMoreTokens()) {
131 prevToken = token;
132 token = tokenizer.nextToken();
133
134 if (":".equals(token)) {
135 if (":".equals(prevToken)) {
136 doubleColonIndex = hexStrings.size();
137 } else if (prevToken.length() != 0) {
138 hexStrings.add(prevToken);
139 }
140 } else if (".".equals(token)) {
141 decStrings.add(prevToken);
142 }
143 }
144
145 if (":".equals(prevToken)) {
146 if (":".equals(token)) {
147 doubleColonIndex = hexStrings.size();
148 } else {
149 hexStrings.add(token);
150 }
151 } else if (".".equals(prevToken)) {
152 decStrings.add(token);
153 }
154
155 // figure out how many hexStrings we should have
156 // also check if it is a IPv4 address
157 int hexStringsLength = 8;
158
159 // If we have an IPv4 address tagged on at the end, subtract
160 // 4 bytes, or 2 hex words from the total
161 if (!decStrings.isEmpty()) {
162 hexStringsLength -= 2;
163 }
164
165 // if we hit a double Colon add the appropriate hex strings
166 if (doubleColonIndex != -1) {
167 int numberToInsert = hexStringsLength - hexStrings.size();
168 for (int i = 0; i < numberToInsert; i ++) {
169 hexStrings.add(doubleColonIndex, "0");
170 }
171 }
172
173 byte[] ipByteArray = new byte[IPV6_BYTE_COUNT];
174
175 // Finally convert these strings to bytes...
176 for (int i = 0; i < hexStrings.size(); i ++) {
177 convertToBytes(hexStrings.get(i), ipByteArray, i << 1);
178 }
179
180 // Now if there are any decimal values, we know where they go...
181 for (int i = 0; i < decStrings.size(); i ++) {
182 ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings.get(i)) & 255);
183 }
184 return ipByteArray;
185 }
186 return null;
187 }
188
189 /**
190 * Converts a 4 character hex word into a 2 byte word equivalent
191 */
192 private static void convertToBytes(String hexWord, byte[] ipByteArray, int byteIndex) {
193
194 int hexWordLength = hexWord.length();
195 int hexWordIndex = 0;
196 ipByteArray[byteIndex] = 0;
197 ipByteArray[byteIndex + 1] = 0;
198 int charValue;
199
200 // high order 4 bits of first byte
201 if (hexWordLength > 3) {
202 charValue = getIntValue(hexWord.charAt(hexWordIndex ++));
203 ipByteArray[byteIndex] |= charValue << 4;
204 }
205
206 // low order 4 bits of the first byte
207 if (hexWordLength > 2) {
208 charValue = getIntValue(hexWord.charAt(hexWordIndex ++));
209 ipByteArray[byteIndex] |= charValue;
210 }
211
212 // high order 4 bits of second byte
213 if (hexWordLength > 1) {
214 charValue = getIntValue(hexWord.charAt(hexWordIndex ++));
215 ipByteArray[byteIndex + 1] |= charValue << 4;
216 }
217
218 // low order 4 bits of the first byte
219 charValue = getIntValue(hexWord.charAt(hexWordIndex));
220 ipByteArray[byteIndex + 1] |= charValue & 15;
221 }
222
223 static int getIntValue(char c) {
224
225 switch (c) {
226 case '0':
227 return 0;
228 case '1':
229 return 1;
230 case '2':
231 return 2;
232 case '3':
233 return 3;
234 case '4':
235 return 4;
236 case '5':
237 return 5;
238 case '6':
239 return 6;
240 case '7':
241 return 7;
242 case '8':
243 return 8;
244 case '9':
245 return 9;
246 }
247
248 c = Character.toLowerCase(c);
249 switch (c) {
250 case 'a':
251 return 10;
252 case 'b':
253 return 11;
254 case 'c':
255 return 12;
256 case 'd':
257 return 13;
258 case 'e':
259 return 14;
260 case 'f':
261 return 15;
262 }
263 return 0;
264 }
265
266 public static boolean isValidIpV6Address(String ipAddress) {
267 int length = ipAddress.length();
268 boolean doubleColon = false;
269 int numberOfColons = 0;
270 int numberOfPeriods = 0;
271 StringBuilder word = new StringBuilder();
272 char c = 0;
273 char prevChar;
274 int startOffset = 0; // offset for [] ip addresses
275 int endOffset = ipAddress.length();
276
277 if (endOffset < 2) {
278 return false;
279 }
280
281 // Strip []
282 if (ipAddress.charAt(0) == '[') {
283 if (ipAddress.charAt(endOffset - 1) != ']') {
284 return false; // must have a close ]
285 }
286
287 startOffset = 1;
288 endOffset --;
289 }
290
291 // Strip the interface name/index after the percent sign.
292 int percentIdx = ipAddress.indexOf('%', startOffset);
293 if (percentIdx >= 0) {
294 endOffset = percentIdx;
295 }
296
297 for (int i = startOffset; i < endOffset; i ++) {
298 prevChar = c;
299 c = ipAddress.charAt(i);
300 switch (c) {
301 // case for the last 32-bits represented as IPv4 x:x:x:x:x:x:d.d.d.d
302 case '.':
303 numberOfPeriods ++;
304 if (numberOfPeriods > 3) {
305 return false;
306 }
307 if (!isValidIp4Word(word.toString())) {
308 return false;
309 }
310 if (numberOfColons != 6 && !doubleColon) {
311 return false;
312 }
313 // a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an
314 // IPv4 ending, otherwise 7 :'s is bad
315 if (numberOfColons == 7 && ipAddress.charAt(startOffset) != ':' &&
316 ipAddress.charAt(1 + startOffset) != ':') {
317 return false;
318 }
319 word.delete(0, word.length());
320 break;
321
322 case ':':
323 // FIX "IP6 mechanism syntax #ip6-bad1"
324 // An IPV6 address cannot start with a single ":".
325 // Either it can starti with "::" or with a number.
326 if (i == startOffset && (ipAddress.length() <= i || ipAddress.charAt(i + 1) != ':')) {
327 return false;
328 }
329 // END FIX "IP6 mechanism syntax #ip6-bad1"
330 numberOfColons ++;
331 if (numberOfColons > 7) {
332 return false;
333 }
334 if (numberOfPeriods > 0) {
335 return false;
336 }
337 if (prevChar == ':') {
338 if (doubleColon) {
339 return false;
340 }
341 doubleColon = true;
342 }
343 word.delete(0, word.length());
344 break;
345
346 default:
347 if (word != null && word.length() > 3) {
348 return false;
349 }
350 if (!isValidHexChar(c)) {
351 return false;
352 }
353 word.append(c);
354 }
355 }
356
357 // Check if we have an IPv4 ending
358 if (numberOfPeriods > 0) {
359 // There is a test case with 7 colons and valid ipv4 this should resolve it
360 if (numberOfPeriods != 3 || !(isValidIp4Word(word.toString()) && numberOfColons < 7)) {
361 return false;
362 }
363 } else {
364 // If we're at then end and we haven't had 7 colons then there is a
365 // problem unless we encountered a doubleColon
366 if (numberOfColons != 7 && !doubleColon) {
367 return false;
368 }
369
370 // If we have an empty word at the end, it means we ended in either
371 // a : or a .
372 // If we did not end in :: then this is invalid
373 if (word.length() == 0 && ipAddress.charAt(length - 1 - startOffset) == ':' &&
374 ipAddress.charAt(length - 2 - startOffset) != ':') {
375 return false;
376 }
377 }
378
379 return true;
380 }
381
382 public static boolean isValidIp4Word(String word) {
383 char c;
384 if (word.length() < 1 || word.length() > 3) {
385 return false;
386 }
387 for (int i = 0; i < word.length(); i ++) {
388 c = word.charAt(i);
389 if (!(c >= '0' && c <= '9')) {
390 return false;
391 }
392 }
393 return Integer.parseInt(word) <= 255;
394 }
395
396 private static boolean isValidHexChar(char c) {
397 return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f';
398 }
399
400 private static boolean isValidNumericChar(char c) {
401 return c >= '0' && c <= '9';
402 }
403
404 /**
405 * Takes a string and parses it to see if it is a valid IPV4 address.
406 *
407 * @return true, if the string represents an IPV4 address in dotted
408 * notation, false otherwise
409 */
410 public static boolean isValidIpV4Address(String value) {
411
412 int periods = 0;
413 int i;
414 int length = value.length();
415
416 if (length > 15) {
417 return false;
418 }
419 char c;
420 StringBuilder word = new StringBuilder();
421 for (i = 0; i < length; i ++) {
422 c = value.charAt(i);
423 if (c == '.') {
424 periods ++;
425 if (periods > 3) {
426 return false;
427 }
428 if (word.length() == 0) {
429 return false;
430 }
431 if (Integer.parseInt(word.toString()) > 255) {
432 return false;
433 }
434 word.delete(0, word.length());
435 } else if (!Character.isDigit(c)) {
436 return false;
437 } else {
438 if (word.length() > 2) {
439 return false;
440 }
441 word.append(c);
442 }
443 }
444
445 if (word.length() == 0 || Integer.parseInt(word.toString()) > 255) {
446 return false;
447 }
448
449 return periods == 3;
450 }
451
452 /**
453 * Returns the {@link Inet6Address} representation of a {@link CharSequence} IP address.
454 * <p>
455 * This method will treat all IPv4 type addresses as "IPv4 mapped" (see {@link #getByName(CharSequence, boolean)})
456 * @param ip {@link CharSequence} IP address to be converted to a {@link Inet6Address}
457 * @return {@link Inet6Address} representation of the {@code ip} or {@code null} if not a valid IP address.
458 */
459 public static Inet6Address getByName(CharSequence ip) {
460 return getByName(ip, true);
461 }
462
463 /**
464 * Returns the {@link Inet6Address} representation of a {@link CharSequence} IP address.
465 * <p>
466 * The {@code ipv4Mapped} parameter specifies how IPv4 addresses should be treated.
467 * "IPv4 mapped" format as
468 * defined in <a href="http://tools.ietf.org/html/rfc4291#section-2.5.5">rfc 4291 section 2</a> is supported.
469 * @param ip {@link CharSequence} IP address to be converted to a {@link Inet6Address}
470 * @param ipv4Mapped
471 * <ul>
472 * <li>{@code true} To allow IPv4 mapped inputs to be translated into {@link Inet6Address}</li>
473 * <li>{@code false} Don't turn IPv4 addressed to mapped addresses</li>
474 * </ul>
475 * @return {@link Inet6Address} representation of the {@code ip} or {@code null} if not a valid IP address.
476 */
477 public static Inet6Address getByName(CharSequence ip, boolean ipv4Mapped) {
478 final byte[] bytes = new byte[IPV6_BYTE_COUNT];
479 final int ipLength = ip.length();
480 int compressBegin = 0;
481 int compressLength = 0;
482 int currentIndex = 0;
483 int value = 0;
484 int begin = -1;
485 int i = 0;
486 int ipv6Seperators = 0;
487 int ipv4Seperators = 0;
488 int tmp;
489 boolean needsShift = false;
490 for (; i < ipLength; ++i) {
491 final char c = ip.charAt(i);
492 switch (c) {
493 case ':':
494 ++ipv6Seperators;
495 if (i - begin > IPV6_MAX_CHAR_BETWEEN_SEPARATOR ||
496 ipv4Seperators > 0 || ipv6Seperators > IPV6_MAX_SEPARATORS ||
497 currentIndex + 1 >= bytes.length) {
498 return null;
499 }
500 value <<= (IPV6_MAX_CHAR_BETWEEN_SEPARATOR - (i - begin)) << 2;
501
502 if (compressLength > 0) {
503 compressLength -= 2;
504 }
505
506 // The value integer holds at most 4 bytes from right (most significant) to left (least significant).
507 // The following bit shifting is used to extract and re-order the individual bytes to achieve a
508 // left (most significant) to right (least significant) ordering.
509 bytes[currentIndex++] = (byte) (((value & 0xf) << 4) | ((value >> 4) & 0xf));
510 bytes[currentIndex++] = (byte) ((((value >> 8) & 0xf) << 4) | ((value >> 12) & 0xf));
511 tmp = i + 1;
512 if (tmp < ipLength && ip.charAt(tmp) == ':') {
513 ++tmp;
514 if (compressBegin != 0 || (tmp < ipLength && ip.charAt(tmp) == ':')) {
515 return null;
516 }
517 ++ipv6Seperators;
518 needsShift = ipv6Seperators == 2 && value == 0;
519 compressBegin = currentIndex;
520 compressLength = bytes.length - compressBegin - 2;
521 ++i;
522 }
523 value = 0;
524 begin = -1;
525 break;
526 case '.':
527 ++ipv4Seperators;
528 if (i - begin > IPV4_MAX_CHAR_BETWEEN_SEPARATOR
529 || ipv4Seperators > IPV4_SEPARATORS
530 || (ipv6Seperators > 0 && (currentIndex + compressLength < 12))
531 || i + 1 >= ipLength
532 || currentIndex >= bytes.length
533 || begin < 0
534 || (begin == 0 && (i == 3 && (!isValidNumericChar(ip.charAt(2)) ||
535 !isValidNumericChar(ip.charAt(1)) ||
536 !isValidNumericChar(ip.charAt(0))) ||
537 i == 2 && (!isValidNumericChar(ip.charAt(1)) ||
538 !isValidNumericChar(ip.charAt(0))) ||
539 i == 1 && !isValidNumericChar(ip.charAt(0))))) {
540 return null;
541 }
542 value <<= (IPV4_MAX_CHAR_BETWEEN_SEPARATOR - (i - begin)) << 2;
543
544 // The value integer holds at most 3 bytes from right (most significant) to left (least significant).
545 // The following bit shifting is to restructure the bytes to be left (most significant) to
546 // right (least significant) while also accounting for each IPv4 digit is base 10.
547 begin = (value & 0xf) * 100 + ((value >> 4) & 0xf) * 10 + ((value >> 8) & 0xf);
548 if (begin < 0 || begin > 255) {
549 return null;
550 }
551 bytes[currentIndex++] = (byte) begin;
552 value = 0;
553 begin = -1;
554 break;
555 default:
556 if (!isValidHexChar(c) || (ipv4Seperators > 0 && !isValidNumericChar(c))) {
557 return null;
558 }
559 if (begin < 0) {
560 begin = i;
561 } else if (i - begin > IPV6_MAX_CHAR_BETWEEN_SEPARATOR) {
562 return null;
563 }
564 // The value is treated as a sort of array of numbers because we are dealing with
565 // at most 4 consecutive bytes we can use bit shifting to accomplish this.
566 // The most significant byte will be encountered first, and reside in the right most
567 // position of the following integer
568 value += getIntValue(c) << ((i - begin) << 2);
569 break;
570 }
571 }
572
573 final boolean isCompressed = compressBegin > 0;
574 // Finish up last set of data that was accumulated in the loop (or before the loop)
575 if (ipv4Seperators > 0) {
576 if (begin > 0 && i - begin > IPV4_MAX_CHAR_BETWEEN_SEPARATOR ||
577 ipv4Seperators != IPV4_SEPARATORS ||
578 currentIndex >= bytes.length) {
579 return null;
580 }
581 if (ipv6Seperators == 0) {
582 compressLength = 12;
583 } else if (ipv6Seperators >= IPV6_MIN_SEPARATORS &&
584 ip.charAt(ipLength - 1) != ':' &&
585 (!isCompressed && (ipv6Seperators == 6 && ip.charAt(0) != ':') ||
586 isCompressed && (ipv6Seperators + 1 < IPV6_MAX_SEPARATORS &&
587 (ip.charAt(0) != ':' || compressBegin <= 2)))) {
588 compressLength -= 2;
589 } else {
590 return null;
591 }
592 value <<= (IPV4_MAX_CHAR_BETWEEN_SEPARATOR - (i - begin)) << 2;
593
594 // The value integer holds at most 3 bytes from right (most significant) to left (least significant).
595 // The following bit shifting is to restructure the bytes to be left (most significant) to
596 // right (least significant) while also accounting for each IPv4 digit is base 10.
597 begin = (value & 0xf) * 100 + ((value >> 4) & 0xf) * 10 + ((value >> 8) & 0xf);
598 if (begin < 0 || begin > 255) {
599 return null;
600 }
601 bytes[currentIndex++] = (byte) begin;
602 } else {
603 tmp = ipLength - 1;
604 if (begin > 0 && i - begin > IPV6_MAX_CHAR_BETWEEN_SEPARATOR ||
605 ipv6Seperators < IPV6_MIN_SEPARATORS ||
606 !isCompressed && (ipv6Seperators + 1 != IPV6_MAX_SEPARATORS ||
607 ip.charAt(0) == ':' || ip.charAt(tmp) == ':') ||
608 isCompressed && (ipv6Seperators > IPV6_MAX_SEPARATORS ||
609 (ipv6Seperators == IPV6_MAX_SEPARATORS &&
610 (compressBegin <= 2 && ip.charAt(0) != ':' ||
611 compressBegin >= 14 && ip.charAt(tmp) != ':'))) ||
612 currentIndex + 1 >= bytes.length) {
613 return null;
614 }
615 if (begin >= 0 && i - begin <= IPV6_MAX_CHAR_BETWEEN_SEPARATOR) {
616 value <<= (IPV6_MAX_CHAR_BETWEEN_SEPARATOR - (i - begin)) << 2;
617 }
618 // The value integer holds at most 4 bytes from right (most significant) to left (least significant).
619 // The following bit shifting is used to extract and re-order the individual bytes to achieve a
620 // left (most significant) to right (least significant) ordering.
621 bytes[currentIndex++] = (byte) (((value & 0xf) << 4) | ((value >> 4) & 0xf));
622 bytes[currentIndex++] = (byte) ((((value >> 8) & 0xf) << 4) | ((value >> 12) & 0xf));
623 }
624
625 i = currentIndex + compressLength;
626 if (needsShift || i >= bytes.length) {
627 // Right shift array
628 if (i >= bytes.length) {
629 ++compressBegin;
630 }
631 for (i = currentIndex; i < bytes.length; ++i) {
632 for (begin = bytes.length - 1; begin >= compressBegin; --begin) {
633 bytes[begin] = bytes[begin - 1];
634 }
635 bytes[begin] = 0;
636 ++compressBegin;
637 }
638 } else {
639 // Selectively move elements
640 for (i = 0; i < compressLength; ++i) {
641 begin = i + compressBegin;
642 currentIndex = begin + compressLength;
643 if (currentIndex < bytes.length) {
644 bytes[currentIndex] = bytes[begin];
645 bytes[begin] = 0;
646 } else {
647 break;
648 }
649 }
650 }
651
652 if (ipv4Mapped && ipv4Seperators > 0 &&
653 bytes[0] == 0 && bytes[1] == 0 && bytes[2] == 0 && bytes[3] == 0 && bytes[4] == 0 &&
654 bytes[5] == 0 && bytes[6] == 0 && bytes[7] == 0 && bytes[8] == 0 && bytes[9] == 0) {
655 bytes[10] = bytes[11] = (byte) 0xff;
656 }
657
658 try {
659 return Inet6Address.getByAddress(null, bytes, -1);
660 } catch (UnknownHostException e) {
661 throw new RuntimeException(e); // Should never happen
662 }
663 }
664
665 /**
666 * Returns the {@link String} representation of an {@link InetAddress}.
667 * <ul>
668 * <li>Inet4Address results are identical to {@link InetAddress#getHostAddress()}</li>
669 * <li>Inet6Address results adhere to
670 * <a href="http://tools.ietf.org/html/rfc5952#section-4">rfc 5952 section 4</a></li>
671 * </ul>
672 * <p>
673 * The output does not include Scope ID.
674 * @param ip {@link InetAddress} to be converted to an address string
675 * @return {@code String} containing the text-formatted IP address
676 */
677 public static String toAddressString(InetAddress ip) {
678 return toAddressString(ip, false);
679 }
680
681 /**
682 * Returns the {@link String} representation of an {@link InetAddress}.
683 * <ul>
684 * <li>Inet4Address results are identical to {@link InetAddress#getHostAddress()}</li>
685 * <li>Inet6Address results adhere to
686 * <a href="http://tools.ietf.org/html/rfc5952#section-4">rfc 5952 section 4</a> if
687 * {@code ipv4Mapped} is false. If {@code ipv4Mapped} is true then "IPv4 mapped" format
688 * from <a href="http://tools.ietf.org/html/rfc4291#section-2.5.5">rfc 4291 section 2</a> will be supported.
689 * The compressed result will always obey the compression rules defined in
690 * <a href="http://tools.ietf.org/html/rfc5952#section-4">rfc 5952 section 4</a></li>
691 * </ul>
692 * <p>
693 * The output does not include Scope ID.
694 * @param ip {@link InetAddress} to be converted to an address string
695 * @param ipv4Mapped
696 * <ul>
697 * <li>{@code true} to stray from strict rfc 5952 and support the "IPv4 mapped" format
698 * defined in <a href="http://tools.ietf.org/html/rfc4291#section-2.5.5">rfc 4291 section 2</a> while still
699 * following the updated guidelines in
700 * <a href="http://tools.ietf.org/html/rfc5952#section-4">rfc 5952 section 4</a></li>
701 * <li>{@code false} to strictly follow rfc 5952</li>
702 * </ul>
703 * @return {@code String} containing the text-formatted IP address
704 */
705 public static String toAddressString(InetAddress ip, boolean ipv4Mapped) {
706 if (ip instanceof Inet4Address) {
707 return ip.getHostAddress();
708 }
709 if (!(ip instanceof Inet6Address)) {
710 throw new IllegalArgumentException("Unhandled type: " + ip.getClass());
711 }
712
713 final byte[] bytes = ip.getAddress();
714 final int[] words = new int[IPV6_WORD_COUNT];
715 int i;
716 for (i = 0; i < words.length; ++i) {
717 words[i] = ((bytes[i << 1] & 0xff) << 8) | (bytes[(i << 1) + 1] & 0xff);
718 }
719
720 // Find longest run of 0s, tie goes to first found instance
721 int currentStart = -1;
722 int currentLength = 0;
723 int shortestStart = -1;
724 int shortestLength = 0;
725 for (i = 0; i < words.length; ++i) {
726 if (words[i] == 0) {
727 if (currentStart < 0) {
728 currentStart = i;
729 }
730 } else if (currentStart >= 0) {
731 currentLength = i - currentStart;
732 if (currentLength > shortestLength) {
733 shortestStart = currentStart;
734 shortestLength = currentLength;
735 }
736 currentStart = -1;
737 }
738 }
739 // If the array ends on a streak of zeros, make sure we account for it
740 if (currentStart >= 0) {
741 currentLength = i - currentStart;
742 if (currentLength > shortestLength) {
743 shortestStart = currentStart;
744 shortestLength = currentLength;
745 }
746 }
747 // Ignore the longest streak if it is only 1 long
748 if (shortestLength == 1) {
749 shortestLength = 0;
750 shortestStart = -1;
751 }
752
753 // Translate to string taking into account longest consecutive 0s
754 final int shortestEnd = shortestStart + shortestLength;
755 final StringBuilder b = new StringBuilder(IPV6_MAX_CHAR_COUNT);
756 if (shortestEnd < 0) { // Optimization when there is no compressing needed
757 b.append(Integer.toHexString(words[0]));
758 for (i = 1; i < words.length; ++i) {
759 b.append(':');
760 b.append(Integer.toHexString(words[i]));
761 }
762 } else { // General case that can handle compressing (and not compressing)
763 // Loop unroll the first index (so we don't constantly check i==0 cases in loop)
764 final boolean isIpv4Mapped;
765 if (inRangeEndExclusive(0, shortestStart, shortestEnd)) {
766 b.append("::");
767 isIpv4Mapped = ipv4Mapped && (shortestEnd == 5 && words[5] == 0xffff);
768 } else {
769 b.append(Integer.toHexString(words[0]));
770 isIpv4Mapped = false;
771 }
772 for (i = 1; i < words.length; ++i) {
773 if (!inRangeEndExclusive(i, shortestStart, shortestEnd)) {
774 if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) {
775 // If the last index was not part of the shortened sequence
776 if (!isIpv4Mapped || i == 6) {
777 b.append(':');
778 } else {
779 b.append('.');
780 }
781 }
782 if (isIpv4Mapped && i > 5) {
783 b.append(words[i] >> 8);
784 b.append('.');
785 b.append(words[i] & 0xff);
786 } else {
787 b.append(Integer.toHexString(words[i]));
788 }
789 } else if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) {
790 // If we are in the shortened sequence and the last index was not
791 b.append("::");
792 }
793 }
794 }
795
796 return b.toString();
797 }
798
799 /**
800 * Does a range check on {@code value} if is within {@code start} (inclusive) and {@code end} (exclusive).
801 * @param value The value to checked if is within {@code start} (inclusive) and {@code end} (exclusive)
802 * @param start The start of the range (inclusive)
803 * @param end The end of the range (exclusive)
804 * @return
805 * <ul>
806 * <li>{@code true} if {@code value} if is within {@code start} (inclusive) and {@code end} (exclusive)</li>
807 * <li>{@code false} otherwise</li>
808 * </ul>
809 */
810 private static boolean inRangeEndExclusive(int value, int start, int end) {
811 return value >= start && value < end;
812 }
813
814 /**
815 * A constructor to stop this class being constructed.
816 */
817 private NetUtil() {
818 // Unused
819 }
820 }