001: /* Licensed to the Apache Software Foundation (ASF) under one or more
002: * contributor license agreements. See the NOTICE file distributed with
003: * this work for additional information regarding copyright ownership.
004: * The ASF licenses this file to You under the Apache License, Version 2.0
005: * (the "License"); you may not use this file except in compliance with
006: * the License. You may obtain a copy of the License at
007: *
008: * http://www.apache.org/licenses/LICENSE-2.0
009: *
010: * Unless required by applicable law or agreed to in writing, software
011: * distributed under the License is distributed on an "AS IS" BASIS,
012: * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
013: * See the License for the specific language governing permissions and
014: * limitations under the License.
015: */
016:
017: package org.apache.harmony.luni.util;
018:
019: import java.util.ArrayList;
020: import java.util.StringTokenizer;
021:
022: /**
023: * Utility functions for IPV6 operations.
024: */
025: public class Inet6Util {
026:
027: /**
028: * Creates an byte[] based on an ipAddressString. No error handling is
029: * performed here.
030: */
031: public static byte[] createByteArrayFromIPAddressString(
032: String ipAddressString) {
033:
034: if (isValidIPV4Address(ipAddressString)) {
035: StringTokenizer tokenizer = new StringTokenizer(
036: ipAddressString, ".");
037: String token = "";
038: int tempInt = 0;
039: byte[] byteAddress = new byte[4];
040: for (int i = 0; i < 4; i++) {
041: token = tokenizer.nextToken();
042: tempInt = Integer.parseInt(token);
043: byteAddress[i] = (byte) tempInt;
044: }
045:
046: return byteAddress;
047: }
048:
049: if (ipAddressString.charAt(0) == '[') {
050: ipAddressString = ipAddressString.substring(1,
051: ipAddressString.length() - 1);
052: }
053:
054: StringTokenizer tokenizer = new StringTokenizer(
055: ipAddressString, ":.", true);
056: ArrayList<String> hexStrings = new ArrayList<String>();
057: ArrayList<String> decStrings = new ArrayList<String>();
058: String token = "";
059: String prevToken = "";
060: // If a double colon exists, we need to insert 0s.
061: int doubleColonIndex = -1;
062:
063: /*
064: * Go through the tokens, including the separators ':' and '.' When we
065: * hit a : or . the previous token will be added to either the hex list
066: * or decimal list. In the case where we hit a :: we will save the index
067: * of the hexStrings so we can add zeros in to fill out the string
068: */
069: while (tokenizer.hasMoreTokens()) {
070: prevToken = token;
071: token = tokenizer.nextToken();
072:
073: if (token.equals(":")) {
074: if (prevToken.equals(":")) {
075: doubleColonIndex = hexStrings.size();
076: } else if (!prevToken.equals("")) {
077: hexStrings.add(prevToken);
078: }
079: } else if (token.equals(".")) {
080: decStrings.add(prevToken);
081: }
082: }
083:
084: if (prevToken.equals(":")) {
085: if (token.equals(":")) {
086: doubleColonIndex = hexStrings.size();
087: } else {
088: hexStrings.add(token);
089: }
090: } else if (prevToken.equals(".")) {
091: decStrings.add(token);
092: }
093:
094: // figure out how many hexStrings we should have
095: // also check if it is a IPv4 address
096: int hexStringsLength = 8;
097:
098: // If we have an IPv4 address tagged on at the end, subtract
099: // 4 bytes, or 2 hex words from the total
100: if (decStrings.size() > 0) {
101: hexStringsLength -= 2;
102: }
103:
104: // if we hit a double Colon add the appropriate hex strings
105: if (doubleColonIndex != -1) {
106: int numberToInsert = hexStringsLength - hexStrings.size();
107: for (int i = 0; i < numberToInsert; i++) {
108: hexStrings.add(doubleColonIndex, "0");
109: }
110: }
111:
112: byte ipByteArray[] = new byte[16];
113:
114: // Finally convert these strings to bytes...
115: for (int i = 0; i < hexStrings.size(); i++) {
116: convertToBytes(hexStrings.get(i), ipByteArray, i * 2);
117: }
118:
119: // Now if there are any decimal values, we know where they go...
120: for (int i = 0; i < decStrings.size(); i++) {
121: ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings
122: .get(i)) & 255);
123: }
124:
125: // now check to see if this guy is actually and IPv4 address
126: // an ipV4 address is ::FFFF:d.d.d.d
127: boolean ipV4 = true;
128: for (int i = 0; i < 10; i++) {
129: if (ipByteArray[i] != 0) {
130: ipV4 = false;
131: break;
132: }
133: }
134:
135: if (ipByteArray[10] != -1 || ipByteArray[11] != -1) {
136: ipV4 = false;
137: }
138:
139: if (ipV4) {
140: byte ipv4ByteArray[] = new byte[4];
141: for (int i = 0; i < 4; i++) {
142: ipv4ByteArray[i] = ipByteArray[i + 12];
143: }
144: return ipv4ByteArray;
145: }
146:
147: return ipByteArray;
148:
149: }
150:
151: static String hexCharacters = "0123456789ABCDEF";
152:
153: public static String createIPAddrStringFromByteArray(
154: byte ipByteArray[]) {
155: if (ipByteArray.length == 4) {
156: return addressToString(bytesToInt(ipByteArray, 0));
157: }
158:
159: if (ipByteArray.length == 16) {
160: if (isIPv4MappedAddress(ipByteArray)) {
161: byte ipv4ByteArray[] = new byte[4];
162: for (int i = 0; i < 4; i++) {
163: ipv4ByteArray[i] = ipByteArray[i + 12];
164: }
165: return addressToString(bytesToInt(ipv4ByteArray, 0));
166: }
167: StringBuilder buffer = new StringBuilder();
168: for (int i = 0; i < ipByteArray.length; i++) {
169: int j = (ipByteArray[i] & 0xf0) >>> 4;
170: buffer.append(hexCharacters.charAt(j));
171: j = ipByteArray[i] & 0x0f;
172: buffer.append(hexCharacters.charAt(j));
173: if (i % 2 != 0 && (i + 1) < ipByteArray.length) {
174: buffer.append(":");
175: }
176: }
177: return buffer.toString();
178: }
179: return null;
180: }
181:
182: /** Converts a 4 character hex word into a 2 byte word equivalent */
183: public static void convertToBytes(String hexWord,
184: byte ipByteArray[], int byteIndex) {
185:
186: int hexWordLength = hexWord.length();
187: int hexWordIndex = 0;
188: ipByteArray[byteIndex] = 0;
189: ipByteArray[byteIndex + 1] = 0;
190: int charValue;
191:
192: // high order 4 bits of first byte
193: if (hexWordLength > 3) {
194: charValue = getIntValue(hexWord.charAt(hexWordIndex++));
195: ipByteArray[byteIndex] = (byte) (ipByteArray[byteIndex] | (charValue << 4));
196: }
197:
198: // low order 4 bits of the first byte
199: if (hexWordLength > 2) {
200: charValue = getIntValue(hexWord.charAt(hexWordIndex++));
201: ipByteArray[byteIndex] = (byte) (ipByteArray[byteIndex] | charValue);
202: }
203:
204: // high order 4 bits of second byte
205: if (hexWordLength > 1) {
206: charValue = getIntValue(hexWord.charAt(hexWordIndex++));
207: ipByteArray[byteIndex + 1] = (byte) (ipByteArray[byteIndex + 1] | (charValue << 4));
208: }
209:
210: // low order 4 bits of the first byte
211: charValue = getIntValue(hexWord.charAt(hexWordIndex));
212: ipByteArray[byteIndex + 1] = (byte) (ipByteArray[byteIndex + 1] | charValue & 15);
213: }
214:
215: static int getIntValue(char c) {
216:
217: switch (c) {
218: case '0':
219: return 0;
220: case '1':
221: return 1;
222: case '2':
223: return 2;
224: case '3':
225: return 3;
226: case '4':
227: return 4;
228: case '5':
229: return 5;
230: case '6':
231: return 6;
232: case '7':
233: return 7;
234: case '8':
235: return 8;
236: case '9':
237: return 9;
238: }
239:
240: c = Character.toLowerCase(c);
241: switch (c) {
242: case 'a':
243: return 10;
244: case 'b':
245: return 11;
246: case 'c':
247: return 12;
248: case 'd':
249: return 13;
250: case 'e':
251: return 14;
252: case 'f':
253: return 15;
254: }
255: return 0;
256: }
257:
258: private static boolean isIPv4MappedAddress(byte ipAddress[]) {
259:
260: // Check if the address matches ::FFFF:d.d.d.d
261: // The first 10 bytes are 0. The next to are -1 (FF).
262: // The last 4 bytes are varied.
263: for (int i = 0; i < 10; i++) {
264: if (ipAddress[i] != 0) {
265: return false;
266: }
267: }
268:
269: if (ipAddress[10] != -1 || ipAddress[11] != -1) {
270: return false;
271: }
272:
273: return true;
274:
275: }
276:
277: /**
278: * Takes the byte array and creates an integer out of four bytes starting at
279: * start as the high-order byte. This method makes no checks on the validity
280: * of the parameters.
281: */
282: public static int bytesToInt(byte bytes[], int start) {
283: // First mask the byte with 255, as when a negative
284: // signed byte converts to an integer, it has bits
285: // on in the first 3 bytes, we are only concerned
286: // about the right-most 8 bits.
287: // Then shift the rightmost byte to align with its
288: // position in the integer.
289: int value = ((bytes[start + 3] & 255))
290: | ((bytes[start + 2] & 255) << 8)
291: | ((bytes[start + 1] & 255) << 16)
292: | ((bytes[start] & 255) << 24);
293: return value;
294: }
295:
296: public static String addressToString(int value) {
297: return ((value >> 24) & 0xff) + "." + ((value >> 16) & 0xff)
298: + "." + ((value >> 8) & 0xff) + "." + (value & 0xff);
299: }
300:
301: public static boolean isValidIP6Address(String ipAddress) {
302: int length = ipAddress.length();
303: boolean doubleColon = false;
304: int numberOfColons = 0;
305: int numberOfPeriods = 0;
306: int numberOfPercent = 0;
307: String word = "";
308: char c = 0;
309: char prevChar = 0;
310: int offset = 0; // offset for [] IP addresses
311:
312: if (length < 2) {
313: return false;
314: }
315:
316: for (int i = 0; i < length; i++) {
317: prevChar = c;
318: c = ipAddress.charAt(i);
319: switch (c) {
320:
321: // case for an open bracket [x:x:x:...x]
322: case '[':
323: if (i != 0) {
324: return false; // must be first character
325: }
326: if (ipAddress.charAt(length - 1) != ']') {
327: return false; // must have a close ]
328: }
329: offset = 1;
330: if (length < 4) {
331: return false;
332: }
333: break;
334:
335: // case for a closed bracket at end of IP [x:x:x:...x]
336: case ']':
337: if (i != length - 1) {
338: return false; // must be last character
339: }
340: if (ipAddress.charAt(0) != '[') {
341: return false; // must have a open [
342: }
343: break;
344:
345: // case for the last 32-bits represented as IPv4 x:x:x:x:x:x:d.d.d.d
346: case '.':
347: numberOfPeriods++;
348: if (numberOfPeriods > 3) {
349: return false;
350: }
351: if (!isValidIP4Word(word)) {
352: return false;
353: }
354: if (numberOfColons != 6 && !doubleColon) {
355: return false;
356: }
357: // a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an
358: // IPv4 ending, otherwise 7 :'s is bad
359: if (numberOfColons == 7
360: && ipAddress.charAt(0 + offset) != ':'
361: && ipAddress.charAt(1 + offset) != ':') {
362: return false;
363: }
364: word = "";
365: break;
366:
367: case ':':
368: numberOfColons++;
369: if (numberOfColons > 7) {
370: return false;
371: }
372: if (numberOfPeriods > 0) {
373: return false;
374: }
375: if (prevChar == ':') {
376: if (doubleColon) {
377: return false;
378: }
379: doubleColon = true;
380: }
381: word = "";
382: break;
383: case '%':
384: if (numberOfColons == 0) {
385: return false;
386: }
387: numberOfPercent++;
388:
389: // validate that the stuff after the % is valid
390: if ((i + 1) >= length) {
391: // in this case the percent is there but no number is
392: // available
393: return false;
394: }
395: try {
396: Integer.parseInt(ipAddress.substring(i + 1));
397: } catch (NumberFormatException e) {
398: // right now we just support an integer after the % so if
399: // this is not
400: // what is there then return
401: return false;
402: }
403: break;
404:
405: default:
406: if (numberOfPercent == 0) {
407: if (word.length() > 3) {
408: return false;
409: }
410: if (!isValidHexChar(c)) {
411: return false;
412: }
413: }
414: word += c;
415: }
416: }
417:
418: // Check if we have an IPv4 ending
419: if (numberOfPeriods > 0) {
420: if (numberOfPeriods != 3 || !isValidIP4Word(word)) {
421: return false;
422: }
423: } else {
424: // If we're at then end and we haven't had 7 colons then there is a
425: // problem unless we encountered a doubleColon
426: if (numberOfColons != 7 && !doubleColon) {
427: return false;
428: }
429:
430: // If we have an empty word at the end, it means we ended in either
431: // a : or a .
432: // If we did not end in :: then this is invalid
433: if (numberOfPercent == 0) {
434: if (word == ""
435: && ipAddress.charAt(length - 1 - offset) == ':'
436: && ipAddress.charAt(length - 2 - offset) != ':') {
437: return false;
438: }
439: }
440: }
441:
442: return true;
443: }
444:
445: public static boolean isValidIP4Word(String word) {
446: char c;
447: if (word.length() < 1 || word.length() > 3) {
448: return false;
449: }
450: for (int i = 0; i < word.length(); i++) {
451: c = word.charAt(i);
452: if (!(c >= '0' && c <= '9')) {
453: return false;
454: }
455: }
456: if (Integer.parseInt(word) > 255) {
457: return false;
458: }
459: return true;
460: }
461:
462: static boolean isValidHexChar(char c) {
463:
464: return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F')
465: || (c >= 'a' && c <= 'f');
466: }
467:
468: /**
469: * Takes a string and parses it to see if it is a valid IPV4 address.
470: *
471: * @return true, if the string represents an IPV4 address in dotted
472: * notation, false otherwise
473: */
474: public static boolean isValidIPV4Address(String value) {
475: // general test
476: if (!value.matches("[\\p{Digit}\\.]*")) {
477: return false;
478: }
479:
480: String[] parts = value.split("\\.");
481: int length = parts.length;
482: if (length > 4) {
483: return false;
484: }
485:
486: // for one part
487: if (parts.length == 1) {
488: long longValue = Long.parseLong(parts[0]);
489: return longValue >= 0 && longValue <= 0xFFFFFFFFL;
490: }
491: // test every parts
492: for (int i = 0; i < parts.length; i++) {
493: if (parts[i].length() > 3
494: || Integer.parseInt(parts[i]) > 255) {
495: return false;
496: }
497: }
498: return true;
499: }
500:
501: }
|