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    }