| /* Licensed to the Apache Software Foundation (ASF) under one or more |
| * contributor license agreements. See the NOTICE file distributed with |
| * this work for additional information regarding copyright ownership. |
| * The ASF licenses this file to You under the Apache License, Version 2.0 |
| * (the "License"); you may not use this file except in compliance with |
| * the License. You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| package org.apache.harmony.luni.util; |
| |
| import java.util.ArrayList; |
| import java.util.StringTokenizer; |
| |
| /** |
| * Utility functions for IPV6 operations. |
| */ |
| public class Inet6Util { |
| |
| /** |
| * Creates an byte[] based on an ipAddressString. No error handling is |
| * performed here. |
| */ |
| public static byte[] createByteArrayFromIPAddressString( |
| String ipAddressString) { |
| |
| if (isValidIPV4Address(ipAddressString)) { |
| StringTokenizer tokenizer = new StringTokenizer(ipAddressString, "."); |
| String token = ""; |
| int tempInt = 0; |
| byte[] byteAddress = new byte[4]; |
| for (int i = 0; i < 4; i++) { |
| token = tokenizer.nextToken(); |
| tempInt = Integer.parseInt(token); |
| byteAddress[i] = (byte) tempInt; |
| } |
| |
| return byteAddress; |
| } |
| |
| if (ipAddressString.charAt(0) == '[') { |
| ipAddressString = ipAddressString.substring(1, ipAddressString.length() - 1); |
| } |
| |
| StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ":.", true); |
| ArrayList<String> hexStrings = new ArrayList<String>(); |
| ArrayList<String> decStrings = new ArrayList<String>(); |
| String token = ""; |
| String prevToken = ""; |
| // If a double colon exists, we need to insert 0s. |
| int doubleColonIndex = -1; |
| |
| /* |
| * Go through the tokens, including the separators ':' and '.' When we |
| * hit a : or . the previous token will be added to either the hex list |
| * or decimal list. In the case where we hit a :: we will save the index |
| * of the hexStrings so we can add zeros in to fill out the string |
| */ |
| while (tokenizer.hasMoreTokens()) { |
| prevToken = token; |
| token = tokenizer.nextToken(); |
| |
| if (token.equals(":")) { |
| if (prevToken.equals(":")) { |
| doubleColonIndex = hexStrings.size(); |
| } else if (!prevToken.equals("")) { |
| hexStrings.add(prevToken); |
| } |
| } else if (token.equals(".")) { |
| decStrings.add(prevToken); |
| } |
| } |
| |
| if (prevToken.equals(":")) { |
| if (token.equals(":")) { |
| doubleColonIndex = hexStrings.size(); |
| } else { |
| hexStrings.add(token); |
| } |
| } else if (prevToken.equals(".")) { |
| decStrings.add(token); |
| } |
| |
| // figure out how many hexStrings we should have |
| // also check if it is a IPv4 address |
| int hexStringsLength = 8; |
| |
| // If we have an IPv4 address tagged on at the end, subtract |
| // 4 bytes, or 2 hex words from the total |
| if (decStrings.size() > 0) { |
| hexStringsLength -= 2; |
| } |
| |
| // if we hit a double Colon add the appropriate hex strings |
| if (doubleColonIndex != -1) { |
| int numberToInsert = hexStringsLength - hexStrings.size(); |
| for (int i = 0; i < numberToInsert; i++) { |
| hexStrings.add(doubleColonIndex, "0"); |
| } |
| } |
| |
| byte ipByteArray[] = new byte[16]; |
| |
| // Finally convert these strings to bytes... |
| for (int i = 0; i < hexStrings.size(); i++) { |
| convertToBytes(hexStrings.get(i), ipByteArray, i * 2); |
| } |
| |
| // Now if there are any decimal values, we know where they go... |
| for (int i = 0; i < decStrings.size(); i++) { |
| ipByteArray[i + 12] = (byte) (Integer.parseInt(decStrings.get(i)) & 255); |
| } |
| |
| // now check to see if this guy is actually and IPv4 address |
| // an ipV4 address is ::FFFF:d.d.d.d |
| boolean ipV4 = true; |
| for (int i = 0; i < 10; i++) { |
| if (ipByteArray[i] != 0) { |
| ipV4 = false; |
| break; |
| } |
| } |
| |
| if (ipByteArray[10] != -1 || ipByteArray[11] != -1) { |
| ipV4 = false; |
| } |
| |
| if (ipV4) { |
| byte ipv4ByteArray[] = new byte[4]; |
| for (int i = 0; i < 4; i++) { |
| ipv4ByteArray[i] = ipByteArray[i + 12]; |
| } |
| return ipv4ByteArray; |
| } |
| |
| return ipByteArray; |
| |
| } |
| |
| // BEGIN android-changed |
| static char[] hexCharacters = {'0', '1', '2', '3', '4', '5', '6', '7', '8', |
| '9', 'a', 'b', 'c', 'd', 'e', 'f'}; |
| // END android-changed |
| |
| public static String createIPAddrStringFromByteArray(byte ipByteArray[]) { |
| if (ipByteArray.length == 4) { |
| return addressToString(bytesToInt(ipByteArray, 0)); |
| } |
| |
| if (ipByteArray.length == 16) { |
| if (isIPv4MappedAddress(ipByteArray)) { |
| byte ipv4ByteArray[] = new byte[4]; |
| for (int i = 0; i < 4; i++) { |
| ipv4ByteArray[i] = ipByteArray[i + 12]; |
| } |
| return addressToString(bytesToInt(ipv4ByteArray, 0)); |
| } |
| StringBuilder buffer = new StringBuilder(); |
| // BEGIN android-changed |
| for (int i = 0; i < 8; i++) { // ipByteArray.length / 2 |
| |
| int num = (ipByteArray[2 * i] & 0xff) << 8; |
| num ^= ipByteArray[2 * i + 1] & 0xff; |
| |
| // count the digits to display without leading 0 |
| int count = 1, j = num; |
| while ((j >>>= 4) != 0) { |
| count++; |
| } |
| |
| char[] buf = new char[count]; |
| do { |
| int t = num & 0x0f; |
| buf[--count] = hexCharacters[t]; |
| num >>>= 4; |
| } while (count > 0); |
| |
| buffer.append(buf); |
| if ((i + 1) < 8) { // ipByteArray.length / 2 |
| buffer.append(":"); |
| } |
| } |
| // END android-changed |
| return buffer.toString(); |
| } |
| return null; |
| } |
| |
| /** Converts a 4 character hex word into a 2 byte word equivalent */ |
| public static void convertToBytes(String hexWord, byte ipByteArray[], |
| int byteIndex) { |
| |
| int hexWordLength = hexWord.length(); |
| int hexWordIndex = 0; |
| ipByteArray[byteIndex] = 0; |
| ipByteArray[byteIndex + 1] = 0; |
| int charValue; |
| |
| // high order 4 bits of first byte |
| if (hexWordLength > 3) { |
| charValue = getIntValue(hexWord.charAt(hexWordIndex++)); |
| ipByteArray[byteIndex] = (byte) (ipByteArray[byteIndex] | (charValue << 4)); |
| } |
| |
| // low order 4 bits of the first byte |
| if (hexWordLength > 2) { |
| charValue = getIntValue(hexWord.charAt(hexWordIndex++)); |
| ipByteArray[byteIndex] = (byte) (ipByteArray[byteIndex] | charValue); |
| } |
| |
| // high order 4 bits of second byte |
| if (hexWordLength > 1) { |
| charValue = getIntValue(hexWord.charAt(hexWordIndex++)); |
| ipByteArray[byteIndex + 1] = (byte) (ipByteArray[byteIndex + 1] | (charValue << 4)); |
| } |
| |
| // low order 4 bits of the first byte |
| charValue = getIntValue(hexWord.charAt(hexWordIndex)); |
| ipByteArray[byteIndex + 1] = (byte) (ipByteArray[byteIndex + 1] | charValue & 15); |
| } |
| |
| static int getIntValue(char c) { |
| |
| switch (c) { |
| case '0': |
| return 0; |
| case '1': |
| return 1; |
| case '2': |
| return 2; |
| case '3': |
| return 3; |
| case '4': |
| return 4; |
| case '5': |
| return 5; |
| case '6': |
| return 6; |
| case '7': |
| return 7; |
| case '8': |
| return 8; |
| case '9': |
| return 9; |
| } |
| |
| c = Character.toLowerCase(c); |
| switch (c) { |
| case 'a': |
| return 10; |
| case 'b': |
| return 11; |
| case 'c': |
| return 12; |
| case 'd': |
| return 13; |
| case 'e': |
| return 14; |
| case 'f': |
| return 15; |
| } |
| return 0; |
| } |
| |
| private static boolean isIPv4MappedAddress(byte ipAddress[]) { |
| |
| // Check if the address matches ::FFFF:d.d.d.d |
| // The first 10 bytes are 0. The next to are -1 (FF). |
| // The last 4 bytes are varied. |
| for (int i = 0; i < 10; i++) { |
| if (ipAddress[i] != 0) { |
| return false; |
| } |
| } |
| |
| if (ipAddress[10] != -1 || ipAddress[11] != -1) { |
| return false; |
| } |
| |
| return true; |
| |
| } |
| |
| /** |
| * Takes the byte array and creates an integer out of four bytes starting at |
| * start as the high-order byte. This method makes no checks on the validity |
| * of the parameters. |
| */ |
| public static int bytesToInt(byte bytes[], int start) { |
| // First mask the byte with 255, as when a negative |
| // signed byte converts to an integer, it has bits |
| // on in the first 3 bytes, we are only concerned |
| // about the right-most 8 bits. |
| // Then shift the rightmost byte to align with its |
| // position in the integer. |
| int value = ((bytes[start + 3] & 255)) |
| | ((bytes[start + 2] & 255) << 8) |
| | ((bytes[start + 1] & 255) << 16) |
| | ((bytes[start] & 255) << 24); |
| return value; |
| } |
| |
| public static String addressToString(int value) { |
| return ((value >> 24) & 0xff) + "." + ((value >> 16) & 0xff) + "." |
| + ((value >> 8) & 0xff) + "." + (value & 0xff); |
| } |
| |
| public static boolean isIP6AddressInFullForm(String ipAddress) { |
| if (isValidIP6Address(ipAddress)) { |
| int doubleColonIndex = ipAddress.indexOf("::"); |
| if (doubleColonIndex >= 0) { |
| // Simplified form which contains :: |
| return false; |
| } |
| return true; |
| } |
| return false; |
| } |
| |
| public static boolean isValidIP6Address(String ipAddress) { |
| int length = ipAddress.length(); |
| boolean doubleColon = false; |
| int numberOfColons = 0; |
| int numberOfPeriods = 0; |
| int numberOfPercent = 0; |
| String word = ""; |
| char c = 0; |
| char prevChar = 0; |
| int offset = 0; // offset for [] IP addresses |
| |
| if (length < 2) { |
| return false; |
| } |
| |
| for (int i = 0; i < length; i++) { |
| prevChar = c; |
| c = ipAddress.charAt(i); |
| switch (c) { |
| |
| // case for an open bracket [x:x:x:...x] |
| case '[': |
| if (i != 0) { |
| return false; // must be first character |
| } |
| if (ipAddress.charAt(length - 1) != ']') { |
| return false; // must have a close ] |
| } |
| offset = 1; |
| if (length < 4) { |
| return false; |
| } |
| break; |
| |
| // case for a closed bracket at end of IP [x:x:x:...x] |
| case ']': |
| if (i != length - 1) { |
| return false; // must be last character |
| } |
| if (ipAddress.charAt(0) != '[') { |
| return false; // must have a open [ |
| } |
| break; |
| |
| // case for the last 32-bits represented as IPv4 x:x:x:x:x:x:d.d.d.d |
| case '.': |
| numberOfPeriods++; |
| if (numberOfPeriods > 3) { |
| return false; |
| } |
| if (!isValidIP4Word(word)) { |
| return false; |
| } |
| if (numberOfColons != 6 && !doubleColon) { |
| return false; |
| } |
| // a special case ::1:2:3:4:5:d.d.d.d allows 7 colons with an |
| // IPv4 ending, otherwise 7 :'s is bad |
| if (numberOfColons == 7 && ipAddress.charAt(0 + offset) != ':' |
| && ipAddress.charAt(1 + offset) != ':') { |
| return false; |
| } |
| word = ""; |
| break; |
| |
| case ':': |
| numberOfColons++; |
| if (numberOfColons > 7) { |
| return false; |
| } |
| if (numberOfPeriods > 0) { |
| return false; |
| } |
| if (prevChar == ':') { |
| if (doubleColon) { |
| return false; |
| } |
| doubleColon = true; |
| } |
| word = ""; |
| break; |
| case '%': |
| if (numberOfColons == 0) { |
| return false; |
| } |
| numberOfPercent++; |
| |
| // validate that the stuff after the % is valid |
| if ((i + 1) >= length) { |
| // in this case the percent is there but no number is |
| // available |
| return false; |
| } |
| try { |
| Integer.parseInt(ipAddress.substring(i + 1)); |
| } catch (NumberFormatException e) { |
| // right now we just support an integer after the % so if |
| // this is not |
| // what is there then return |
| return false; |
| } |
| break; |
| |
| default: |
| if (numberOfPercent == 0) { |
| if (word.length() > 3) { |
| return false; |
| } |
| if (!isValidHexChar(c)) { |
| return false; |
| } |
| } |
| word += c; |
| } |
| } |
| |
| // Check if we have an IPv4 ending |
| if (numberOfPeriods > 0) { |
| if (numberOfPeriods != 3 || !isValidIP4Word(word)) { |
| return false; |
| } |
| } else { |
| // If we're at then end and we haven't had 7 colons then there is a |
| // problem unless we encountered a doubleColon |
| if (numberOfColons != 7 && !doubleColon) { |
| return false; |
| } |
| |
| // If we have an empty word at the end, it means we ended in either |
| // a : or a . |
| // If we did not end in :: then this is invalid |
| if (numberOfPercent == 0) { |
| if (word == "" && ipAddress.charAt(length - 1 - offset) == ':' |
| && ipAddress.charAt(length - 2 - offset) != ':') { |
| return false; |
| } |
| } |
| } |
| |
| return true; |
| } |
| |
| public static boolean isValidIP4Word(String word) { |
| char c; |
| if (word.length() < 1 || word.length() > 3) { |
| return false; |
| } |
| for (int i = 0; i < word.length(); i++) { |
| c = word.charAt(i); |
| if (!(c >= '0' && c <= '9')) { |
| return false; |
| } |
| } |
| if (Integer.parseInt(word) > 255) { |
| return false; |
| } |
| return true; |
| } |
| |
| static boolean isValidHexChar(char c) { |
| |
| return (c >= '0' && c <= '9') || (c >= 'A' && c <= 'F') |
| || (c >= 'a' && c <= 'f'); |
| } |
| |
| /** |
| * Takes a string and parses it to see if it is a valid IPV4 address. |
| * |
| * @return true, if the string represents an IPV4 address in dotted |
| * notation, false otherwise |
| */ |
| public static boolean isValidIPV4Address(String value) { |
| // BEGIN android-changed |
| // general test |
| if (!value.matches("\\p{Digit}+(\\.\\p{Digit}+)*")) { |
| return false; |
| } |
| |
| String[] parts = value.split("\\."); |
| int length = parts.length; |
| if (length < 1 || length > 4) { |
| return false; |
| } |
| |
| if (length == 1) { |
| // One part decimal numeric address |
| long longValue = Long.parseLong(parts[0]); |
| return longValue <= 0xFFFFFFFFL; |
| } else { |
| // Test each part for inclusion in the correct range |
| for (int i = 0; i < length; i++) { |
| // For two part addresses, the second part expresses |
| // a 24-bit quantity; for three part addresses, the third |
| // part expresses a 16-bit quantity. |
| int max = 0xff; |
| if ((length == 2) && (i == 1)) { |
| max = 0xffffff; |
| } else if ((length == 3) && (i == 2)) { |
| max = 0xffff; |
| } |
| if (Integer.parseInt(parts[i]) > max) { |
| return false; |
| } |
| } |
| return true; |
| } |
| // END android-changed |
| } |
| |
| } |