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