Loading core/java/android/net/IpPrefix.java +58 −12 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; import java.net.InetAddress; import java.net.UnknownHostException; Loading Loading @@ -46,9 +47,18 @@ public final class IpPrefix implements Parcelable { private final byte[] address; // network byte order private final int prefixLength; private void checkAndMaskAddressAndPrefixLength() { if (address.length != 4 && address.length != 16) { throw new IllegalArgumentException( "IpPrefix has " + address.length + " bytes which is neither 4 nor 16"); } NetworkUtils.maskRawAddress(address, prefixLength); } /** * Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in * network byte order and a prefix length. * network byte order and a prefix length. Silently truncates the address to the prefix length, * so for example {@code 192.0.2.1/24} is silently converted to {@code 192.0.2.0/24}. * * @param address the IP address. Must be non-null and exactly 4 or 16 bytes long. * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). Loading @@ -56,24 +66,46 @@ public final class IpPrefix implements Parcelable { * @hide */ public IpPrefix(byte[] address, int prefixLength) { if (address.length != 4 && address.length != 16) { throw new IllegalArgumentException( "IpPrefix has " + address.length + " bytes which is neither 4 nor 16"); } if (prefixLength < 0 || prefixLength > (address.length * 8)) { throw new IllegalArgumentException("IpPrefix with " + address.length + " bytes has invalid prefix length " + prefixLength); } this.address = address.clone(); this.prefixLength = prefixLength; // TODO: Validate that the non-prefix bits are zero checkAndMaskAddressAndPrefixLength(); } /** * Constructs a new {@code IpPrefix} from an IPv4 or IPv6 address and a prefix length. Silently * truncates the address to the prefix length, so for example {@code 192.0.2.1/24} is silently * converted to {@code 192.0.2.0/24}. * * @param address the IP address. Must be non-null. * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). * @hide */ public IpPrefix(InetAddress address, int prefixLength) { this(address.getAddress(), prefixLength); // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array, // which is unnecessary because getAddress() already returns a clone. this.address = address.getAddress(); this.prefixLength = prefixLength; checkAndMaskAddressAndPrefixLength(); } /** * Constructs a new IpPrefix from a string such as "192.0.2.1/24" or "2001:db8::1/64". * Silently truncates the address to the prefix length, so for example {@code 192.0.2.1/24} * is silently converted to {@code 192.0.2.0/24}. * * @param prefix the prefix to parse * * @hide */ public IpPrefix(String prefix) { // We don't reuse the (InetAddress, int) constructor because "error: call to this must be // first statement in constructor". We could factor out setting the member variables to an // init() method, but if we did, then we'd have to make the members non-final, or "error: // cannot assign a value to final variable address". So we just duplicate the code here. Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix); this.address = ipAndMask.first.getAddress(); this.prefixLength = ipAndMask.second; checkAndMaskAddressAndPrefixLength(); } /** Loading Loading @@ -129,7 +161,7 @@ public final class IpPrefix implements Parcelable { } /** * Returns the prefix length of this {@code IpAddress}. * Returns the prefix length of this {@code IpPrefix}. * * @return the prefix length. */ Loading @@ -137,6 +169,20 @@ public final class IpPrefix implements Parcelable { return prefixLength; } /** * Returns a string representation of this {@code IpPrefix}. * * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::"}. */ public String toString() { try { return InetAddress.getByAddress(address).getHostAddress() + "/" + prefixLength; } catch(UnknownHostException e) { // Cosmic rays? throw new IllegalStateException("IpPrefix with invalid address! Shouldn't happen.", e); } } /** * Implement the Parcelable interface. */ Loading core/java/android/net/LinkAddress.java +4 −17 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; import java.net.Inet4Address; import java.net.InetAddress; Loading Loading @@ -166,23 +167,9 @@ public class LinkAddress implements Parcelable { * @hide */ public LinkAddress(String address, int flags, int scope) { InetAddress inetAddress = null; int prefixLength = -1; try { String [] pieces = address.split("/", 2); prefixLength = Integer.parseInt(pieces[1]); inetAddress = InetAddress.parseNumericAddress(pieces[0]); } catch (NullPointerException e) { // Null string. } catch (ArrayIndexOutOfBoundsException e) { // No prefix length. } catch (NumberFormatException e) { // Non-numeric prefix. } catch (IllegalArgumentException e) { // Invalid IP address. } if (inetAddress == null || prefixLength == -1) { throw new IllegalArgumentException("Bad LinkAddress params " + address); } init(inetAddress, prefixLength, flags, scope); // This may throw an IllegalArgumentException; catching it is the caller's responsibility. Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address); init(ipAndMask.first, ipAndMask.second, flags, scope); } /** Loading core/java/android/net/NetworkUtils.java +42 −13 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import java.util.Collection; import java.util.Locale; import android.util.Log; import android.util.Pair; /** * Native methods for managing network interfaces. Loading Loading @@ -218,24 +220,17 @@ public class NetworkUtils { } /** * Get InetAddress masked with prefixLength. Will never return null. * @param IP address which will be masked with specified prefixLength * @param prefixLength the prefixLength used to mask the IP * Masks a raw IP address byte array with the specified prefix length. */ public static InetAddress getNetworkPart(InetAddress address, int prefixLength) { if (address == null) { throw new RuntimeException("getNetworkPart doesn't accept null address"); } byte[] array = address.getAddress(); public static void maskRawAddress(byte[] array, int prefixLength) { if (prefixLength < 0 || prefixLength > array.length * 8) { throw new RuntimeException("getNetworkPart - bad prefixLength"); throw new RuntimeException("IP address with " + array.length + " bytes has invalid prefix length " + prefixLength); } int offset = prefixLength / 8; int reminder = prefixLength % 8; byte mask = (byte)(0xFF << (8 - reminder)); int remainder = prefixLength % 8; byte mask = (byte)(0xFF << (8 - remainder)); if (offset < array.length) array[offset] = (byte)(array[offset] & mask); Loading @@ -244,6 +239,16 @@ public class NetworkUtils { for (; offset < array.length; offset++) { array[offset] = 0; } } /** * Get InetAddress masked with prefixLength. Will never return null. * @param address the IP address to mask with * @param prefixLength the prefixLength used to mask the IP */ public static InetAddress getNetworkPart(InetAddress address, int prefixLength) { byte[] array = address.getAddress(); maskRawAddress(array, prefixLength); InetAddress netPart = null; try { Loading @@ -254,6 +259,30 @@ public class NetworkUtils { return netPart; } /** * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64". * @hide */ public static Pair<InetAddress, Integer> parseIpAndMask(String ipAndMaskString) { InetAddress address = null; int prefixLength = -1; try { String[] pieces = ipAndMaskString.split("/", 2); prefixLength = Integer.parseInt(pieces[1]); address = InetAddress.parseNumericAddress(pieces[0]); } catch (NullPointerException e) { // Null string. } catch (ArrayIndexOutOfBoundsException e) { // No prefix length. } catch (NumberFormatException e) { // Non-numeric prefix. } catch (IllegalArgumentException e) { // Invalid IP address. } if (address == null || prefixLength == -1) { throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString); } return new Pair<InetAddress, Integer>(address, prefixLength); } /** * Check if IP address type is consistent between two InetAddress. * @return true if both are the same type. False otherwise. Loading core/tests/coretests/src/android/net/IpPrefixTest.java 0 → 100644 +281 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net; import android.net.IpPrefix; import android.os.Parcel; import static android.test.MoreAsserts.assertNotEqual; import android.test.suitebuilder.annotation.SmallTest; import static org.junit.Assert.assertArrayEquals; import java.net.InetAddress; import java.util.Random; import junit.framework.TestCase; public class IpPrefixTest extends TestCase { // Explicitly cast everything to byte because "error: possible loss of precision". private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4}; private static final byte[] IPV6_BYTES = { (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0 }; @SmallTest public void testConstructor() { IpPrefix p; try { p = new IpPrefix((byte[]) null, 9); fail("Expected NullPointerException: null byte array"); } catch(RuntimeException expected) {} try { p = new IpPrefix((InetAddress) null, 10); fail("Expected NullPointerException: null InetAddress"); } catch(RuntimeException expected) {} try { p = new IpPrefix((String) null); fail("Expected NullPointerException: null String"); } catch(RuntimeException expected) {} try { byte[] b2 = {1, 2, 3, 4, 5}; p = new IpPrefix(b2, 29); fail("Expected IllegalArgumentException: invalid array length"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("1.2.3.4"); fail("Expected IllegalArgumentException: no prefix length"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("1.2.3.4/"); fail("Expected IllegalArgumentException: empty prefix length"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("foo/32"); fail("Expected IllegalArgumentException: invalid address"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("1/32"); fail("Expected IllegalArgumentException: deprecated IPv4 format"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("1.2.3.256/32"); fail("Expected IllegalArgumentException: invalid IPv4 address"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("foo/32"); fail("Expected IllegalArgumentException: non-address"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("f00:::/32"); fail("Expected IllegalArgumentException: invalid IPv6 address"); } catch(IllegalArgumentException expected) {} } public void testTruncation() { IpPrefix p; p = new IpPrefix(IPV4_BYTES, 32); assertEquals("192.0.2.4/32", p.toString()); p = new IpPrefix(IPV4_BYTES, 29); assertEquals("192.0.2.0/29", p.toString()); p = new IpPrefix(IPV4_BYTES, 8); assertEquals("192.0.0.0/8", p.toString()); p = new IpPrefix(IPV4_BYTES, 0); assertEquals("0.0.0.0/0", p.toString()); try { p = new IpPrefix(IPV4_BYTES, 33); fail("Expected IllegalArgumentException: invalid prefix length"); } catch(RuntimeException expected) {} try { p = new IpPrefix(IPV4_BYTES, 128); fail("Expected IllegalArgumentException: invalid prefix length"); } catch(RuntimeException expected) {} try { p = new IpPrefix(IPV4_BYTES, -1); fail("Expected IllegalArgumentException: negative prefix length"); } catch(RuntimeException expected) {} p = new IpPrefix(IPV6_BYTES, 128); assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString()); p = new IpPrefix(IPV6_BYTES, 122); assertEquals("2001:db8:dead:beef:f00::80/122", p.toString()); p = new IpPrefix(IPV6_BYTES, 64); assertEquals("2001:db8:dead:beef::/64", p.toString()); p = new IpPrefix(IPV6_BYTES, 3); assertEquals("2000::/3", p.toString()); p = new IpPrefix(IPV6_BYTES, 0); assertEquals("::/0", p.toString()); try { p = new IpPrefix(IPV6_BYTES, -1); fail("Expected IllegalArgumentException: negative prefix length"); } catch(RuntimeException expected) {} try { p = new IpPrefix(IPV6_BYTES, 129); fail("Expected IllegalArgumentException: negative prefix length"); } catch(RuntimeException expected) {} } private void assertAreEqual(Object o1, Object o2) { assertTrue(o1.equals(o2)); assertTrue(o2.equals(o1)); } private void assertAreNotEqual(Object o1, Object o2) { assertFalse(o1.equals(o2)); assertFalse(o2.equals(o1)); } @SmallTest public void testEquals() { IpPrefix p1, p2; p1 = new IpPrefix("192.0.2.251/23"); p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23); assertAreEqual(p1, p2); p1 = new IpPrefix("192.0.2.5/23"); assertAreEqual(p1, p2); p1 = new IpPrefix("192.0.2.5/24"); assertAreNotEqual(p1, p2); p1 = new IpPrefix("192.0.4.5/23"); assertAreNotEqual(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122"); p2 = new IpPrefix(IPV6_BYTES, 122); assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString()); assertAreEqual(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122"); assertAreEqual(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123"); assertAreNotEqual(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef::/122"); assertAreNotEqual(p1, p2); // 192.0.2.4/32 != c000:0204::/32. byte[] ipv6bytes = new byte[16]; System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length); p1 = new IpPrefix(ipv6bytes, 32); assertAreEqual(p1, new IpPrefix("c000:0204::/32")); p2 = new IpPrefix(IPV4_BYTES, 32); assertAreNotEqual(p1, p2); } @SmallTest public void testHashCode() { IpPrefix p; int oldCode = -1; Random random = new Random(); for (int i = 0; i < 100; i++) { if (random.nextBoolean()) { // IPv4. byte[] b = new byte[4]; random.nextBytes(b); p = new IpPrefix(b, random.nextInt(33)); assertNotEqual(oldCode, p.hashCode()); oldCode = p.hashCode(); } else { // IPv6. byte[] b = new byte[16]; random.nextBytes(b); p = new IpPrefix(b, random.nextInt(129)); assertNotEqual(oldCode, p.hashCode()); oldCode = p.hashCode(); } } } @SmallTest public void testMappedAddressesAreBroken() { // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress, // we are unable to comprehend that. byte[] ipv6bytes = { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff, (byte) 192, (byte) 0, (byte) 2, (byte) 0}; IpPrefix p = new IpPrefix(ipv6bytes, 120); assertEquals(16, p.getRawAddress().length); // Fine. assertArrayEquals(ipv6bytes, p.getRawAddress()); // Fine. // Broken. assertEquals("192.0.2.0/120", p.toString()); assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress()); } public IpPrefix passThroughParcel(IpPrefix p) { Parcel parcel = Parcel.obtain(); IpPrefix p2 = null; try { p.writeToParcel(parcel, 0); parcel.setDataPosition(0); p2 = IpPrefix.CREATOR.createFromParcel(parcel); } finally { parcel.recycle(); } assertNotNull(p2); return p2; } public void assertParcelingIsLossless(IpPrefix p) { IpPrefix p2 = passThroughParcel(p); assertEquals(p, p2); } public void testParceling() { IpPrefix p; p = new IpPrefix("2001:4860:db8::/64"); assertParcelingIsLossless(p); p = new IpPrefix("192.0.2.0/25"); assertParcelingIsLossless(p); } } core/tests/coretests/src/android/net/RouteInfoTest.java +7 −8 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package android.net; import java.lang.reflect.Method; import java.net.InetAddress; import android.net.LinkAddress; import android.net.IpPrefix; import android.net.RouteInfo; import android.os.Parcel; Loading @@ -32,9 +32,8 @@ public class RouteInfoTest extends TestCase { return InetAddress.parseNumericAddress(addr); } private LinkAddress Prefix(String prefix) { String[] parts = prefix.split("/"); return new LinkAddress(Address(parts[0]), Integer.parseInt(parts[1])); private IpPrefix Prefix(String prefix) { return new IpPrefix(prefix); } @SmallTest Loading @@ -43,17 +42,17 @@ public class RouteInfoTest extends TestCase { // Invalid input. try { r = new RouteInfo((LinkAddress) null, null, "rmnet0"); r = new RouteInfo((IpPrefix) null, null, "rmnet0"); fail("Expected RuntimeException: destination and gateway null"); } catch(RuntimeException e) {} // Null destination is default route. r = new RouteInfo((LinkAddress) null, Address("2001:db8::1"), null); r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null); assertEquals(Prefix("::/0"), r.getDestination()); assertEquals(Address("2001:db8::1"), r.getGateway()); assertNull(r.getInterface()); r = new RouteInfo((LinkAddress) null, Address("192.0.2.1"), "wlan0"); r = new RouteInfo((IpPrefix) null, Address("192.0.2.1"), "wlan0"); assertEquals(Prefix("0.0.0.0/0"), r.getDestination()); assertEquals(Address("192.0.2.1"), r.getGateway()); assertEquals("wlan0", r.getInterface()); Loading @@ -74,7 +73,7 @@ public class RouteInfoTest extends TestCase { class PatchedRouteInfo { private final RouteInfo mRouteInfo; public PatchedRouteInfo(LinkAddress destination, InetAddress gateway, String iface) { public PatchedRouteInfo(IpPrefix destination, InetAddress gateway, String iface) { mRouteInfo = new RouteInfo(destination, gateway, iface); } Loading Loading
core/java/android/net/IpPrefix.java +58 −12 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; import java.net.InetAddress; import java.net.UnknownHostException; Loading Loading @@ -46,9 +47,18 @@ public final class IpPrefix implements Parcelable { private final byte[] address; // network byte order private final int prefixLength; private void checkAndMaskAddressAndPrefixLength() { if (address.length != 4 && address.length != 16) { throw new IllegalArgumentException( "IpPrefix has " + address.length + " bytes which is neither 4 nor 16"); } NetworkUtils.maskRawAddress(address, prefixLength); } /** * Constructs a new {@code IpPrefix} from a byte array containing an IPv4 or IPv6 address in * network byte order and a prefix length. * network byte order and a prefix length. Silently truncates the address to the prefix length, * so for example {@code 192.0.2.1/24} is silently converted to {@code 192.0.2.0/24}. * * @param address the IP address. Must be non-null and exactly 4 or 16 bytes long. * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). Loading @@ -56,24 +66,46 @@ public final class IpPrefix implements Parcelable { * @hide */ public IpPrefix(byte[] address, int prefixLength) { if (address.length != 4 && address.length != 16) { throw new IllegalArgumentException( "IpPrefix has " + address.length + " bytes which is neither 4 nor 16"); } if (prefixLength < 0 || prefixLength > (address.length * 8)) { throw new IllegalArgumentException("IpPrefix with " + address.length + " bytes has invalid prefix length " + prefixLength); } this.address = address.clone(); this.prefixLength = prefixLength; // TODO: Validate that the non-prefix bits are zero checkAndMaskAddressAndPrefixLength(); } /** * Constructs a new {@code IpPrefix} from an IPv4 or IPv6 address and a prefix length. Silently * truncates the address to the prefix length, so for example {@code 192.0.2.1/24} is silently * converted to {@code 192.0.2.0/24}. * * @param address the IP address. Must be non-null. * @param prefixLength the prefix length. Must be >= 0 and <= (32 or 128) (IPv4 or IPv6). * @hide */ public IpPrefix(InetAddress address, int prefixLength) { this(address.getAddress(), prefixLength); // We don't reuse the (byte[], int) constructor because it calls clone() on the byte array, // which is unnecessary because getAddress() already returns a clone. this.address = address.getAddress(); this.prefixLength = prefixLength; checkAndMaskAddressAndPrefixLength(); } /** * Constructs a new IpPrefix from a string such as "192.0.2.1/24" or "2001:db8::1/64". * Silently truncates the address to the prefix length, so for example {@code 192.0.2.1/24} * is silently converted to {@code 192.0.2.0/24}. * * @param prefix the prefix to parse * * @hide */ public IpPrefix(String prefix) { // We don't reuse the (InetAddress, int) constructor because "error: call to this must be // first statement in constructor". We could factor out setting the member variables to an // init() method, but if we did, then we'd have to make the members non-final, or "error: // cannot assign a value to final variable address". So we just duplicate the code here. Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(prefix); this.address = ipAndMask.first.getAddress(); this.prefixLength = ipAndMask.second; checkAndMaskAddressAndPrefixLength(); } /** Loading Loading @@ -129,7 +161,7 @@ public final class IpPrefix implements Parcelable { } /** * Returns the prefix length of this {@code IpAddress}. * Returns the prefix length of this {@code IpPrefix}. * * @return the prefix length. */ Loading @@ -137,6 +169,20 @@ public final class IpPrefix implements Parcelable { return prefixLength; } /** * Returns a string representation of this {@code IpPrefix}. * * @return a string such as {@code "192.0.2.0/24"} or {@code "2001:db8:1:2::"}. */ public String toString() { try { return InetAddress.getByAddress(address).getHostAddress() + "/" + prefixLength; } catch(UnknownHostException e) { // Cosmic rays? throw new IllegalStateException("IpPrefix with invalid address! Shouldn't happen.", e); } } /** * Implement the Parcelable interface. */ Loading
core/java/android/net/LinkAddress.java +4 −17 Original line number Diff line number Diff line Loading @@ -18,6 +18,7 @@ package android.net; import android.os.Parcel; import android.os.Parcelable; import android.util.Pair; import java.net.Inet4Address; import java.net.InetAddress; Loading Loading @@ -166,23 +167,9 @@ public class LinkAddress implements Parcelable { * @hide */ public LinkAddress(String address, int flags, int scope) { InetAddress inetAddress = null; int prefixLength = -1; try { String [] pieces = address.split("/", 2); prefixLength = Integer.parseInt(pieces[1]); inetAddress = InetAddress.parseNumericAddress(pieces[0]); } catch (NullPointerException e) { // Null string. } catch (ArrayIndexOutOfBoundsException e) { // No prefix length. } catch (NumberFormatException e) { // Non-numeric prefix. } catch (IllegalArgumentException e) { // Invalid IP address. } if (inetAddress == null || prefixLength == -1) { throw new IllegalArgumentException("Bad LinkAddress params " + address); } init(inetAddress, prefixLength, flags, scope); // This may throw an IllegalArgumentException; catching it is the caller's responsibility. Pair<InetAddress, Integer> ipAndMask = NetworkUtils.parseIpAndMask(address); init(ipAndMask.first, ipAndMask.second, flags, scope); } /** Loading
core/java/android/net/NetworkUtils.java +42 −13 Original line number Diff line number Diff line Loading @@ -24,6 +24,8 @@ import java.util.Collection; import java.util.Locale; import android.util.Log; import android.util.Pair; /** * Native methods for managing network interfaces. Loading Loading @@ -218,24 +220,17 @@ public class NetworkUtils { } /** * Get InetAddress masked with prefixLength. Will never return null. * @param IP address which will be masked with specified prefixLength * @param prefixLength the prefixLength used to mask the IP * Masks a raw IP address byte array with the specified prefix length. */ public static InetAddress getNetworkPart(InetAddress address, int prefixLength) { if (address == null) { throw new RuntimeException("getNetworkPart doesn't accept null address"); } byte[] array = address.getAddress(); public static void maskRawAddress(byte[] array, int prefixLength) { if (prefixLength < 0 || prefixLength > array.length * 8) { throw new RuntimeException("getNetworkPart - bad prefixLength"); throw new RuntimeException("IP address with " + array.length + " bytes has invalid prefix length " + prefixLength); } int offset = prefixLength / 8; int reminder = prefixLength % 8; byte mask = (byte)(0xFF << (8 - reminder)); int remainder = prefixLength % 8; byte mask = (byte)(0xFF << (8 - remainder)); if (offset < array.length) array[offset] = (byte)(array[offset] & mask); Loading @@ -244,6 +239,16 @@ public class NetworkUtils { for (; offset < array.length; offset++) { array[offset] = 0; } } /** * Get InetAddress masked with prefixLength. Will never return null. * @param address the IP address to mask with * @param prefixLength the prefixLength used to mask the IP */ public static InetAddress getNetworkPart(InetAddress address, int prefixLength) { byte[] array = address.getAddress(); maskRawAddress(array, prefixLength); InetAddress netPart = null; try { Loading @@ -254,6 +259,30 @@ public class NetworkUtils { return netPart; } /** * Utility method to parse strings such as "192.0.2.5/24" or "2001:db8::cafe:d00d/64". * @hide */ public static Pair<InetAddress, Integer> parseIpAndMask(String ipAndMaskString) { InetAddress address = null; int prefixLength = -1; try { String[] pieces = ipAndMaskString.split("/", 2); prefixLength = Integer.parseInt(pieces[1]); address = InetAddress.parseNumericAddress(pieces[0]); } catch (NullPointerException e) { // Null string. } catch (ArrayIndexOutOfBoundsException e) { // No prefix length. } catch (NumberFormatException e) { // Non-numeric prefix. } catch (IllegalArgumentException e) { // Invalid IP address. } if (address == null || prefixLength == -1) { throw new IllegalArgumentException("Invalid IP address and mask " + ipAndMaskString); } return new Pair<InetAddress, Integer>(address, prefixLength); } /** * Check if IP address type is consistent between two InetAddress. * @return true if both are the same type. False otherwise. Loading
core/tests/coretests/src/android/net/IpPrefixTest.java 0 → 100644 +281 −0 Original line number Diff line number Diff line /* * Copyright (C) 2014 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ package android.net; import android.net.IpPrefix; import android.os.Parcel; import static android.test.MoreAsserts.assertNotEqual; import android.test.suitebuilder.annotation.SmallTest; import static org.junit.Assert.assertArrayEquals; import java.net.InetAddress; import java.util.Random; import junit.framework.TestCase; public class IpPrefixTest extends TestCase { // Explicitly cast everything to byte because "error: possible loss of precision". private static final byte[] IPV4_BYTES = { (byte) 192, (byte) 0, (byte) 2, (byte) 4}; private static final byte[] IPV6_BYTES = { (byte) 0x20, (byte) 0x01, (byte) 0x0d, (byte) 0xb8, (byte) 0xde, (byte) 0xad, (byte) 0xbe, (byte) 0xef, (byte) 0x0f, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0x00, (byte) 0xa0 }; @SmallTest public void testConstructor() { IpPrefix p; try { p = new IpPrefix((byte[]) null, 9); fail("Expected NullPointerException: null byte array"); } catch(RuntimeException expected) {} try { p = new IpPrefix((InetAddress) null, 10); fail("Expected NullPointerException: null InetAddress"); } catch(RuntimeException expected) {} try { p = new IpPrefix((String) null); fail("Expected NullPointerException: null String"); } catch(RuntimeException expected) {} try { byte[] b2 = {1, 2, 3, 4, 5}; p = new IpPrefix(b2, 29); fail("Expected IllegalArgumentException: invalid array length"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("1.2.3.4"); fail("Expected IllegalArgumentException: no prefix length"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("1.2.3.4/"); fail("Expected IllegalArgumentException: empty prefix length"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("foo/32"); fail("Expected IllegalArgumentException: invalid address"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("1/32"); fail("Expected IllegalArgumentException: deprecated IPv4 format"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("1.2.3.256/32"); fail("Expected IllegalArgumentException: invalid IPv4 address"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("foo/32"); fail("Expected IllegalArgumentException: non-address"); } catch(IllegalArgumentException expected) {} try { p = new IpPrefix("f00:::/32"); fail("Expected IllegalArgumentException: invalid IPv6 address"); } catch(IllegalArgumentException expected) {} } public void testTruncation() { IpPrefix p; p = new IpPrefix(IPV4_BYTES, 32); assertEquals("192.0.2.4/32", p.toString()); p = new IpPrefix(IPV4_BYTES, 29); assertEquals("192.0.2.0/29", p.toString()); p = new IpPrefix(IPV4_BYTES, 8); assertEquals("192.0.0.0/8", p.toString()); p = new IpPrefix(IPV4_BYTES, 0); assertEquals("0.0.0.0/0", p.toString()); try { p = new IpPrefix(IPV4_BYTES, 33); fail("Expected IllegalArgumentException: invalid prefix length"); } catch(RuntimeException expected) {} try { p = new IpPrefix(IPV4_BYTES, 128); fail("Expected IllegalArgumentException: invalid prefix length"); } catch(RuntimeException expected) {} try { p = new IpPrefix(IPV4_BYTES, -1); fail("Expected IllegalArgumentException: negative prefix length"); } catch(RuntimeException expected) {} p = new IpPrefix(IPV6_BYTES, 128); assertEquals("2001:db8:dead:beef:f00::a0/128", p.toString()); p = new IpPrefix(IPV6_BYTES, 122); assertEquals("2001:db8:dead:beef:f00::80/122", p.toString()); p = new IpPrefix(IPV6_BYTES, 64); assertEquals("2001:db8:dead:beef::/64", p.toString()); p = new IpPrefix(IPV6_BYTES, 3); assertEquals("2000::/3", p.toString()); p = new IpPrefix(IPV6_BYTES, 0); assertEquals("::/0", p.toString()); try { p = new IpPrefix(IPV6_BYTES, -1); fail("Expected IllegalArgumentException: negative prefix length"); } catch(RuntimeException expected) {} try { p = new IpPrefix(IPV6_BYTES, 129); fail("Expected IllegalArgumentException: negative prefix length"); } catch(RuntimeException expected) {} } private void assertAreEqual(Object o1, Object o2) { assertTrue(o1.equals(o2)); assertTrue(o2.equals(o1)); } private void assertAreNotEqual(Object o1, Object o2) { assertFalse(o1.equals(o2)); assertFalse(o2.equals(o1)); } @SmallTest public void testEquals() { IpPrefix p1, p2; p1 = new IpPrefix("192.0.2.251/23"); p2 = new IpPrefix(new byte[]{(byte) 192, (byte) 0, (byte) 2, (byte) 251}, 23); assertAreEqual(p1, p2); p1 = new IpPrefix("192.0.2.5/23"); assertAreEqual(p1, p2); p1 = new IpPrefix("192.0.2.5/24"); assertAreNotEqual(p1, p2); p1 = new IpPrefix("192.0.4.5/23"); assertAreNotEqual(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::80/122"); p2 = new IpPrefix(IPV6_BYTES, 122); assertEquals("2001:db8:dead:beef:f00::80/122", p2.toString()); assertAreEqual(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::bf/122"); assertAreEqual(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef:f00::8:0/123"); assertAreNotEqual(p1, p2); p1 = new IpPrefix("2001:db8:dead:beef::/122"); assertAreNotEqual(p1, p2); // 192.0.2.4/32 != c000:0204::/32. byte[] ipv6bytes = new byte[16]; System.arraycopy(IPV4_BYTES, 0, ipv6bytes, 0, IPV4_BYTES.length); p1 = new IpPrefix(ipv6bytes, 32); assertAreEqual(p1, new IpPrefix("c000:0204::/32")); p2 = new IpPrefix(IPV4_BYTES, 32); assertAreNotEqual(p1, p2); } @SmallTest public void testHashCode() { IpPrefix p; int oldCode = -1; Random random = new Random(); for (int i = 0; i < 100; i++) { if (random.nextBoolean()) { // IPv4. byte[] b = new byte[4]; random.nextBytes(b); p = new IpPrefix(b, random.nextInt(33)); assertNotEqual(oldCode, p.hashCode()); oldCode = p.hashCode(); } else { // IPv6. byte[] b = new byte[16]; random.nextBytes(b); p = new IpPrefix(b, random.nextInt(129)); assertNotEqual(oldCode, p.hashCode()); oldCode = p.hashCode(); } } } @SmallTest public void testMappedAddressesAreBroken() { // 192.0.2.0/24 != ::ffff:c000:0204/120, but because we use InetAddress, // we are unable to comprehend that. byte[] ipv6bytes = { (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0, (byte) 0xff, (byte) 0xff, (byte) 192, (byte) 0, (byte) 2, (byte) 0}; IpPrefix p = new IpPrefix(ipv6bytes, 120); assertEquals(16, p.getRawAddress().length); // Fine. assertArrayEquals(ipv6bytes, p.getRawAddress()); // Fine. // Broken. assertEquals("192.0.2.0/120", p.toString()); assertEquals(InetAddress.parseNumericAddress("192.0.2.0"), p.getAddress()); } public IpPrefix passThroughParcel(IpPrefix p) { Parcel parcel = Parcel.obtain(); IpPrefix p2 = null; try { p.writeToParcel(parcel, 0); parcel.setDataPosition(0); p2 = IpPrefix.CREATOR.createFromParcel(parcel); } finally { parcel.recycle(); } assertNotNull(p2); return p2; } public void assertParcelingIsLossless(IpPrefix p) { IpPrefix p2 = passThroughParcel(p); assertEquals(p, p2); } public void testParceling() { IpPrefix p; p = new IpPrefix("2001:4860:db8::/64"); assertParcelingIsLossless(p); p = new IpPrefix("192.0.2.0/25"); assertParcelingIsLossless(p); } }
core/tests/coretests/src/android/net/RouteInfoTest.java +7 −8 Original line number Diff line number Diff line Loading @@ -19,7 +19,7 @@ package android.net; import java.lang.reflect.Method; import java.net.InetAddress; import android.net.LinkAddress; import android.net.IpPrefix; import android.net.RouteInfo; import android.os.Parcel; Loading @@ -32,9 +32,8 @@ public class RouteInfoTest extends TestCase { return InetAddress.parseNumericAddress(addr); } private LinkAddress Prefix(String prefix) { String[] parts = prefix.split("/"); return new LinkAddress(Address(parts[0]), Integer.parseInt(parts[1])); private IpPrefix Prefix(String prefix) { return new IpPrefix(prefix); } @SmallTest Loading @@ -43,17 +42,17 @@ public class RouteInfoTest extends TestCase { // Invalid input. try { r = new RouteInfo((LinkAddress) null, null, "rmnet0"); r = new RouteInfo((IpPrefix) null, null, "rmnet0"); fail("Expected RuntimeException: destination and gateway null"); } catch(RuntimeException e) {} // Null destination is default route. r = new RouteInfo((LinkAddress) null, Address("2001:db8::1"), null); r = new RouteInfo((IpPrefix) null, Address("2001:db8::1"), null); assertEquals(Prefix("::/0"), r.getDestination()); assertEquals(Address("2001:db8::1"), r.getGateway()); assertNull(r.getInterface()); r = new RouteInfo((LinkAddress) null, Address("192.0.2.1"), "wlan0"); r = new RouteInfo((IpPrefix) null, Address("192.0.2.1"), "wlan0"); assertEquals(Prefix("0.0.0.0/0"), r.getDestination()); assertEquals(Address("192.0.2.1"), r.getGateway()); assertEquals("wlan0", r.getInterface()); Loading @@ -74,7 +73,7 @@ public class RouteInfoTest extends TestCase { class PatchedRouteInfo { private final RouteInfo mRouteInfo; public PatchedRouteInfo(LinkAddress destination, InetAddress gateway, String iface) { public PatchedRouteInfo(IpPrefix destination, InetAddress gateway, String iface) { mRouteInfo = new RouteInfo(destination, gateway, iface); } Loading