1
2
3
4
5
6
7
8
9
10
11
12
13
14
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
30
31
32
33
34
35 public final class NetUtil {
36
37
38
39
40 private static final int IPV6_WORD_COUNT = 8;
41
42
43
44
45 private static final int IPV6_MAX_CHAR_COUNT = 39;
46
47
48
49
50 private static final int IPV6_BYTE_COUNT = 16;
51
52
53
54
55 private static final int IPV6_MAX_CHAR_BETWEEN_SEPARATOR = 4;
56
57
58
59
60 private static final int IPV6_MIN_SEPARATORS = 2;
61
62
63
64
65 private static final int IPV6_MAX_SEPARATORS = 8;
66
67
68
69
70 private static final int IPV4_BYTE_COUNT = 4;
71
72
73
74
75 private static final int IPV4_MAX_CHAR_BETWEEN_SEPARATOR = 3;
76
77
78
79
80 private static final int IPV4_SEPARATORS = 3;
81
82
83
84
85 private static final InternalLogger logger = InternalLoggerFactory.getInstance(NetUtil.class);
86
87
88
89
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;
123
124
125
126
127
128
129
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
156
157 int hexStringsLength = 8;
158
159
160
161 if (!decStrings.isEmpty()) {
162 hexStringsLength -= 2;
163 }
164
165
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
176 for (int i = 0; i < hexStrings.size(); i ++) {
177 convertToBytes(hexStrings.get(i), ipByteArray, i << 1);
178 }
179
180
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
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
201 if (hexWordLength > 3) {
202 charValue = getIntValue(hexWord.charAt(hexWordIndex ++));
203 ipByteArray[byteIndex] |= charValue << 4;
204 }
205
206
207 if (hexWordLength > 2) {
208 charValue = getIntValue(hexWord.charAt(hexWordIndex ++));
209 ipByteArray[byteIndex] |= charValue;
210 }
211
212
213 if (hexWordLength > 1) {
214 charValue = getIntValue(hexWord.charAt(hexWordIndex ++));
215 ipByteArray[byteIndex + 1] |= charValue << 4;
216 }
217
218
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;
275 int endOffset = ipAddress.length();
276
277 if (endOffset < 2) {
278 return false;
279 }
280
281
282 if (ipAddress.charAt(0) == '[') {
283 if (ipAddress.charAt(endOffset - 1) != ']') {
284 return false;
285 }
286
287 startOffset = 1;
288 endOffset --;
289 }
290
291
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
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
314
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
324
325
326 if (i == startOffset && (ipAddress.length() <= i || ipAddress.charAt(i + 1) != ':')) {
327 return false;
328 }
329
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
358 if (numberOfPeriods > 0) {
359
360 if (numberOfPeriods != 3 || !(isValidIp4Word(word.toString()) && numberOfColons < 7)) {
361 return false;
362 }
363 } else {
364
365
366 if (numberOfColons != 7 && !doubleColon) {
367 return false;
368 }
369
370
371
372
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
406
407
408
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
454
455
456
457
458
459 public static Inet6Address getByName(CharSequence ip) {
460 return getByName(ip, true);
461 }
462
463
464
465
466
467
468
469
470
471
472
473
474
475
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
507
508
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
545
546
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
565
566
567
568 value += getIntValue(c) << ((i - begin) << 2);
569 break;
570 }
571 }
572
573 final boolean isCompressed = compressBegin > 0;
574
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
595
596
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
619
620
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
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
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);
662 }
663 }
664
665
666
667
668
669
670
671
672
673
674
675
676
677 public static String toAddressString(InetAddress ip) {
678 return toAddressString(ip, false);
679 }
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
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
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
740 if (currentStart >= 0) {
741 currentLength = i - currentStart;
742 if (currentLength > shortestLength) {
743 shortestStart = currentStart;
744 shortestLength = currentLength;
745 }
746 }
747
748 if (shortestLength == 1) {
749 shortestLength = 0;
750 shortestStart = -1;
751 }
752
753
754 final int shortestEnd = shortestStart + shortestLength;
755 final StringBuilder b = new StringBuilder(IPV6_MAX_CHAR_COUNT);
756 if (shortestEnd < 0) {
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 {
763
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
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
791 b.append("::");
792 }
793 }
794 }
795
796 return b.toString();
797 }
798
799
800
801
802
803
804
805
806
807
808
809
810 private static boolean inRangeEndExclusive(int value, int start, int end) {
811 return value >= start && value < end;
812 }
813
814
815
816
817 private NetUtil() {
818
819 }
820 }