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 org.jboss.netty.util.internal.StringUtil;
19
20 import java.net.InetAddress;
21 import java.net.UnknownHostException;
22 import java.util.StringTokenizer;
23
24 /**
25 * This class allows to check if an IP-V4-Address is contained in a subnet.<BR>
26 * Supported Formats for the Subnets are: 1.1.1.1/255.255.255.255 or 1.1.1.1/32 (CIDR-Notation)
27 * and (InetAddress,Mask) where Mask is a integer for CIDR-notation or a String for Standard Mask notation.<BR>
28 * <BR><BR>Example1:<BR>
29 * <tt>IpV4Subnet ips = new IpV4Subnet("192.168.1.0/24");</tt><BR>
30 * <tt>System.out.println("Result: "+ ips.contains("192.168.1.123"));</tt><BR>
31 * <tt>System.out.println("Result: "+ ips.contains(inetAddress2));</tt><BR>
32 * <BR>Example1 bis:<BR>
33 * <tt>IpV4Subnet ips = new IpV4Subnet(inetAddress, 24);</tt><BR>
34 * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123<BR>
35 * <BR><BR>Example2:<BR>
36 * <tt>IpV4Subnet ips = new IpV4Subnet("192.168.1.0/255.255.255.0");</tt><BR>
37 * <tt>System.out.println("Result: "+ ips.contains("192.168.1.123"));</tt><BR>
38 * <tt>System.out.println("Result: "+ ips.contains(inetAddress2));</tt><BR>
39 * <BR>Example2 bis:<BR>
40 * <tt>IpV4Subnet ips = new IpV4Subnet(inetAddress, "255.255.255.0");</tt><BR>
41 * where inetAddress is 192.168.1.0 and inetAddress2 is 192.168.1.123<BR>
42 */
43 public class IpV4Subnet implements IpSet, Comparable<IpV4Subnet> {
44
45 private static final int SUBNET_MASK = 0x80000000;
46
47 private InetAddress inetAddress;
48
49 private int subnet;
50
51 private int mask;
52
53 private int cidrMask;
54
55 /** Create IpV4Subnet for ALL (used for ALLOW or DENY ALL) */
56 public IpV4Subnet() {
57 // ALLOW or DENY ALL
58 mask = -1;
59 // other will be ignored
60 inetAddress = null;
61 subnet = 0;
62 cidrMask = 0;
63 }
64
65 /**
66 * Create IpV4Subnet using the CIDR or normal Notation<BR>
67 * i.e.:
68 * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/24"); or
69 * IpV4Subnet subnet = new IpV4Subnet("10.10.10.0/255.255.255.0");
70 *
71 * @param netAddress a network address as string.
72 */
73 public IpV4Subnet(String netAddress) throws UnknownHostException {
74 setNetAddress(netAddress);
75 }
76
77 /** Create IpV4Subnet using the CIDR Notation */
78 public IpV4Subnet(InetAddress inetAddress, int cidrNetMask) {
79 setNetAddress(inetAddress, cidrNetMask);
80 }
81
82 /** Create IpV4Subnet using the normal Notation */
83 public IpV4Subnet(InetAddress inetAddress, String netMask) {
84 setNetAddress(inetAddress, netMask);
85 }
86
87 /**
88 * Sets the Network Address in either CIDR or Decimal Notation.<BR>
89 * i.e.: setNetAddress("1.1.1.1/24"); or<BR>
90 * setNetAddress("1.1.1.1/255.255.255.0");<BR>
91 *
92 * @param netAddress a network address as string.
93 */
94 private void setNetAddress(String netAddress) throws UnknownHostException {
95 String[] tokens = StringUtil.split(netAddress, '/');
96 if (tokens.length != 2) {
97 throw new IllegalArgumentException("netAddress: " + netAddress + " (expected: CIDR or Decimal Notation)");
98 }
99
100 if (tokens[1].length() < 3) {
101 setNetId(tokens[0]);
102 setCidrNetMask(Integer.parseInt(tokens[1]));
103 } else {
104 setNetId(tokens[0]);
105 setNetMask(tokens[1]);
106 }
107 }
108
109 /** Sets the Network Address in CIDR Notation. */
110 private void setNetAddress(InetAddress inetAddress, int cidrNetMask) {
111 setNetId(inetAddress);
112 setCidrNetMask(cidrNetMask);
113 }
114
115 /** Sets the Network Address in Decimal Notation. */
116 private void setNetAddress(InetAddress inetAddress, String netMask) {
117 setNetId(inetAddress);
118 setNetMask(netMask);
119 }
120
121 /**
122 * Sets the BaseAdress of the Subnet.<BR>
123 * i.e.: setNetId("192.168.1.0");
124 *
125 * @param netId a network ID
126 */
127 private void setNetId(String netId) throws UnknownHostException {
128 InetAddress inetAddress1 = InetAddress.getByName(netId);
129 setNetId(inetAddress1);
130 }
131
132 /**
133 * Compute integer representation of InetAddress
134 *
135 * @return the integer representation
136 */
137 private static int toInt(InetAddress inetAddress1) {
138 byte[] octets = inetAddress1.getAddress();
139 assert octets.length == 4;
140
141 return (octets[0] & 0xff) << 24 |
142 (octets[1] & 0xff) << 16 |
143 (octets[2] & 0xff) << 8 |
144 octets[3] & 0xff;
145 }
146
147 /** Sets the BaseAdress of the Subnet. */
148 private void setNetId(InetAddress inetAddress) {
149 this.inetAddress = inetAddress;
150 subnet = toInt(inetAddress);
151 }
152
153 /**
154 * Sets the Subnet's Netmask in Decimal format.<BR>
155 * i.e.: setNetMask("255.255.255.0");
156 *
157 * @param netMask a network mask
158 */
159 private void setNetMask(String netMask) {
160 StringTokenizer nm = new StringTokenizer(netMask, ".");
161 int i = 0;
162 int[] netmask = new int[4];
163 while (nm.hasMoreTokens()) {
164 netmask[i] = Integer.parseInt(nm.nextToken());
165 i++;
166 }
167 int mask1 = 0;
168 for (i = 0; i < 4; i++) {
169 mask1 += Integer.bitCount(netmask[i]);
170 }
171 setCidrNetMask(mask1);
172 }
173
174 /**
175 * Sets the CIDR Netmask<BR>
176 * i.e.: setCidrNetMask(24);
177 *
178 * @param cidrNetMask a netmask in CIDR notation
179 */
180 private void setCidrNetMask(int cidrNetMask) {
181 cidrMask = cidrNetMask;
182 mask = SUBNET_MASK >> cidrMask - 1;
183 }
184
185 /**
186 * Compares the given IP-Address against the Subnet and returns true if
187 * the ip is in the subnet-ip-range and false if not.
188 *
189 * @param ipAddr an ipaddress
190 * @return returns true if the given IP address is inside the currently
191 * set network.
192 */
193 public boolean contains(String ipAddr) throws UnknownHostException {
194 InetAddress inetAddress1 = InetAddress.getByName(ipAddr);
195 return contains(inetAddress1);
196 }
197
198 /**
199 * Compares the given InetAddress against the Subnet and returns true if
200 * the ip is in the subnet-ip-range and false if not.
201 *
202 * @return returns true if the given IP address is inside the currently
203 * set network.
204 */
205 public boolean contains(InetAddress inetAddress1) {
206 return (toInt(inetAddress1) & mask) == subnet;
207 }
208
209 @Override
210 public String toString() {
211 return inetAddress.getHostAddress() + '/' + cidrMask;
212 }
213
214 @Override
215 public boolean equals(Object o) {
216 if (!(o instanceof IpV4Subnet)) {
217 return false;
218 }
219 IpV4Subnet ipV4Subnet = (IpV4Subnet) o;
220 return ipV4Subnet.subnet == subnet && ipV4Subnet.cidrMask == cidrMask;
221 }
222
223 @Override
224 public int hashCode() {
225 return subnet;
226 }
227
228 /** Compare two IpV4Subnet */
229 public int compareTo(IpV4Subnet o) {
230 if (o.subnet == subnet && o.cidrMask == cidrMask) {
231 return 0;
232 }
233 if (o.subnet < subnet) {
234 return 1;
235 }
236 if (o.subnet > subnet) {
237 return -1;
238 }
239 if (o.cidrMask < cidrMask) {
240 // greater Mask means less IpAddresses so -1
241 return -1;
242 }
243 return 1;
244 }
245 }