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
021package org.apache.mina.filter.firewall;
022
023import java.net.Inet4Address;
024import java.net.Inet6Address;
025import java.net.InetAddress;
026
027/**
028 * A IP subnet using the CIDR notation. Currently, only IP version 4
029 * address are supported.
030 *
031 * @author <a href="http://mina.apache.org">Apache MINA Project</a>
032 */
033public class Subnet {
034
035    private static final int IP_MASK_V4 = 0x80000000;
036
037    private static final long IP_MASK_V6 = 0x8000000000000000L;
038
039    private static final int BYTE_MASK = 0xFF;
040
041    private InetAddress subnet;
042
043    /** An int representation of a subnet for IPV4 addresses */
044    private int subnetInt;
045
046    /** An long representation of a subnet for IPV6 addresses */
047    private long subnetLong;
048
049    private long subnetMask;
050
051    private int suffix;
052
053    /**
054     * Creates a subnet from CIDR notation. For example, the subnet
055     * 192.168.0.0/24 would be created using the {@link InetAddress}
056     * 192.168.0.0 and the mask 24.
057     * @param subnet The {@link InetAddress} of the subnet
058     * @param mask The mask
059     */
060    public Subnet(InetAddress subnet, int mask) {
061        if (subnet == null) {
062            throw new IllegalArgumentException("Subnet address can not be null");
063        }
064
065        if (!(subnet instanceof Inet4Address) && !(subnet instanceof Inet6Address)) {
066            throw new IllegalArgumentException("Only IPv4 and IPV6 supported");
067        }
068
069        if (subnet instanceof Inet4Address) {
070            // IPV4 address
071            if ((mask < 0) || (mask > 32)) {
072                throw new IllegalArgumentException("Mask has to be an integer between 0 and 32 for an IPV4 address");
073            } else {
074                this.subnet = subnet;
075                subnetInt = toInt(subnet);
076                this.suffix = mask;
077
078                // binary mask for this subnet
079                this.subnetMask = IP_MASK_V4 >> (mask - 1);
080            }
081        } else {
082            // IPV6 address
083            if ((mask < 0) || (mask > 128)) {
084                throw new IllegalArgumentException("Mask has to be an integer between 0 and 128 for an IPV6 address");
085            } else {
086                this.subnet = subnet;
087                subnetLong = toLong(subnet);
088                this.suffix = mask;
089
090                // binary mask for this subnet
091                this.subnetMask = IP_MASK_V6 >> (mask - 1);
092            }
093        }
094    }
095
096    /**
097     * Converts an IP address into an integer
098     */
099    private int toInt(InetAddress inetAddress) {
100        byte[] address = inetAddress.getAddress();
101        int result = 0;
102
103        for (int i = 0; i < address.length; i++) {
104            result <<= 8;
105            result |= address[i] & BYTE_MASK;
106        }
107
108        return result;
109    }
110
111    /**
112     * Converts an IP address into a long
113     */
114    private long toLong(InetAddress inetAddress) {
115        byte[] address = inetAddress.getAddress();
116        long result = 0;
117
118        for (int i = 0; i < address.length; i++) {
119            result <<= 8;
120            result |= address[i] & BYTE_MASK;
121        }
122
123        return result;
124    }
125
126    /**
127     * Converts an IP address to a subnet using the provided mask
128     * 
129     * @param address
130     *            The address to convert into a subnet
131     * @return The subnet as an integer
132     */
133    private long toSubnet(InetAddress address) {
134        if (address instanceof Inet4Address) {
135            return toInt(address) & (int) subnetMask;
136        } else {
137            return toLong(address) & subnetMask;
138        }
139    }
140
141    /**
142     * Checks if the {@link InetAddress} is within this subnet
143     * @param address The {@link InetAddress} to check
144     * @return True if the address is within this subnet, false otherwise
145     */
146    public boolean inSubnet(InetAddress address) {
147        if (address.isAnyLocalAddress()) {
148            return true;
149        }
150
151        if (address instanceof Inet4Address) {
152            return (int) toSubnet(address) == subnetInt;
153        } else {
154            return toSubnet(address) == subnetLong;
155        }
156    }
157
158    /**
159     * @see Object#toString()
160     */
161    @Override
162    public String toString() {
163        return subnet.getHostAddress() + "/" + suffix;
164    }
165
166    @Override
167    public boolean equals(Object obj) {
168        if (!(obj instanceof Subnet)) {
169            return false;
170        }
171
172        Subnet other = (Subnet) obj;
173
174        return other.subnetInt == subnetInt && other.suffix == suffix;
175    }
176
177}