1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16 package io.netty.util;
17
18 import io.netty.util.internal.PlatformDependent;
19 import io.netty.util.internal.SocketUtils;
20 import io.netty.util.internal.StringUtil;
21 import io.netty.util.internal.SystemPropertyUtil;
22 import io.netty.util.internal.logging.InternalLogger;
23 import io.netty.util.internal.logging.InternalLoggerFactory;
24
25 import java.io.BufferedReader;
26 import java.io.File;
27 import java.io.FileReader;
28 import java.net.Inet4Address;
29 import java.net.Inet6Address;
30 import java.net.InetAddress;
31 import java.net.InetSocketAddress;
32 import java.net.NetworkInterface;
33 import java.net.SocketException;
34 import java.net.UnknownHostException;
35 import java.security.AccessController;
36 import java.security.PrivilegedAction;
37 import java.util.ArrayList;
38 import java.util.Enumeration;
39 import java.util.List;
40
41
42
43
44
45
46
47
48 public final class NetUtil {
49
50
51
52
53 public static final Inet4Address LOCALHOST4;
54
55
56
57
58 public static final Inet6Address LOCALHOST6;
59
60
61
62
63
64 public static final InetAddress LOCALHOST;
65
66
67
68
69 public static final NetworkInterface LOOPBACK_IF;
70
71
72
73
74
75 public static final int SOMAXCONN;
76
77
78
79
80 private static final int IPV6_WORD_COUNT = 8;
81
82
83
84
85 private static final int IPV6_MAX_CHAR_COUNT = 39;
86
87
88
89
90 private static final int IPV6_BYTE_COUNT = 16;
91
92
93
94
95 private static final int IPV6_MAX_CHAR_BETWEEN_SEPARATOR = 4;
96
97
98
99
100 private static final int IPV6_MIN_SEPARATORS = 2;
101
102
103
104
105 private static final int IPV6_MAX_SEPARATORS = 8;
106
107
108
109
110 private static final int IPV4_MAX_CHAR_BETWEEN_SEPARATOR = 3;
111
112
113
114
115 private static final int IPV4_SEPARATORS = 3;
116
117
118
119
120 private static final boolean IPV4_PREFERRED = SystemPropertyUtil.getBoolean("java.net.preferIPv4Stack", false);
121
122
123
124
125 private static final InternalLogger logger = InternalLoggerFactory.getInstance(NetUtil.class);
126
127 static {
128 byte[] LOCALHOST4_BYTES = {127, 0, 0, 1};
129 byte[] LOCALHOST6_BYTES = {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1};
130
131
132 Inet4Address localhost4 = null;
133 try {
134 localhost4 = (Inet4Address) InetAddress.getByAddress(LOCALHOST4_BYTES);
135 } catch (Exception e) {
136
137 PlatformDependent.throwException(e);
138 }
139 LOCALHOST4 = localhost4;
140
141
142 Inet6Address localhost6 = null;
143 try {
144 localhost6 = (Inet6Address) InetAddress.getByAddress(LOCALHOST6_BYTES);
145 } catch (Exception e) {
146
147 PlatformDependent.throwException(e);
148 }
149 LOCALHOST6 = localhost6;
150
151
152 List<NetworkInterface> ifaces = new ArrayList<NetworkInterface>();
153 try {
154 Enumeration<NetworkInterface> interfaces = NetworkInterface.getNetworkInterfaces();
155 if (interfaces != null) {
156 while (interfaces.hasMoreElements()) {
157 NetworkInterface iface = interfaces.nextElement();
158
159 if (SocketUtils.addressesFromNetworkInterface(iface).hasMoreElements()) {
160 ifaces.add(iface);
161 }
162 }
163 }
164 } catch (SocketException e) {
165 logger.warn("Failed to retrieve the list of available network interfaces", e);
166 }
167
168
169
170
171 NetworkInterface loopbackIface = null;
172 InetAddress loopbackAddr = null;
173 loop: for (NetworkInterface iface: ifaces) {
174 for (Enumeration<InetAddress> i = SocketUtils.addressesFromNetworkInterface(iface); i.hasMoreElements();) {
175 InetAddress addr = i.nextElement();
176 if (addr.isLoopbackAddress()) {
177
178 loopbackIface = iface;
179 loopbackAddr = addr;
180 break loop;
181 }
182 }
183 }
184
185
186 if (loopbackIface == null) {
187 try {
188 for (NetworkInterface iface: ifaces) {
189 if (iface.isLoopback()) {
190 Enumeration<InetAddress> i = SocketUtils.addressesFromNetworkInterface(iface);
191 if (i.hasMoreElements()) {
192
193 loopbackIface = iface;
194 loopbackAddr = i.nextElement();
195 break;
196 }
197 }
198 }
199
200 if (loopbackIface == null) {
201 logger.warn("Failed to find the loopback interface");
202 }
203 } catch (SocketException e) {
204 logger.warn("Failed to find the loopback interface", e);
205 }
206 }
207
208 if (loopbackIface != null) {
209
210 logger.debug(
211 "Loopback interface: {} ({}, {})",
212 loopbackIface.getName(), loopbackIface.getDisplayName(), loopbackAddr.getHostAddress());
213 } else {
214
215
216 if (loopbackAddr == null) {
217 try {
218 if (NetworkInterface.getByInetAddress(LOCALHOST6) != null) {
219 logger.debug("Using hard-coded IPv6 localhost address: {}", localhost6);
220 loopbackAddr = localhost6;
221 }
222 } catch (Exception e) {
223
224 } finally {
225 if (loopbackAddr == null) {
226 logger.debug("Using hard-coded IPv4 localhost address: {}", localhost4);
227 loopbackAddr = localhost4;
228 }
229 }
230 }
231 }
232
233 LOOPBACK_IF = loopbackIface;
234 LOCALHOST = loopbackAddr;
235
236
237
238
239 SOMAXCONN = AccessController.doPrivileged(new PrivilegedAction<Integer>() {
240 @Override
241 public Integer run() {
242
243
244
245
246 int somaxconn = PlatformDependent.isWindows() ? 200 : 128;
247 File file = new File("/proc/sys/net/core/somaxconn");
248 BufferedReader in = null;
249 try {
250
251
252
253 if (file.exists()) {
254 in = new BufferedReader(new FileReader(file));
255 somaxconn = Integer.parseInt(in.readLine());
256 if (logger.isDebugEnabled()) {
257 logger.debug("{}: {}", file, somaxconn);
258 }
259 } else {
260 if (logger.isDebugEnabled()) {
261 logger.debug("{}: {} (non-existent)", file, somaxconn);
262 }
263 }
264 } catch (Exception e) {
265 logger.debug("Failed to get SOMAXCONN from: {}", file, e);
266 } finally {
267 if (in != null) {
268 try {
269 in.close();
270 } catch (Exception e) {
271
272 }
273 }
274 }
275 return somaxconn;
276 }
277 });
278 }
279
280
281
282
283 public static boolean isIpV4StackPreferred() {
284 return IPV4_PREFERRED;
285 }
286
287
288
289
290 public static byte[] createByteArrayFromIpAddressString(String ipAddressString) {
291
292 if (isValidIpV4Address(ipAddressString)) {
293 return validIpV4ToBytes(ipAddressString);
294 }
295
296 if (isValidIpV6Address(ipAddressString)) {
297 if (ipAddressString.charAt(0) == '[') {
298 ipAddressString = ipAddressString.substring(1, ipAddressString.length() - 1);
299 }
300
301 int percentPos = ipAddressString.indexOf('%');
302 if (percentPos >= 0) {
303 ipAddressString = ipAddressString.substring(0, percentPos);
304 }
305
306 return getIPv6ByName(ipAddressString, true);
307 }
308 return null;
309 }
310
311 private static int decimalDigit(String str, int pos) {
312 return str.charAt(pos) - '0';
313 }
314
315 private static byte ipv4WordToByte(String ip, int from, int toExclusive) {
316 int ret = decimalDigit(ip, from);
317 from++;
318 if (from == toExclusive) {
319 return (byte) ret;
320 }
321 ret = ret * 10 + decimalDigit(ip, from);
322 from++;
323 if (from == toExclusive) {
324 return (byte) ret;
325 }
326 return (byte) (ret * 10 + decimalDigit(ip, from));
327 }
328
329
330 static byte[] validIpV4ToBytes(String ip) {
331 int i;
332 return new byte[] {
333 ipv4WordToByte(ip, 0, i = ip.indexOf('.', 1)),
334 ipv4WordToByte(ip, i + 1, i = ip.indexOf('.', i + 2)),
335 ipv4WordToByte(ip, i + 1, i = ip.indexOf('.', i + 2)),
336 ipv4WordToByte(ip, i + 1, ip.length())
337 };
338 }
339
340 public static boolean isValidIpV6Address(String ip) {
341 int end = ip.length();
342 if (end < 2) {
343 return false;
344 }
345
346
347 int start;
348 char c = ip.charAt(0);
349 if (c == '[') {
350 end--;
351 if (ip.charAt(end) != ']') {
352
353 return false;
354 }
355 start = 1;
356 c = ip.charAt(1);
357 } else {
358 start = 0;
359 }
360
361 int colons;
362 int compressBegin;
363 if (c == ':') {
364
365 if (ip.charAt(start + 1) != ':') {
366 return false;
367 }
368 colons = 2;
369 compressBegin = start;
370 start += 2;
371 } else {
372 colons = 0;
373 compressBegin = -1;
374 }
375
376 int wordLen = 0;
377 loop:
378 for (int i = start; i < end; i++) {
379 c = ip.charAt(i);
380 if (isValidHexChar(c)) {
381 if (wordLen < 4) {
382 wordLen++;
383 continue;
384 }
385 return false;
386 }
387
388 switch (c) {
389 case ':':
390 if (colons > 7) {
391 return false;
392 }
393 if (ip.charAt(i - 1) == ':') {
394 if (compressBegin >= 0) {
395 return false;
396 }
397 compressBegin = i - 1;
398 } else {
399 wordLen = 0;
400 }
401 colons++;
402 break;
403 case '.':
404
405
406
407 if (compressBegin < 0 && colons != 6 ||
408
409
410 (colons == 7 && compressBegin >= start || colons > 7)) {
411 return false;
412 }
413
414
415
416
417 int ipv4Start = i - wordLen;
418 int j = ipv4Start - 2;
419 if (isValidIPv4MappedChar(ip.charAt(j))) {
420 if (!isValidIPv4MappedChar(ip.charAt(j - 1)) ||
421 !isValidIPv4MappedChar(ip.charAt(j - 2)) ||
422 !isValidIPv4MappedChar(ip.charAt(j - 3))) {
423 return false;
424 }
425 j -= 5;
426 }
427
428 for (; j >= start; --j) {
429 char tmpChar = ip.charAt(j);
430 if (tmpChar != '0' && tmpChar != ':') {
431 return false;
432 }
433 }
434
435
436 int ipv4End = ip.indexOf('%', ipv4Start + 7);
437 if (ipv4End < 0) {
438 ipv4End = end;
439 }
440 return isValidIpV4Address(ip, ipv4Start, ipv4End);
441 case '%':
442
443 end = i;
444 break loop;
445 default:
446 return false;
447 }
448 }
449
450
451 if (compressBegin < 0) {
452 return colons == 7 && wordLen > 0;
453 }
454
455 return compressBegin + 2 == end ||
456
457 wordLen > 0 && (colons < 8 || compressBegin <= start);
458 }
459
460 private static boolean isValidIpV4Word(CharSequence word, int from, int toExclusive) {
461 int len = toExclusive - from;
462 char c0, c1, c2;
463 if (len < 1 || len > 3 || (c0 = word.charAt(from)) < '0') {
464 return false;
465 }
466 if (len == 3) {
467 return (c1 = word.charAt(from + 1)) >= '0' &&
468 (c2 = word.charAt(from + 2)) >= '0' &&
469 (c0 <= '1' && c1 <= '9' && c2 <= '9' ||
470 c0 == '2' && c1 <= '5' && (c2 <= '5' || c1 < '5' && c2 <= '9'));
471 }
472 return c0 <= '9' && (len == 1 || isValidNumericChar(word.charAt(from + 1)));
473 }
474
475 private static boolean isValidHexChar(char c) {
476 return c >= '0' && c <= '9' || c >= 'A' && c <= 'F' || c >= 'a' && c <= 'f';
477 }
478
479 private static boolean isValidNumericChar(char c) {
480 return c >= '0' && c <= '9';
481 }
482
483 private static boolean isValidIPv4MappedChar(char c) {
484 return c == 'f' || c == 'F';
485 }
486
487 private static boolean isValidIPv4MappedSeparators(byte b0, byte b1, boolean mustBeZero) {
488
489
490
491 return b0 == b1 && (b0 == 0 || !mustBeZero && b1 == -1);
492 }
493
494 private static boolean isValidIPv4Mapped(byte[] bytes, int currentIndex, int compressBegin, int compressLength) {
495 final boolean mustBeZero = compressBegin + compressLength >= 14;
496 return currentIndex <= 12 && currentIndex >= 2 && (!mustBeZero || compressBegin < 12) &&
497 isValidIPv4MappedSeparators(bytes[currentIndex - 1], bytes[currentIndex - 2], mustBeZero) &&
498 PlatformDependent.isZero(bytes, 0, currentIndex - 3);
499 }
500
501
502
503
504
505
506
507 public static boolean isValidIpV4Address(String ip) {
508 return isValidIpV4Address(ip, 0, ip.length());
509 }
510
511 @SuppressWarnings("DuplicateBooleanBranch")
512 private static boolean isValidIpV4Address(String ip, int from, int toExcluded) {
513 int len = toExcluded - from;
514 int i;
515 return len <= 15 && len >= 7 &&
516 (i = ip.indexOf('.', from + 1)) > 0 && isValidIpV4Word(ip, from, i) &&
517 (i = ip.indexOf('.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
518 (i = ip.indexOf('.', from = i + 2)) > 0 && isValidIpV4Word(ip, from - 1, i) &&
519 isValidIpV4Word(ip, i + 1, toExcluded);
520 }
521
522
523
524
525
526
527
528
529 public static Inet6Address getByName(CharSequence ip) {
530 return getByName(ip, true);
531 }
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547 public static Inet6Address getByName(CharSequence ip, boolean ipv4Mapped) {
548 byte[] bytes = getIPv6ByName(ip, ipv4Mapped);
549 if (bytes == null) {
550 return null;
551 }
552 try {
553 return Inet6Address.getByAddress(null, bytes, -1);
554 } catch (UnknownHostException e) {
555 throw new RuntimeException(e);
556 }
557 }
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573 private static byte[] getIPv6ByName(CharSequence ip, boolean ipv4Mapped) {
574 final byte[] bytes = new byte[IPV6_BYTE_COUNT];
575 final int ipLength = ip.length();
576 int compressBegin = 0;
577 int compressLength = 0;
578 int currentIndex = 0;
579 int value = 0;
580 int begin = -1;
581 int i = 0;
582 int ipv6Separators = 0;
583 int ipv4Separators = 0;
584 int tmp;
585 boolean needsShift = false;
586 for (; i < ipLength; ++i) {
587 final char c = ip.charAt(i);
588 switch (c) {
589 case ':':
590 ++ipv6Separators;
591 if (i - begin > IPV6_MAX_CHAR_BETWEEN_SEPARATOR ||
592 ipv4Separators > 0 || ipv6Separators > IPV6_MAX_SEPARATORS ||
593 currentIndex + 1 >= bytes.length) {
594 return null;
595 }
596 value <<= (IPV6_MAX_CHAR_BETWEEN_SEPARATOR - (i - begin)) << 2;
597
598 if (compressLength > 0) {
599 compressLength -= 2;
600 }
601
602
603
604
605 bytes[currentIndex++] = (byte) (((value & 0xf) << 4) | ((value >> 4) & 0xf));
606 bytes[currentIndex++] = (byte) ((((value >> 8) & 0xf) << 4) | ((value >> 12) & 0xf));
607 tmp = i + 1;
608 if (tmp < ipLength && ip.charAt(tmp) == ':') {
609 ++tmp;
610 if (compressBegin != 0 || (tmp < ipLength && ip.charAt(tmp) == ':')) {
611 return null;
612 }
613 ++ipv6Separators;
614 needsShift = ipv6Separators == 2 && value == 0;
615 compressBegin = currentIndex;
616 compressLength = bytes.length - compressBegin - 2;
617 ++i;
618 }
619 value = 0;
620 begin = -1;
621 break;
622 case '.':
623 ++ipv4Separators;
624 tmp = i - begin;
625 if (tmp > IPV4_MAX_CHAR_BETWEEN_SEPARATOR
626 || begin < 0
627 || ipv4Separators > IPV4_SEPARATORS
628 || (ipv6Separators > 0 && (currentIndex + compressLength < 12))
629 || i + 1 >= ipLength
630 || currentIndex >= bytes.length
631 || ipv4Separators == 1 &&
632
633 ((!ipv4Mapped || currentIndex != 0 && !isValidIPv4Mapped(bytes, currentIndex,
634 compressBegin, compressLength)) ||
635 (tmp == 3 && (!isValidNumericChar(ip.charAt(i - 1)) ||
636 !isValidNumericChar(ip.charAt(i - 2)) ||
637 !isValidNumericChar(ip.charAt(i - 3))) ||
638 tmp == 2 && (!isValidNumericChar(ip.charAt(i - 1)) ||
639 !isValidNumericChar(ip.charAt(i - 2))) ||
640 tmp == 1 && !isValidNumericChar(ip.charAt(i - 1))))) {
641 return null;
642 }
643 value <<= (IPV4_MAX_CHAR_BETWEEN_SEPARATOR - tmp) << 2;
644
645
646
647
648 begin = (value & 0xf) * 100 + ((value >> 4) & 0xf) * 10 + ((value >> 8) & 0xf);
649 if (begin < 0 || begin > 255) {
650 return null;
651 }
652 bytes[currentIndex++] = (byte) begin;
653 value = 0;
654 begin = -1;
655 break;
656 default:
657 if (!isValidHexChar(c) || (ipv4Separators > 0 && !isValidNumericChar(c))) {
658 return null;
659 }
660 if (begin < 0) {
661 begin = i;
662 } else if (i - begin > IPV6_MAX_CHAR_BETWEEN_SEPARATOR) {
663 return null;
664 }
665
666
667
668
669 value += StringUtil.decodeHexNibble(c) << ((i - begin) << 2);
670 break;
671 }
672 }
673
674 final boolean isCompressed = compressBegin > 0;
675
676 if (ipv4Separators > 0) {
677 if (begin > 0 && i - begin > IPV4_MAX_CHAR_BETWEEN_SEPARATOR ||
678 ipv4Separators != IPV4_SEPARATORS ||
679 currentIndex >= bytes.length) {
680 return null;
681 }
682 if (ipv6Separators == 0) {
683 compressLength = 12;
684 } else if (ipv6Separators >= IPV6_MIN_SEPARATORS &&
685 (!isCompressed && (ipv6Separators == 6 && ip.charAt(0) != ':') ||
686 isCompressed && (ipv6Separators < IPV6_MAX_SEPARATORS &&
687 (ip.charAt(0) != ':' || compressBegin <= 2)))) {
688 compressLength -= 2;
689 } else {
690 return null;
691 }
692 value <<= (IPV4_MAX_CHAR_BETWEEN_SEPARATOR - (i - begin)) << 2;
693
694
695
696
697 begin = (value & 0xf) * 100 + ((value >> 4) & 0xf) * 10 + ((value >> 8) & 0xf);
698 if (begin < 0 || begin > 255) {
699 return null;
700 }
701 bytes[currentIndex++] = (byte) begin;
702 } else {
703 tmp = ipLength - 1;
704 if (begin > 0 && i - begin > IPV6_MAX_CHAR_BETWEEN_SEPARATOR ||
705 ipv6Separators < IPV6_MIN_SEPARATORS ||
706 !isCompressed && (ipv6Separators + 1 != IPV6_MAX_SEPARATORS ||
707 ip.charAt(0) == ':' || ip.charAt(tmp) == ':') ||
708 isCompressed && (ipv6Separators > IPV6_MAX_SEPARATORS ||
709 (ipv6Separators == IPV6_MAX_SEPARATORS &&
710 (compressBegin <= 2 && ip.charAt(0) != ':' ||
711 compressBegin >= 14 && ip.charAt(tmp) != ':'))) ||
712 currentIndex + 1 >= bytes.length ||
713 begin < 0 && ip.charAt(tmp - 1) != ':' ||
714 compressBegin > 2 && ip.charAt(0) == ':') {
715 return null;
716 }
717 if (begin >= 0 && i - begin <= IPV6_MAX_CHAR_BETWEEN_SEPARATOR) {
718 value <<= (IPV6_MAX_CHAR_BETWEEN_SEPARATOR - (i - begin)) << 2;
719 }
720
721
722
723 bytes[currentIndex++] = (byte) (((value & 0xf) << 4) | ((value >> 4) & 0xf));
724 bytes[currentIndex++] = (byte) ((((value >> 8) & 0xf) << 4) | ((value >> 12) & 0xf));
725 }
726
727 i = currentIndex + compressLength;
728 if (needsShift || i >= bytes.length) {
729
730 if (i >= bytes.length) {
731 ++compressBegin;
732 }
733 for (i = currentIndex; i < bytes.length; ++i) {
734 for (begin = bytes.length - 1; begin >= compressBegin; --begin) {
735 bytes[begin] = bytes[begin - 1];
736 }
737 bytes[begin] = 0;
738 ++compressBegin;
739 }
740 } else {
741
742 for (i = 0; i < compressLength; ++i) {
743 begin = i + compressBegin;
744 currentIndex = begin + compressLength;
745 if (currentIndex < bytes.length) {
746 bytes[currentIndex] = bytes[begin];
747 bytes[begin] = 0;
748 } else {
749 break;
750 }
751 }
752 }
753
754 if (ipv4Separators > 0) {
755
756
757
758 bytes[10] = bytes[11] = (byte) 0xff;
759 }
760
761 return bytes;
762 }
763
764
765
766
767
768
769
770
771 public static String toSocketAddressString(InetSocketAddress addr) {
772 String port = String.valueOf(addr.getPort());
773 final StringBuilder sb;
774
775 if (addr.isUnresolved()) {
776 String hostString = PlatformDependent.javaVersion() >= 7 ? addr.getHostString() : addr.getHostName();
777 sb = newSocketAddressStringBuilder(hostString, port, !isValidIpV6Address(hostString));
778 } else {
779 InetAddress address = addr.getAddress();
780 String hostString = toAddressString(address);
781 sb = newSocketAddressStringBuilder(hostString, port, address instanceof Inet4Address);
782 }
783 return sb.append(':').append(port).toString();
784 }
785
786
787
788
789 public static String toSocketAddressString(String host, int port) {
790 String portStr = String.valueOf(port);
791 return newSocketAddressStringBuilder(
792 host, portStr, !isValidIpV6Address(host)).append(':').append(portStr).toString();
793 }
794
795 private static StringBuilder newSocketAddressStringBuilder(String host, String port, boolean ipv4) {
796 int hostLen = host.length();
797 if (ipv4) {
798
799 return new StringBuilder(hostLen + 1 + port.length()).append(host);
800 }
801
802 StringBuilder stringBuilder = new StringBuilder(hostLen + 3 + port.length());
803 if (hostLen > 1 && host.charAt(0) == '[' && host.charAt(hostLen - 1) == ']') {
804 return stringBuilder.append(host);
805 }
806 return stringBuilder.append('[').append(host).append(']');
807 }
808
809
810
811
812
813
814
815
816
817
818
819
820
821 public static String toAddressString(InetAddress ip) {
822 return toAddressString(ip, false);
823 }
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849 public static String toAddressString(InetAddress ip, boolean ipv4Mapped) {
850 if (ip instanceof Inet4Address) {
851 return ip.getHostAddress();
852 }
853 if (!(ip instanceof Inet6Address)) {
854 throw new IllegalArgumentException("Unhandled type: " + ip.getClass());
855 }
856
857 final byte[] bytes = ip.getAddress();
858 final int[] words = new int[IPV6_WORD_COUNT];
859 int i;
860 for (i = 0; i < words.length; ++i) {
861 words[i] = ((bytes[i << 1] & 0xff) << 8) | (bytes[(i << 1) + 1] & 0xff);
862 }
863
864
865 int currentStart = -1;
866 int currentLength;
867 int shortestStart = -1;
868 int shortestLength = 0;
869 for (i = 0; i < words.length; ++i) {
870 if (words[i] == 0) {
871 if (currentStart < 0) {
872 currentStart = i;
873 }
874 } else if (currentStart >= 0) {
875 currentLength = i - currentStart;
876 if (currentLength > shortestLength) {
877 shortestStart = currentStart;
878 shortestLength = currentLength;
879 }
880 currentStart = -1;
881 }
882 }
883
884 if (currentStart >= 0) {
885 currentLength = i - currentStart;
886 if (currentLength > shortestLength) {
887 shortestStart = currentStart;
888 shortestLength = currentLength;
889 }
890 }
891
892 if (shortestLength == 1) {
893 shortestLength = 0;
894 shortestStart = -1;
895 }
896
897
898 final int shortestEnd = shortestStart + shortestLength;
899 final StringBuilder b = new StringBuilder(IPV6_MAX_CHAR_COUNT);
900 if (shortestEnd < 0) {
901 b.append(Integer.toHexString(words[0]));
902 for (i = 1; i < words.length; ++i) {
903 b.append(':');
904 b.append(Integer.toHexString(words[i]));
905 }
906 } else {
907
908 final boolean isIpv4Mapped;
909 if (inRangeEndExclusive(0, shortestStart, shortestEnd)) {
910 b.append("::");
911 isIpv4Mapped = ipv4Mapped && (shortestEnd == 5 && words[5] == 0xffff);
912 } else {
913 b.append(Integer.toHexString(words[0]));
914 isIpv4Mapped = false;
915 }
916 for (i = 1; i < words.length; ++i) {
917 if (!inRangeEndExclusive(i, shortestStart, shortestEnd)) {
918 if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) {
919
920 if (!isIpv4Mapped || i == 6) {
921 b.append(':');
922 } else {
923 b.append('.');
924 }
925 }
926 if (isIpv4Mapped && i > 5) {
927 b.append(words[i] >> 8);
928 b.append('.');
929 b.append(words[i] & 0xff);
930 } else {
931 b.append(Integer.toHexString(words[i]));
932 }
933 } else if (!inRangeEndExclusive(i - 1, shortestStart, shortestEnd)) {
934
935 b.append("::");
936 }
937 }
938 }
939
940 return b.toString();
941 }
942
943
944
945
946
947
948
949
950
951
952
953
954 private static boolean inRangeEndExclusive(int value, int start, int end) {
955 return value >= start && value < end;
956 }
957
958
959
960
961 private NetUtil() {
962
963 }
964 }