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 }