View Javadoc
1   /*
2    * Copyright 2012 The Netty Project
3    *
4    * The Netty Project licenses this file to you under the Apache License,
5    * version 2.0 (the "License"); you may not use this file except in compliance
6    * with the License. You may obtain a copy of the License at:
7    *
8    *   http://www.apache.org/licenses/LICENSE-2.0
9    *
10   * Unless required by applicable law or agreed to in writing, software
11   * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
12   * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
13   * License for the specific language governing permissions and limitations
14   * under the License.
15   */
16  package org.jboss.netty.handler.ipfilter;
17  
18  import java.net.Inet4Address;
19  import java.net.Inet6Address;
20  import java.net.InetAddress;
21  import java.net.UnknownHostException;
22  import java.util.StringTokenizer;
23  
24  /**
25   */
26  public abstract class CIDR implements Comparable<CIDR> {
27      /** The base address of the CIDR notation */
28      protected InetAddress baseAddress;
29  
30      /** The mask used in the CIDR notation */
31      protected int cidrMask;
32  
33      /**
34       * Create CIDR using the CIDR Notation
35       *
36       * @return the generated CIDR
37       */
38      public static CIDR newCIDR(InetAddress baseAddress, int cidrMask) throws UnknownHostException {
39          if (cidrMask < 0) {
40              throw new UnknownHostException("Invalid mask length used: " + cidrMask);
41          }
42          if (baseAddress instanceof Inet4Address) {
43              if (cidrMask > 32) {
44                  throw new UnknownHostException("Invalid mask length used: " + cidrMask);
45              }
46              return new CIDR4((Inet4Address) baseAddress, cidrMask);
47          }
48          // IPv6.
49          if (cidrMask > 128) {
50              throw new UnknownHostException("Invalid mask length used: " + cidrMask);
51          }
52          return new CIDR6((Inet6Address) baseAddress, cidrMask);
53      }
54  
55      /**
56       * Create CIDR using the normal Notation
57       *
58       * @return the generated CIDR
59       */
60      public static CIDR newCIDR(InetAddress baseAddress, String scidrMask) throws UnknownHostException {
61          int cidrMask = getNetMask(scidrMask);
62          if (cidrMask < 0) {
63              throw new UnknownHostException("Invalid mask length used: " + cidrMask);
64          }
65          if (baseAddress instanceof Inet4Address) {
66              if (cidrMask > 32) {
67                  throw new UnknownHostException("Invalid mask length used: " + cidrMask);
68              }
69              return new CIDR4((Inet4Address) baseAddress, cidrMask);
70          }
71          cidrMask += 96;
72          // IPv6.
73          if (cidrMask > 128) {
74              throw new UnknownHostException("Invalid mask length used: " + cidrMask);
75          }
76          return new CIDR6((Inet6Address) baseAddress, cidrMask);
77      }
78  
79      /**
80       * Create CIDR using the CIDR or normal Notation<BR>
81       * i.e.:
82       * CIDR  subnet = newCIDR ("10.10.10.0/24"); or
83       * CIDR  subnet = newCIDR ("1fff:0:0a88:85a3:0:0:ac1f:8001/24"); or
84       * CIDR  subnet = newCIDR ("10.10.10.0/255.255.255.0");
85       *
86       * @return the generated CIDR
87       */
88      public static CIDR newCIDR(String cidr) throws UnknownHostException {
89          int p = cidr.indexOf('/');
90          if (p < 0) {
91              throw new UnknownHostException("Invalid CIDR notation used: " + cidr);
92          }
93          String addrString = cidr.substring(0, p);
94          String maskString = cidr.substring(p + 1);
95          InetAddress addr = addressStringToInet(addrString);
96          int mask;
97          if (maskString.indexOf('.') < 0) {
98              mask = parseInt(maskString, -1);
99          } else {
100             mask = getNetMask(maskString);
101             if (addr instanceof Inet6Address) {
102                 mask += 96;
103             }
104         }
105         if (mask < 0) {
106             throw new UnknownHostException("Invalid mask length used: " + maskString);
107         }
108         return newCIDR(addr, mask);
109     }
110 
111     /** @return the baseAddress of the CIDR block. */
112     public InetAddress getBaseAddress() {
113         return baseAddress;
114     }
115 
116     /** @return the Mask length. */
117     public int getMask() {
118         return cidrMask;
119     }
120 
121     /** @return the textual CIDR notation. */
122     @Override
123     public String toString() {
124         return baseAddress.getHostAddress() + '/' + cidrMask;
125     }
126 
127     /** @return the end address of this block. */
128     public abstract InetAddress getEndAddress();
129 
130     /**
131      * Compares the given InetAddress against the CIDR and returns true if
132      * the ip is in the subnet-ip-range and false if not.
133      *
134      * @return returns true if the given IP address is inside the currently
135      *         set network.
136      */
137     public abstract boolean contains(InetAddress inetAddress);
138 
139     @Override
140     public boolean equals(Object o) {
141         if (!(o instanceof CIDR)) {
142             return false;
143         }
144         return compareTo((CIDR) o) == 0;
145     }
146 
147     @Override
148     public int hashCode() {
149         return baseAddress.hashCode();
150     }
151 
152     /**
153      * Convert an IPv4 or IPv6 textual representation into an
154      * InetAddress.
155      *
156      * @return the created InetAddress
157      */
158     private static InetAddress addressStringToInet(String addr) throws UnknownHostException {
159         return InetAddress.getByName(addr);
160     }
161 
162     /**
163      * Get the Subnet's Netmask in Decimal format.<BR>
164      * i.e.: getNetMask("255.255.255.0") returns the integer CIDR mask
165      *
166      * @param netMask a network mask
167      * @return the integer CIDR mask
168      */
169     private static int getNetMask(String netMask) {
170         StringTokenizer nm = new StringTokenizer(netMask, ".");
171         int i = 0;
172         int[] netmask = new int[4];
173         while (nm.hasMoreTokens()) {
174             netmask[i] = Integer.parseInt(nm.nextToken());
175             i++;
176         }
177         int mask1 = 0;
178         for (i = 0; i < 4; i++) {
179             mask1 += Integer.bitCount(netmask[i]);
180         }
181         return mask1;
182     }
183 
184     /**
185      * @param intstr a string containing an integer.
186      * @param def    the default if the string does not contain a valid
187      *               integer.
188      * @return the inetAddress from the integer
189      */
190     private static int parseInt(String intstr, int def) {
191         Integer res;
192         if (intstr == null) {
193             return def;
194         }
195         try {
196             res = Integer.decode(intstr);
197         } catch (Exception e) {
198             res = def;
199         }
200         return res.intValue();
201     }
202 
203     /**
204      * Compute a byte representation of IpV4 from a IpV6
205      *
206      * @return the byte representation
207      * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4
208      */
209     public static byte[] getIpV4FromIpV6(Inet6Address address) {
210         byte[] baddr = address.getAddress();
211         for (int i = 0; i < 9; i++) {
212             if (baddr[i] != 0) {
213                 throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context");
214             }
215         }
216         if (baddr[10] != 0 && baddr[10] != 0xFF || baddr[11] != 0 && baddr[11] != 0xFF) {
217             throw new IllegalArgumentException("This IPv6 address cannot be used in IPv4 context");
218         }
219         return new byte[]
220                 {baddr[12], baddr[13], baddr[14], baddr[15]};
221     }
222 
223     /**
224      * Compute a byte representation of IpV6 from a IpV4
225      *
226      * @return the byte representation
227      * @throws IllegalArgumentException if the IpV6 cannot be mapped to IpV4
228      */
229     public static byte[] getIpV6FromIpV4(Inet4Address address) {
230         byte[] baddr = address.getAddress();
231         return new byte[]
232                 {0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, baddr[0], baddr[1], baddr[2], baddr[3]};
233     }
234 }