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.net.IpPrefix;
20import android.os.Parcel;
21import static android.test.MoreAsserts.assertNotEqual;
22import android.test.suitebuilder.annotation.SmallTest;
23
24import static org.junit.Assert.assertArrayEquals;
25import java.net.InetAddress;
26import java.util.Random;
27import junit.framework.TestCase;
28
29
30public class IpPrefixTest extends TestCase {
31
32    private static InetAddress Address(String addr) {
33        return InetAddress.parseNumericAddress(addr);
34    }
35
36    // Explicitly cast everything to byte because "error: possible loss of precision".
37    private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4};
38    private static final byte[] IPV6_BYTES = {
39        (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8,
40        (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef,
41        (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00,
42        (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0
43    };
44
45    @SmallTest
46    public void testConstructor() {
47        IpPrefix p;
48        try {
49            p = new IpPrefix((byte[]) null, 9);
50            fail("Expected NullPointerException: null byte array");
51        } catch(RuntimeException expected) {}
52
53        try {
54            p = new IpPrefix((InetAddress) null, 10);
55            fail("Expected NullPointerException: null InetAddress");
56        } catch(RuntimeException expected) {}
57
58        try {
59            p = new IpPrefix((String) null);
60            fail("Expected NullPointerException: null String");
61        } catch(RuntimeException expected) {}
62
63
64        try {
65            byte[] b2 = {1, 2, 3, 4, 5};
66            p = new IpPrefix(b2, 29);
67            fail("Expected IllegalArgumentException: invalid array length");
68        } catch(IllegalArgumentException expected) {}
69
70        try {
71            p = new IpPrefix("1.2.3.4");
72            fail("Expected IllegalArgumentException: no prefix length");
73        } catch(IllegalArgumentException expected) {}
74
75        try {
76            p = new IpPrefix("1.2.3.4/");
77            fail("Expected IllegalArgumentException: empty prefix length");
78        } catch(IllegalArgumentException expected) {}
79
80        try {
81            p = new IpPrefix("foo/32");
82            fail("Expected IllegalArgumentException: invalid address");
83        } catch(IllegalArgumentException expected) {}
84
85        try {
86            p = new IpPrefix("1/32");
87            fail("Expected IllegalArgumentException: deprecated IPv4 format");
88        } catch(IllegalArgumentException expected) {}
89
90        try {
91            p = new IpPrefix("1.2.3.256/32");
92            fail("Expected IllegalArgumentException: invalid IPv4 address");
93        } catch(IllegalArgumentException expected) {}
94
95        try {
96            p = new IpPrefix("foo/32");
97            fail("Expected IllegalArgumentException: non-address");
98        } catch(IllegalArgumentException expected) {}
99
100        try {
101            p = new IpPrefix("f00:::/32");
102            fail("Expected IllegalArgumentException: invalid IPv6 address");
103        } catch(IllegalArgumentException expected) {}
104    }
105
106    public void testTruncation() {
107        IpPrefix p;
108
109        p = new IpPrefix(IPV4_BYTES, 32);
110        assertEquals("192.0.2.4/32", p.toString());
111
112        p = new IpPrefix(IPV4_BYTES, 29);
113        assertEquals("192.0.2.0/29", p.toString());
114
115        p = new IpPrefix(IPV4_BYTES, 8);
116        assertEquals("192.0.0.0/8", p.toString());
117
118        p = new IpPrefix(IPV4_BYTES, 0);
119        assertEquals("0.0.0.0/0", p.toString());
120
121        try {
122            p = new IpPrefix(IPV4_BYTES, 33);
123            fail("Expected IllegalArgumentException: invalid prefix length");
124        } catch(RuntimeException expected) {}
125
126        try {
127            p = new IpPrefix(IPV4_BYTES, 128);
128            fail("Expected IllegalArgumentException: invalid prefix length");
129        } catch(RuntimeException expected) {}
130
131        try {
132            p = new IpPrefix(IPV4_BYTES, -1);
133            fail("Expected IllegalArgumentException: negative prefix length");
134        } catch(RuntimeException expected) {}
135
136        p = new IpPrefix(IPV6_BYTES, 128);
137        assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString());
138
139        p = new IpPrefix(IPV6_BYTES, 122);
140        assertEquals("2001:db8:dead:beef:f00::80/122", p.toString());
141
142        p = new IpPrefix(IPV6_BYTES, 64);
143        assertEquals("2001:db8:dead:beef::/64", p.toString());
144
145        p = new IpPrefix(IPV6_BYTES, 3);
146        assertEquals("2000::/3", p.toString());
147
148        p = new IpPrefix(IPV6_BYTES, 0);
149        assertEquals("::/0", p.toString());
150
151        try {
152            p = new IpPrefix(IPV6_BYTES, -1);
153            fail("Expected IllegalArgumentException: negative prefix length");
154        } catch(RuntimeException expected) {}
155
156        try {
157            p = new IpPrefix(IPV6_BYTES, 129);
158            fail("Expected IllegalArgumentException: negative prefix length");
159        } catch(RuntimeException expected) {}
160
161    }
162
163    private void assertAreEqual(Object o1, Object o2) {
164        assertTrue(o1.equals(o2));
165        assertTrue(o2.equals(o1));
166    }
167
168    private void assertAreNotEqual(Object o1, Object o2) {
169        assertFalse(o1.equals(o2));
170        assertFalse(o2.equals(o1));
171    }
172
173    @SmallTest
174    public void testEquals() {
175        IpPrefix p1, p2;
176
177        p1 = new IpPrefix("192.0.2.251/23");
178        p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23);
179        assertAreEqual(p1, p2);
180
181        p1 = new IpPrefix("192.0.2.5/23");
182        assertAreEqual(p1, p2);
183
184        p1 = new IpPrefix("192.0.2.5/24");
185        assertAreNotEqual(p1, p2);
186
187        p1 = new IpPrefix("192.0.4.5/23");
188        assertAreNotEqual(p1, p2);
189
190
191        p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122");
192        p2 = new IpPrefix(IPV6_BYTES, 122);
193        assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString());
194        assertAreEqual(p1, p2);
195
196        p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122");
197        assertAreEqual(p1, p2);
198
199        p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123");
200        assertAreNotEqual(p1, p2);
201
202        p1 = new IpPrefix("2001:db8:dead:beef::/122");
203        assertAreNotEqual(p1, p2);
204
205        // 192.0.2.4/32 != c000:0204::/32.
206        byte[] ipv6bytes = new byte[16];
207        System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length);
208        p1 = new IpPrefix(ipv6bytes, 32);
209        assertAreEqual(p1, new IpPrefix("c000:0204::/32"));
210
211        p2 = new IpPrefix(IPV4_BYTES, 32);
212        assertAreNotEqual(p1, p2);
213    }
214
215    @SmallTest
216    public void testContains() {
217        IpPrefix p = new IpPrefix("2001:db8:f00::ace:d00d/127");
218        assertTrue(p.contains(Address("2001:db8:f00::ace:d00c")));
219        assertTrue(p.contains(Address("2001:db8:f00::ace:d00d")));
220        assertFalse(p.contains(Address("2001:db8:f00::ace:d00e")));
221        assertFalse(p.contains(Address("2001:db8:f00::bad:d00d")));
222        assertFalse(p.contains(Address("2001:4868:4860::8888")));
223        assertFalse(p.contains(null));
224        assertFalse(p.contains(Address("8.8.8.8")));
225
226        p = new IpPrefix("192.0.2.0/23");
227        assertTrue(p.contains(Address("192.0.2.43")));
228        assertTrue(p.contains(Address("192.0.3.21")));
229        assertFalse(p.contains(Address("192.0.0.21")));
230        assertFalse(p.contains(Address("8.8.8.8")));
231        assertFalse(p.contains(Address("2001:4868:4860::8888")));
232
233        IpPrefix ipv6Default = new IpPrefix("::/0");
234        assertTrue(ipv6Default.contains(Address("2001:db8::f00")));
235        assertFalse(ipv6Default.contains(Address("192.0.2.1")));
236
237        IpPrefix ipv4Default = new IpPrefix("0.0.0.0/0");
238        assertTrue(ipv4Default.contains(Address("255.255.255.255")));
239        assertTrue(ipv4Default.contains(Address("192.0.2.1")));
240        assertFalse(ipv4Default.contains(Address("2001:db8::f00")));
241    }
242
243    @SmallTest
244    public void testHashCode() {
245        IpPrefix p;
246        int oldCode = -1;
247        Random random = new Random();
248        for (int i = 0; i < 100; i++) {
249            if (random.nextBoolean()) {
250                // IPv4.
251                byte[] b = new byte[4];
252                random.nextBytes(b);
253                p = new IpPrefix(b, random.nextInt(33));
254                assertNotEqual(oldCode, p.hashCode());
255                oldCode = p.hashCode();
256            } else {
257                // IPv6.
258                byte[] b = new byte[16];
259                random.nextBytes(b);
260                p = new IpPrefix(b, random.nextInt(129));
261                assertNotEqual(oldCode, p.hashCode());
262                oldCode = p.hashCode();
263            }
264        }
265    }
266
267    @SmallTest
268    public void testMappedAddressesAreBroken() {
269        // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress,
270        // we are unable to comprehend that.
271        byte[] ipv6bytes = {
272            (byte) 0, (byte) 0, (byte) 0, (byte) 0,
273            (byte) 0, (byte) 0, (byte) 0, (byte) 0,
274            (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff,
275            (byte) 192, (byte) 0, (byte) 2, (byte) 0};
276        IpPrefix p = new IpPrefix(ipv6bytes, 120);
277        assertEquals(16, p.getRawAddress().length);       // Fine.
278        assertArrayEquals(ipv6bytes, p.getRawAddress());  // Fine.
279
280        // Broken.
281        assertEquals("192.0.2.0/120", p.toString());
282        assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress());
283    }
284
285    public IpPrefix passThroughParcel(IpPrefix p) {
286        Parcel parcel = Parcel.obtain();
287        IpPrefix p2 = null;
288        try {
289            p.writeToParcel(parcel, 0);
290            parcel.setDataPosition(0);
291            p2 = IpPrefix.CREATOR.createFromParcel(parcel);
292        } finally {
293            parcel.recycle();
294        }
295        assertNotNull(p2);
296        return p2;
297    }
298
299    public void assertParcelingIsLossless(IpPrefix p) {
300      IpPrefix p2 = passThroughParcel(p);
301      assertEquals(p, p2);
302    }
303
304    public void testParceling() {
305        IpPrefix p;
306
307        p = new IpPrefix("2001:4860:db8::/64");
308        assertParcelingIsLossless(p);
309
310        p = new IpPrefix("192.0.2.0/25");
311        assertParcelingIsLossless(p);
312    }
313}
314