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.net.UnknownHostException;
23  
24  import org.apache.james.jspf.core.exceptions.PermErrorException;
25  import org.xbill.DNS.Address;
26  
27  public class IPAddr {
28  
29      // Default IP4
30  
31      private static final int MASK8 = 255;
32  
33      private static final int MASK16 = 65535;
34  
35      private int[] address = new int[4];
36  
37      private int[] mask = new int[4];
38  
39      private int maskLength = 32;
40  
41      private int ipLength = 4;
42  
43      private int ipRun = 4;
44  
45      private String ipJoiner = ".";
46      
47      private static String ipv4MappedRegex = "::FFFF:[1-9][0-9]{0,2}\\.[1-9][0-9]{0,2}\\.[1-9][0-9]{0,2}\\.[1-9][0-9]{0,2}";
48  
49      // Allow factory creates only
50      private IPAddr() {
51  
52      }
53  
54      /**
55       * Get ipAddress for the given String and netmask
56       * 
57       * @param netAddress
58       *            The ipAddress given as String
59       * @param maskLength
60       *            The netmask
61       * @return IpAddress AAn Arraylist which contains all ipAddresses
62       * @throws PermErrorException
63       *             on error
64       */
65      public static IPAddr getAddress(String netAddress, int maskLength)
66              throws PermErrorException {
67          IPAddr returnAddress = new IPAddr();
68          returnAddress.stringToInternal(netAddress);
69          returnAddress.setMask(maskLength);
70          return returnAddress;
71      }
72  
73      /**
74       * 
75       * @see #getAddress(String, int)
76       */
77      public static IPAddr getAddress(String netAddress)
78              throws PermErrorException {
79          IPAddr returnAddress = new IPAddr();
80          returnAddress.stringToInternal(netAddress);
81          returnAddress.setMask(returnAddress.maskLength);
82          return returnAddress;
83      }
84  
85      /**
86       * Check if a the Object is instance of this class
87       * 
88       * @param data
89       *            The object to check
90       * @return true or false
91       */
92      public static boolean isIPAddr(String data) {
93          try {
94              getAddress(data);
95              return true;
96          } catch (PermErrorException e) {
97              return false;
98          }
99      }
100 
101     /**
102      * Set default values for ipv6
103      * 
104      */
105     private void setIP6Defaults() {
106         ipLength = 16;
107         ipJoiner = ":";
108         address = new int[8];
109         mask = new int[8];
110         ipRun = 8;
111     }
112 
113     /**
114      * create series of 16 bit masks for each ip block
115      * 
116      * @param maskLength
117      *            The netmask
118      */
119     public void setMask(int maskLength) {
120         int startMask;
121         int shift;
122         int maskSize;
123 
124         this.maskLength = maskLength;
125         if (ipLength == 4) {
126             if (!((maskLength > -1) && (maskLength < 33))) {
127                 maskLength = 32;
128             }
129             maskSize = 8;
130             startMask = (maskLength - 1) / maskSize;
131         } else {
132             if (!((maskLength > -1) && (maskLength < 129))) {
133                 maskLength = 128;
134             }
135             maskSize = 16;
136             startMask = (maskLength - 1) / maskSize;
137         }
138 
139         for (int i = 0; i < ipRun; i++) {
140             // full mask
141             if (i < startMask) {
142                 mask[i] = MASK16;
143                 // variable mask
144             } else if (i == startMask) {
145                 shift = ((i + 1) * maskSize) - maskLength;
146                 mask[i] = (MASK16 << shift) & MASK16;
147                 // no mask
148             } else {
149                 mask[i] = 0;
150             }
151         }
152     }
153 
154     /**
155      * Strip the last char of a string when it ends with a dot
156      * 
157      * @param data
158      *            The String where the dot should removed
159      * @return modified The Given String with last char stripped
160      */
161     public static String stripDot(String data) {
162 
163         data = data.trim();
164 
165         if (data.endsWith(".")) {
166             return data.substring(0, data.length() - 1);
167         } else {
168             return data;
169         }
170 
171     }
172 
173     /**
174      * Convert ipAddress to a byte Array which represent the ipAddress
175      * 
176      * @param netAddress
177      *            The ipAddress we should convert
178      * @throws PermErrorException
179      *             on error
180      */
181     private void stringToInternal(String netAddress) throws PermErrorException {
182         netAddress = stripDot(netAddress);
183 
184         try {
185             byte[] bytes = Inet6Util.createByteArrayFromIPAddressString(netAddress);
186     
187             if (bytes.length == 4) {
188                 for (int i = 0; i < bytes.length; i++) {
189                     address[i] = bytes[i];
190                 }
191             } else if (bytes.length == 16) {
192                 setIP6Defaults();
193                 for (int i = 0; i < bytes.length / 2; i++) {
194                     address[i] = unsigned(bytes[i * 2]) * 256
195                             + unsigned(bytes[i * 2 + 1]);
196                 }
197             } else {
198                 throw new PermErrorException("Not a valid address: " + netAddress);
199             }
200         } catch (NumberFormatException e) {
201             throw new PermErrorException("Not a valid address: " + netAddress);
202         }
203     }
204 
205     /**
206      * Return the Hexdecimal representation of the given long value
207      * 
208      * @param data The value to retrieve the Hexdecimal for
209      * @return The Hexdecimal representation of the given value
210      */
211     private String getHex(long data) {
212         StringBuffer fullHex = new StringBuffer();
213         fullHex.append("0000" + Long.toHexString(data).toUpperCase());
214         fullHex = fullHex.delete(0, fullHex.length() - 4);
215         return fullHex.toString();
216     }
217 
218     /**
219      * @see #getInAddress(String)
220      */
221     public String getIPAddress() {
222         return getIPAddress(address);
223     }
224 
225     /**
226      * Get ip Address from given int Array
227      * 
228      * @param addressData
229      *            The int Array
230      * @return ipAddress The ipAddress
231      */
232     private String getIPAddress(int[] addressData) {
233         StringBuffer createAddress = new StringBuffer();
234         int[] workingAddress;
235 
236         // convert internal address to 8 bit
237         if (ipLength == 4) {
238             workingAddress = get8BitAddress(addressData);
239             // create IP string
240             createAddress.append(workingAddress[0]);
241             for (int i = 1; i < ipRun; i++) {
242                 createAddress.append(ipJoiner + workingAddress[i]);
243             }
244             // leave internal address as 16 bit
245         } else {
246             workingAddress = addressData;
247             // create IP string
248             createAddress.append(getHex(workingAddress[0]));
249             for (int i = 1; i < ipRun; i++) {
250                 createAddress.append(ipJoiner + getHex(workingAddress[i]));
251             }
252         }
253 
254         return createAddress.toString();
255     }
256 
257     /**
258      * 
259      * @see #getIPAddress(int[])
260      */
261     public String getMaskedIPAddress() {
262         return getIPAddress(maskedAddress(address, mask));
263     }
264 
265     /**
266      * Return the NibbleFormat of the IPAddr
267      * 
268      * @return ipAddress The ipAddress in nibbleFormat 
269      */
270     public String getNibbleFormat() {
271         return getNibbleFormat(address);
272     }
273     
274     private String getNibbleFormat(int[] address) {
275         StringBuffer sb = new StringBuffer();
276         int[] ip = address;
277         for (int i = 0; i < ip.length; i++) {
278             String hex = getHex(ip[i]);
279             for (int j = 0; j < hex.length(); j++) {
280                 sb.append(hex.charAt(j));
281                 if (i != ip.length -1 || j != hex.length() -1) {
282                     sb.append(".");
283                 }
284             }
285         }
286         return sb.toString();
287     }
288 
289     /**
290      * Get reverse ipAddress
291      * 
292      * @return reverse ipAddress
293      */
294     public String getReverseIP() {
295         if(isIPV6(getIPAddress())) {
296             StringBuffer ip6 = new StringBuffer(getNibbleFormat());
297             return ip6.reverse().append(".ip6.arpa").toString();     
298         }
299         return (getIPAddress(reverseIP(address)) + ".in-addr.arpa");
300     }
301 
302     /**
303      * Converts 16 bit representation to 8 bit for IP4
304      * 
305      * @param addressData
306      *            The given int Array
307      * @return converted String
308      */
309     private int[] get8BitAddress(int[] addressData) {
310         int[] convertAddress = new int[4];
311         for (int i = 0; i < ipRun; i++) {
312             convertAddress[i] = addressData[i] & MASK8;
313         }
314         return convertAddress;
315     }
316 
317     /**
318      * Create a masked address given an address and mask
319      * 
320      * @param addressData
321      *            The int Array represent the ipAddress
322      * @param maskData
323      *            The int array represent the mask
324      * @return maskedAddress
325      */
326     private int[] maskedAddress(int[] addressData, int[] maskData) {
327         int[] maskedAddress = new int[ipLength];
328 
329         for (int i = 0; i < ipRun; i++) {
330             maskedAddress[i] = addressData[i] & maskData[i];
331         }
332         return maskedAddress;
333     }
334 
335     /**
336      * Reverses internal address
337      * 
338      * @param addressData
339      *            The int array represent the ipAddress
340      * @return reverseIP
341      */
342     private int[] reverseIP(int[] addressData) {
343         int[] reverseIP = new int[ipLength];
344         int temp;
345         for (int i = 0; i < ipRun; i++) {
346             temp = addressData[i];
347             reverseIP[i] = addressData[(ipRun - 1) - i];
348             reverseIP[(ipRun - 1) - i] = temp;
349         }
350         return reverseIP;
351     }
352 
353     /**
354      * Get mask length
355      * 
356      * @return maskLength
357      */
358     public int getMaskLength() {
359         return maskLength;
360     }
361 
362     
363     public String toString() {
364         return getIPAddress();
365     }
366 
367     private int unsigned(byte data) {
368         return data >= 0 ? data : 256 + data;
369     }
370 
371     /**
372      * This method return the InAddress for the given ip.
373      * 
374      * @param ipAddress -
375      *            ipAddress that should be processed
376      * @return the inAddress (in-addr or ip6)
377      * @throws PermErrorException
378      *             if the ipAddress is not valid (rfc conform)
379      */
380     public static String getInAddress(String ipAddress)
381             throws PermErrorException {
382         if (ipAddress == null) {
383             throw new PermErrorException(
384                     "IP is not a valid ipv4 or ipv6 address");
385         } else if (Inet6Util.isValidIPV4Address(ipAddress)) {
386             return "in-addr";
387         } else if (Inet6Util.isValidIP6Address(ipAddress)) {
388             return "ip6";
389         } else {
390             throw new PermErrorException(
391                     "IP is not a valid ipv4 or ipv6 address");
392         }
393     }
394 
395     /**
396      * Check if the given IP is valid. Works with ipv4 and ip6
397      * 
398      * @param ip
399      *            The ipaddress to check
400      * @return true or false
401      */
402     public static boolean isValidIP(String ip) {
403         return ip != null
404                 && (Inet6Util.isValidIPV4Address(ip) || Inet6Util
405                         .isValidIP6Address(ip));
406     }
407     
408     /**
409      * Return if the given ipAddress is ipv6
410      * 
411      * @param ip The ipAddress
412      * @return true or false
413      */
414     public static boolean isIPV6(String ip) {
415         return Inet6Util.isValidIP6Address(ip);
416     }
417 
418     /**
419      * This method try to covnert an ip address to an easy readable ip. See
420      * http://java.sun.com/j2se/1.4.2/docs/api/java/net/Inet6Address.html for
421      * the format it returns. For ipv4 it make no convertion
422      * 
423      * @param ip
424      *            The ip which should be tried to convert
425      * @return ip The converted ip
426      */
427     public static String getReadableIP(String ip) {
428 
429         // Convert the ip if its an ipv6 ip. For ipv4 no conversion is needed
430         if (Inet6Util.isValidIP6Address(ip)) {
431             try {
432                 return getConvertedIP(ip);
433             } catch (UnknownHostException e) {
434                 // ignore this
435             }
436         }
437         return ip;
438     }
439     
440     private static String getConvertedIP(String ip) throws UnknownHostException {
441         // Convert the ip if its an ipv6 ip. For ipv4 no conversion is needed
442         return Address.getByName(ip).getHostAddress();
443     }
444     
445     /**
446      * This method convert the given ip to the proper format. Convertion will only done if the given ipAddress is ipv6 and ipv4-mapped
447      * 
448      * This must be done to correct handle IPv4-mapped-addresses. 
449      * See: http://java.sun.com/j2se/1.4.2/docs/api/java/net/Inet6Address.html
450      *
451      * Special IPv6 address:
452      *  IPv4-mapped address:     
453      *      Of the form::ffff:w.x.y.z, this IPv6 address is used to represent an IPv4 address. It allows 
454      *      the native program to use the same address data structure and also the same socket when 
455      *      communicating with both IPv4 and IPv6 nodes. In InetAddress and Inet6Address, it is used 
456      *      for internal representation; it has no functional role. Java will never return an IPv4-mapped address. 
457      *      These classes can take an IPv4-mapped address as input, both in byte array and text representation. 
458      *       However, it will be converted into an IPv4 address.
459      * @param ip the ipAddress to convert
460      * @return return converted ip
461      * @throws PermErrorException if the given ipAddress is invalid
462      */
463     public static String getProperIpAddress(String ip) throws PermErrorException {
464         if (isIPV6(ip) && isIPV4MappedIP(ip)) {
465             try {
466                 return getConvertedIP(ip);
467             } catch (UnknownHostException e) {
468                 throw new PermErrorException("Invalid ipAddress: " + ip);
469             }
470         }
471         return ip;
472         
473     }
474     
475     /**
476      * Return true if the given ipAddress is a ipv4-mapped-address
477      * @param ip
478      * @return
479      */
480     private static boolean isIPV4MappedIP(String ip) {
481         return ip.toUpperCase().matches(ipv4MappedRegex);
482     }
483 
484 }