From 213c1e3d234135a5d33e333079f662af2f8f823b Mon Sep 17 00:00:00 2001 From: Norman Maurer Date: Sun, 18 Nov 2012 21:07:23 +0100 Subject: [PATCH] Replace sun.net.util.IPAddressUtil usage with own implementation --- .../handler/codec/socks/SocksCmdRequest.java | 10 +- .../src/main/java/io/netty/util/IPUtil.java | 453 ++++++++++++++++++ pom.xml | 1 - 3 files changed, 458 insertions(+), 6 deletions(-) create mode 100644 common/src/main/java/io/netty/util/IPUtil.java diff --git a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequest.java b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequest.java index 796a072176..1587a2fb0d 100644 --- a/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequest.java +++ b/codec-socks/src/main/java/io/netty/handler/codec/socks/SocksCmdRequest.java @@ -17,7 +17,7 @@ package io.netty.handler.codec.socks; import io.netty.buffer.ByteBuf; import io.netty.util.CharsetUtil; -import sun.net.util.IPAddressUtil; +import io.netty.util.IPUtil; import java.net.IDN; @@ -46,7 +46,7 @@ public final class SocksCmdRequest extends SocksRequest { } switch (addressType) { case IPv4: - if (!IPAddressUtil.isIPv4LiteralAddress(host)) { + if (!IPUtil.isValidIPV4Address(host)) { throw new IllegalArgumentException(host + " is not a valid IPv4 address"); } break; @@ -56,7 +56,7 @@ public final class SocksCmdRequest extends SocksRequest { } break; case IPv6: - if (!IPAddressUtil.isIPv6LiteralAddress(host)) { + if (!IPUtil.isValidIP6Address(host)) { throw new IllegalArgumentException(host + " is not a valid IPv6 address"); } break; @@ -116,7 +116,7 @@ public final class SocksCmdRequest extends SocksRequest { byteBuf.writeByte(addressType.getByteValue()); switch (addressType) { case IPv4: { - byteBuf.writeBytes(IPAddressUtil.textToNumericFormatV4(host)); + byteBuf.writeBytes(IPUtil.createByteArrayFromIPAddressString(host)); byteBuf.writeShort(port); break; } @@ -129,7 +129,7 @@ public final class SocksCmdRequest extends SocksRequest { } case IPv6: { - byteBuf.writeBytes(IPAddressUtil.textToNumericFormatV6(host)); + byteBuf.writeBytes(IPUtil.createByteArrayFromIPAddressString(host)); byteBuf.writeShort(port); break; } diff --git a/common/src/main/java/io/netty/util/IPUtil.java b/common/src/main/java/io/netty/util/IPUtil.java new file mode 100644 index 0000000000..e64bc2d4e1 --- /dev/null +++ b/common/src/main/java/io/netty/util/IPUtil.java @@ -0,0 +1,453 @@ +/* + * Copyright 2012 The Netty Project + * + * The Netty Project 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 io.netty.util; + + +import java.util.ArrayList; +import java.util.StringTokenizer; + +/** + * Utility functions for IPV6 operations. + * + * see Inet6Util from the Apache Harmony project + * + * see org.apache.harmony.util.Inet6Util + */ +public final class IPUtil { + + private IPUtil() { + // make this class a an utility class non-instantiable + } + + /** + * 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; + 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 hexStrings = new ArrayList(); + ArrayList decStrings = new ArrayList(); + String token = ""; + String prevToken = ""; + int doubleColonIndex = -1; // If a double colon exists, we need to + // insert 0s. + + // Go through the tokens, including the seperators ':' 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 (":".equals(token)) { + if (":".equals(prevToken)) { + doubleColonIndex = hexStrings.size(); + } else if (!prevToken.isEmpty()) { + hexStrings.add(prevToken); + } + } else if (".".equals(token)) { + decStrings.add(prevToken); + } + } + + if (":".equals(prevToken)) { + if (":".equals(token)) { + doubleColonIndex = hexStrings.size(); + } else { + hexStrings.add(token); + } + } else if (".".equals(prevToken)) { + 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.isEmpty()) { + 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]; + System.arraycopy(ipByteArray, 12, ipv4ByteArray, 0, 4); + return ipv4ByteArray; + } + + return ipByteArray; + + } + + /** Converts a 4 character hex word into a 2 byte word equivalent */ + private 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] |= charValue << 4; + } + + // low order 4 bits of the first byte + if (hexWordLength > 2) { + charValue = getIntValue(hexWord.charAt(hexWordIndex++)); + ipByteArray[byteIndex] |= charValue; + } + + // high order 4 bits of second byte + if (hexWordLength > 1) { + charValue = getIntValue(hexWord.charAt(hexWordIndex++)); + ipByteArray[byteIndex + 1] |= charValue << 4; + } + + // low order 4 bits of the first byte + charValue = getIntValue(hexWord.charAt(hexWordIndex)); + 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; + } + + public static boolean isValidIP6Address(String ipAddress) { + int length = ipAddress.length(); + boolean doubleColon = false; + int numberOfColons = 0; + int numberOfPeriods = 0; + int numberOfPercent = 0; + StringBuilder word = new StringBuilder(); + char c = 0; + char prevChar; + 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 charcter + } + 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.toString())) { + 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(offset) != ':' + && ipAddress.charAt(1 + offset) != ':') { + return false; + } + word.delete(0, word.length()); + break; + + case ':': + // FIX "IP6 mechanism syntax #ip6-bad1" + // An IPV6 address cannot start with a single ":". + // Either it can starti with "::" or with a number. + if (i == offset && (ipAddress.length() <= i || ipAddress.charAt(i + 1) != ':')) { + return false; + } + // END FIX "IP6 mechanism syntax #ip6-bad1" + numberOfColons++; + if (numberOfColons > 7) { + return false; + } + if (numberOfPeriods > 0) { + return false; + } + if (prevChar == ':') { + if (doubleColon) { + return false; + } + doubleColon = true; + } + word.delete(0, word.length()); + 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 != null && word.length() > 3) { + return false; + } + if (!isValidHexChar(c)) { + return false; + } + } + word.append(c); + } + } + + // Check if we have an IPv4 ending + if (numberOfPeriods > 0) { + if (numberOfPeriods != 3 || !isValidIP4Word(word.toString())) { + 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.length() == 0 && 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) { + + int periods = 0; + int i; + int length = value.length(); + + if (length > 15) { + return false; + } + char c; + StringBuilder word = new StringBuilder(); + for (i = 0; i < length; i++) { + c = value.charAt(i); + if (c == '.') { + periods++; + if (periods > 3) { + return false; + } + if (word.length() == 0) { + return false; + } + if (Integer.parseInt(word.toString()) > 255) { + return false; + } + word.delete(0, word.length()); + } else if (!Character.isDigit(c)) { + return false; + } else { + if (word.length() > 2) { + return false; + } + word.append(c); + } + } + + if (word.length() == 0 || Integer.parseInt(word.toString()) > 255) { + return false; + } + if (periods != 3) { + return false; + } + return true; + } + +} diff --git a/pom.xml b/pom.xml index 2baeca654a..68f3a6b793 100644 --- a/pom.xml +++ b/pom.xml @@ -280,7 +280,6 @@ sun.misc.Unsafe sun.misc.Cleaner - sun.net.util.IPAddressUtil java.util.zip.Deflater