1/*
2 * Copyright (C) 2014 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * 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,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17package android.net;
18
19import android.os.Parcel;
20import android.os.Parcelable;
21import android.util.Pair;
22
23import java.net.Inet4Address;
24import java.net.Inet6Address;
25import java.net.InetAddress;
26import java.net.UnknownHostException;
27import java.util.Arrays;
28
29/**
30 * This class represents an IP prefix, i.e., a contiguous block of IP addresses aligned on a
31 * power of two boundary (also known as an "IP subnet"). A prefix is specified by two pieces of
32 * information:
33 *
34 * <ul>
35 * <li>A starting IP address (IPv4 or IPv6). This is the first IP address of the prefix.
36 * <li>A prefix length. This specifies the length of the prefix by specifing the number of bits
37 *     in the IP address, starting from the most significant bit in network byte order, that
38 *     are constant for all addresses in the prefix.
39 * </ul>
40 *
41 * For example, the prefix <code>192.0.2.0/24</code> covers the 256 IPv4 addresses from
42 * <code>192.0.2.0</code> to <code>192.0.2.255</code>, inclusive, and the prefix
43 * <code>2001:db8:1:2</code>  covers the 2^64 IPv6 addresses from <code>2001:db8:1:2::</code> to
44 * <code>2001:db8:1:2:ffff:ffff:ffff:ffff</code>, inclusive.
45 *
46 * Objects of this class are immutable.
47 */
48public final class IpPrefix implements Parcelable {
49    private final byte[] address;  // network byte order
50    private final int prefixLength;
51
52    private void checkAndMaskAddressAndPrefixLength() {
53        if (address.length != 4 && address.length != 16) {
54            throw new IllegalArgumentException(
55                    "IpPrefix has " + address.length + " bytes which is neither 4 nor 16");
56        }
57        NetworkUtils.maskRawAddress(address, prefixLength);
58    }
59
60    /**
61     * Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in
62     * network byte order and a prefix length. Silently truncates the address to the prefix length,
63     * so for example {@code 192.0.2.1/24} is silently converted to {@code 192.0.2.0/24}.
64     *
65     * @param address the IP address. Must be non-null and exactly 4 or 16 bytes long.
66     * @param prefixLength the prefix length. Must be &gt;= 0 and &lt;= (32 or 128) (IPv4 or IPv6).
67     *
68     * @hide
69     */
70    public IpPrefix(byte[] address, int prefixLength) {
71        this.address = address.clone();
72        this.prefixLength = prefixLength;
73        checkAndMaskAddressAndPrefixLength();
74    }
75
76    /**
77     * Constructs a new {@code IpPrefix} from an IPv4 or IPv6 address and a prefix length. Silently
78     * truncates the address to the prefix length, so for example {@code 192.0.2.1/24} is silently
79     * converted to {@code 192.0.2.0/24}.
80     *
81     * @param address the IP address. Must be non-null.
82     * @param prefixLength the prefix length. Must be &gt;= 0 and &lt;= (32 or 128) (IPv4 or IPv6).
83     * @hide
84     */
85    public IpPrefix(InetAddress address, int prefixLength) {
86        // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array,
87        // which is unnecessary because getAddress() already returns a clone.
88        this.address = address.getAddress();
89        this.prefixLength = prefixLength;
90        checkAndMaskAddressAndPrefixLength();
91    }
92
93    /**
94     * Constructs a new IpPrefix from a string such as "192.0.2.1/24" or "2001:db8::1/64".
95     * Silently truncates the address to the prefix length, so for example {@code 192.0.2.1/24}
96     * is silently converted to {@code 192.0.2.0/24}.
97     *
98     * @param prefix the prefix to parse
99     *
100     * @hide
101     */
102    public IpPrefix(String prefix) {
103        // We don't reuse the (InetAddress, int) constructor because "error: call to this must be
104        // first statement in constructor". We could factor out setting the member variables to an
105        // init() method, but if we did, then we'd have to make the members non-final, or "error:
106        // cannot assign a value to final variable address". So we just duplicate the code here.
107        Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix);
108        this.address = ipAndMask.first.getAddress();
109        this.prefixLength = ipAndMask.second;
110        checkAndMaskAddressAndPrefixLength();
111    }
112
113    /**
114     * Compares this {@code IpPrefix} object against the specified object in {@code obj}. Two
115     * objects are equal if they have the same startAddress and prefixLength.
116     *
117     * @param obj the object to be tested for equality.
118     * @return {@code true} if both objects are equal, {@code false} otherwise.
119     */
120    @Override
121    public boolean equals(Object obj) {
122        if (!(obj instanceof IpPrefix)) {
123            return false;
124        }
125        IpPrefix that = (IpPrefix) obj;
126        return Arrays.equals(this.address, that.address) && this.prefixLength == that.prefixLength;
127    }
128
129    /**
130     * Gets the hashcode of the represented IP prefix.
131     *
132     * @return the appropriate hashcode value.
133     */
134    @Override
135    public int hashCode() {
136        return Arrays.hashCode(address) + 11 * prefixLength;
137    }
138
139    /**
140     * Returns a copy of the first IP address in the prefix. Modifying the returned object does not
141     * change this object's contents.
142     *
143     * @return the address in the form of a byte array.
144     */
145    public InetAddress getAddress() {
146        try {
147            return InetAddress.getByAddress(address);
148        } catch (UnknownHostException e) {
149            // Cannot happen. InetAddress.getByAddress can only throw an exception if the byte
150            // array is the wrong length, but we check that in the constructor.
151            return null;
152        }
153    }
154
155    /**
156     * Returns a copy of the IP address bytes in network order (the highest order byte is the zeroth
157     * element). Modifying the returned array does not change this object's contents.
158     *
159     * @return the address in the form of a byte array.
160     */
161    public byte[] getRawAddress() {
162        return address.clone();
163    }
164
165    /**
166     * Returns the prefix length of this {@code IpPrefix}.
167     *
168     * @return the prefix length.
169     */
170    public int getPrefixLength() {
171        return prefixLength;
172    }
173
174    /**
175     * Determines whether the prefix contains the specified address.
176     *
177     * @param address An {@link InetAddress} to test.
178     * @return {@code true} if the prefix covers the given address.
179     */
180    public boolean contains(InetAddress address) {
181        byte[] addrBytes = (address == null) ? null : address.getAddress();
182        if (addrBytes == null || addrBytes.length != this.address.length) {
183            return false;
184        }
185        NetworkUtils.maskRawAddress(addrBytes, prefixLength);
186        return Arrays.equals(this.address, addrBytes);
187    }
188
189    /**
190     * @hide
191     */
192    public boolean isIPv6() {
193        return getAddress() instanceof Inet6Address;
194    }
195
196    /**
197     * @hide
198     */
199    public boolean isIPv4() {
200        return getAddress() instanceof Inet4Address;
201    }
202
203    /**
204     * Returns a string representation of this {@code IpPrefix}.
205     *
206     * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::/64"}.
207     */
208    public String toString() {
209        try {
210            return InetAddress.getByAddress(address).getHostAddress() + "/" + prefixLength;
211        } catch(UnknownHostException e) {
212            // Cosmic rays?
213            throw new IllegalStateException("IpPrefix with invalid address! Shouldn't happen.", e);
214        }
215    }
216
217    /**
218     * Implement the Parcelable interface.
219     */
220    public int describeContents() {
221        return 0;
222    }
223
224    /**
225     * Implement the Parcelable interface.
226     */
227    public void writeToParcel(Parcel dest, int flags) {
228        dest.writeByteArray(address);
229        dest.writeInt(prefixLength);
230    }
231
232    /**
233     * Implement the Parcelable interface.
234     */
235    public static final Creator<IpPrefix> CREATOR =
236            new Creator<IpPrefix>() {
237                public IpPrefix createFromParcel(Parcel in) {
238                    byte[] address = in.createByteArray();
239                    int prefixLength = in.readInt();
240                    return new IpPrefix(address, prefixLength);
241                }
242
243                public IpPrefix[] newArray(int size) {
244                    return new IpPrefix[size];
245                }
246            };
247}
248