View Javadoc

1   /****************************************************************
2    * Licensed to the Apache Software Foundation (ASF) under one   *
3    * or more contributor license agreements.  See the NOTICE file *
4    * distributed with this work for additional information        *
5    * regarding copyright ownership.  The ASF licenses this file   *
6    * to you under the Apache License, Version 2.0 (the            *
7    * "License"); you may not use this file except in compliance   *
8    * with the License.  You may obtain a copy of the License at   *
9    *                                                              *
10   *   http://www.apache.org/licenses/LICENSE-2.0                 *
11   *                                                              *
12   * Unless required by applicable law or agreed to in writing,   *
13   * software distributed under the License is distributed on an  *
14   * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY       *
15   * KIND, either express or implied.  See the License for the    *
16   * specific language governing permissions and limitations      *
17   * under the License.                                           *
18   ****************************************************************/
19  
20  package org.apache.james.jspf.core;
21  
22  import java.util.ArrayList;
23  import java.util.StringTokenizer;
24  
25  /**
26   * Utility functions for IPV6 operations.
27   * 
28   * see Inet6Util from the Apache Harmony project
29   * 
30   * see org.apache.harmony.util.Inet6Util
31   */
32  public class Inet6Util {
33      
34      private Inet6Util() {
35          // make this class a an utility class non-instantiable
36      }
37  
38      /**
39       * Creates an byte[] based on an ipAddressString. No error handling is
40       * performed here.
41       */
42      public static byte[] createByteArrayFromIPAddressString(
43              String ipAddressString) {
44  
45          if (isValidIPV4Address(ipAddressString)) {
46              StringTokenizer tokenizer = new StringTokenizer(ipAddressString,
47                      ".");
48              String token = "";
49              int tempInt = 0;
50              byte[] byteAddress = new byte[4];
51              for (int i = 0; i < 4; i++) {
52                  token = tokenizer.nextToken();
53                  tempInt = Integer.parseInt(token);
54                  byteAddress[i] = (byte) tempInt;
55              }
56  
57              return byteAddress;
58          }
59  
60          if (ipAddressString.charAt(0) == '[') {
61              ipAddressString = ipAddressString.substring(1, ipAddressString
62                      .length() - 1);
63          }
64  
65          StringTokenizer tokenizer = new StringTokenizer(ipAddressString, ":.",
66                  true);
67          ArrayList hexStrings = new ArrayList();
68          ArrayList decStrings = new ArrayList();
69          String token = "";
70          String prevToken = "";
71          int doubleColonIndex = -1; // If a double colon exists, we need to
72          // insert 0s.
73  
74          // Go through the tokens, including the seperators ':' and '.'
75          // When we hit a : or . the previous token will be added to either
76          // the hex list or decimal list. In the case where we hit a ::
77          // we will save the index of the hexStrings so we can add zeros
78          // in to fill out the string
79          while (tokenizer.hasMoreTokens()) {
80              prevToken = token;
81              token = tokenizer.nextToken();
82  
83              if (token.equals(":")) {
84                  if (prevToken.equals(":")) {
85                      doubleColonIndex = hexStrings.size();
86                  } else if (!prevToken.equals("")) {
87                      hexStrings.add(prevToken);
88                  }
89              } else if (token.equals(".")) {
90                  decStrings.add(prevToken);
91              }
92          }
93  
94          if (prevToken.equals(":")) {
95              if (token.equals(":")) {
96                  doubleColonIndex = hexStrings.size();
97              } else {
98                  hexStrings.add(token);
99              }
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 }