Loading AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ android:persistent="true" android:process="com.android.networkstack.process"> <service android:name="com.android.server.NetworkStackService" android:exported="true" android:permission="android.permission.MAINLINE_NETWORK_STACK"> <intent-filter> <action android:name="android.net.INetworkStackConnector"/> Loading AndroidManifest_InProcess.xml +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ <application> <service android:name="com.android.server.NetworkStackService" android:process="system" android:exported="true" android:permission="android.permission.MAINLINE_NETWORK_STACK"> <intent-filter> <action android:name="android.net.INetworkStackConnector.InProcess"/> Loading src/android/net/ip/IpClient.java +17 −3 Original line number Diff line number Diff line Loading @@ -64,7 +64,6 @@ import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; import android.util.Pair; import android.util.Patterns; import android.util.SparseArray; import androidx.annotation.NonNull; Loading @@ -86,6 +85,8 @@ import com.android.server.NetworkStackService.NetworkStackServiceManager; import java.io.FileDescriptor; import java.io.PrintWriter; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; Loading Loading @@ -1255,9 +1256,9 @@ public class IpClient extends StateMachine { } final String capportUrl = mDhcpResults.captivePortalApiUrl; // Uri.parse does no syntax check; do a simple regex check to eliminate garbage. // Uri.parse does no syntax check; do a simple check to eliminate garbage. // If the URL is still incorrect data fetching will fail later, which is fine. if (capportUrl != null && Patterns.WEB_URL.matcher(capportUrl).matches()) { if (isParseableUrl(capportUrl)) { NetworkInformationShimImpl.newInstance() .setCaptivePortalApiUrl(newLp, Uri.parse(capportUrl)); } Loading Loading @@ -1295,6 +1296,19 @@ public class IpClient extends StateMachine { return newLp; } private static boolean isParseableUrl(String url) { // Verify that a URL has a reasonable format that can be parsed as per the URL constructor. // This does not use Patterns.WEB_URL as that pattern excludes URLs without TLDs, such as on // localhost. if (url == null) return false; try { new URL(url); return true; } catch (MalformedURLException e) { return false; } } private static void addAllReachableDnsServers( LinkProperties lp, Iterable<InetAddress> dnses) { // TODO: Investigate deleting this reachability check. We should be Loading src/com/android/server/connectivity/NetworkMonitor.java +6 −1 Original line number Diff line number Diff line Loading @@ -2535,7 +2535,12 @@ public class NetworkMonitor extends StateMachine { final String apiContent; try { final URL url = new URL(mCaptivePortalApiUrl.toString()); if (!"https".equals(url.getProtocol())) { // Protocol must be HTTPS // (as per https://www.ietf.org/id/draft-ietf-capport-api-07.txt, #4). // Only allow HTTP on localhost, for testing. final boolean isLocalhostHttp = "localhost".equals(url.getHost()) && "http".equals(url.getProtocol()); if (!"https".equals(url.getProtocol()) && !isLocalhostHttp) { validationLog("Invalid captive portal API protocol: " + url.getProtocol()); return null; } Loading tests/lib/multivariant/com/android/testutils/PacketFilter.kt 0 → 100644 +87 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 com.android.testutils import java.util.function.Predicate const val ETHER_TYPE_OFFSET = 12 const val ETHER_HEADER_LENGTH = 14 const val IPV4_PROTOCOL_OFFSET = ETHER_HEADER_LENGTH + 9 const val IPV4_CHECKSUM_OFFSET = ETHER_HEADER_LENGTH + 10 const val IPV4_HEADER_LENGTH = 20 const val IPV4_UDP_OFFSET = ETHER_HEADER_LENGTH + IPV4_HEADER_LENGTH const val BOOTP_OFFSET = IPV4_UDP_OFFSET + 8 const val BOOTP_TID_OFFSET = BOOTP_OFFSET + 4 const val BOOTP_CLIENT_MAC_OFFSET = BOOTP_OFFSET + 28 const val DHCP_OPTIONS_OFFSET = BOOTP_OFFSET + 240 /** * A [Predicate] that matches a [ByteArray] if it contains the specified [bytes] at the specified * [offset]. */ class OffsetFilter(val offset: Int, vararg val bytes: Byte) : Predicate<ByteArray> { override fun test(packet: ByteArray) = bytes.withIndex().all { it.value == packet[offset + it.index] } } /** * A [Predicate] that matches ethernet-encapped packets that contain an UDP over IPv4 datagram. */ class IPv4UdpFilter : Predicate<ByteArray> { private val impl = OffsetFilter(ETHER_TYPE_OFFSET, 0x08, 0x00 /* IPv4 */).and( OffsetFilter(IPV4_PROTOCOL_OFFSET, 17 /* UDP */)) override fun test(t: ByteArray) = impl.test(t) } /** * A [Predicate] that matches ethernet-encapped DHCP packets sent from a DHCP client. */ class DhcpClientPacketFilter : Predicate<ByteArray> { private val impl = IPv4UdpFilter() .and(OffsetFilter(IPV4_UDP_OFFSET /* source port */, 0x00, 0x44 /* 68 */)) .and(OffsetFilter(IPV4_UDP_OFFSET + 2 /* dest port */, 0x00, 0x43 /* 67 */)) override fun test(t: ByteArray) = impl.test(t) } /** * A [Predicate] that matches a [ByteArray] if it contains a ethernet-encapped DHCP packet that * contains the specified option with the specified [bytes] as value. */ class DhcpOptionFilter(val option: Byte, vararg val bytes: Byte) : Predicate<ByteArray> { override fun test(packet: ByteArray): Boolean { val option = findDhcpOption(packet, option) ?: return false return option.contentEquals(bytes) } } /** * Find a DHCP option in a packet and return its value, if found. */ fun findDhcpOption(packet: ByteArray, option: Byte): ByteArray? = findOptionOffset(packet, option, DHCP_OPTIONS_OFFSET)?.let { val optionLen = packet[it + 1] return packet.copyOfRange(it + 2 /* type, length bytes */, it + 2 + optionLen) } private tailrec fun findOptionOffset(packet: ByteArray, option: Byte, searchOffset: Int): Int? { if (packet.size <= searchOffset + 2 /* type, length bytes */) return null return if (packet[searchOffset] == option) searchOffset else { val optionLen = packet[searchOffset + 1] findOptionOffset(packet, option, searchOffset + 2 + optionLen) } } Loading
AndroidManifest.xml +1 −0 Original line number Diff line number Diff line Loading @@ -44,6 +44,7 @@ android:persistent="true" android:process="com.android.networkstack.process"> <service android:name="com.android.server.NetworkStackService" android:exported="true" android:permission="android.permission.MAINLINE_NETWORK_STACK"> <intent-filter> <action android:name="android.net.INetworkStackConnector"/> Loading
AndroidManifest_InProcess.xml +1 −0 Original line number Diff line number Diff line Loading @@ -23,6 +23,7 @@ <application> <service android:name="com.android.server.NetworkStackService" android:process="system" android:exported="true" android:permission="android.permission.MAINLINE_NETWORK_STACK"> <intent-filter> <action android:name="android.net.INetworkStackConnector.InProcess"/> Loading
src/android/net/ip/IpClient.java +17 −3 Original line number Diff line number Diff line Loading @@ -64,7 +64,6 @@ import android.text.TextUtils; import android.util.LocalLog; import android.util.Log; import android.util.Pair; import android.util.Patterns; import android.util.SparseArray; import androidx.annotation.NonNull; Loading @@ -86,6 +85,8 @@ import com.android.server.NetworkStackService.NetworkStackServiceManager; import java.io.FileDescriptor; import java.io.PrintWriter; import java.net.InetAddress; import java.net.MalformedURLException; import java.net.URL; import java.nio.BufferUnderflowException; import java.nio.ByteBuffer; import java.util.ArrayList; Loading Loading @@ -1255,9 +1256,9 @@ public class IpClient extends StateMachine { } final String capportUrl = mDhcpResults.captivePortalApiUrl; // Uri.parse does no syntax check; do a simple regex check to eliminate garbage. // Uri.parse does no syntax check; do a simple check to eliminate garbage. // If the URL is still incorrect data fetching will fail later, which is fine. if (capportUrl != null && Patterns.WEB_URL.matcher(capportUrl).matches()) { if (isParseableUrl(capportUrl)) { NetworkInformationShimImpl.newInstance() .setCaptivePortalApiUrl(newLp, Uri.parse(capportUrl)); } Loading Loading @@ -1295,6 +1296,19 @@ public class IpClient extends StateMachine { return newLp; } private static boolean isParseableUrl(String url) { // Verify that a URL has a reasonable format that can be parsed as per the URL constructor. // This does not use Patterns.WEB_URL as that pattern excludes URLs without TLDs, such as on // localhost. if (url == null) return false; try { new URL(url); return true; } catch (MalformedURLException e) { return false; } } private static void addAllReachableDnsServers( LinkProperties lp, Iterable<InetAddress> dnses) { // TODO: Investigate deleting this reachability check. We should be Loading
src/com/android/server/connectivity/NetworkMonitor.java +6 −1 Original line number Diff line number Diff line Loading @@ -2535,7 +2535,12 @@ public class NetworkMonitor extends StateMachine { final String apiContent; try { final URL url = new URL(mCaptivePortalApiUrl.toString()); if (!"https".equals(url.getProtocol())) { // Protocol must be HTTPS // (as per https://www.ietf.org/id/draft-ietf-capport-api-07.txt, #4). // Only allow HTTP on localhost, for testing. final boolean isLocalhostHttp = "localhost".equals(url.getHost()) && "http".equals(url.getProtocol()); if (!"https".equals(url.getProtocol()) && !isLocalhostHttp) { validationLog("Invalid captive portal API protocol: " + url.getProtocol()); return null; } Loading
tests/lib/multivariant/com/android/testutils/PacketFilter.kt 0 → 100644 +87 −0 Original line number Diff line number Diff line /* * Copyright (C) 2020 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 com.android.testutils import java.util.function.Predicate const val ETHER_TYPE_OFFSET = 12 const val ETHER_HEADER_LENGTH = 14 const val IPV4_PROTOCOL_OFFSET = ETHER_HEADER_LENGTH + 9 const val IPV4_CHECKSUM_OFFSET = ETHER_HEADER_LENGTH + 10 const val IPV4_HEADER_LENGTH = 20 const val IPV4_UDP_OFFSET = ETHER_HEADER_LENGTH + IPV4_HEADER_LENGTH const val BOOTP_OFFSET = IPV4_UDP_OFFSET + 8 const val BOOTP_TID_OFFSET = BOOTP_OFFSET + 4 const val BOOTP_CLIENT_MAC_OFFSET = BOOTP_OFFSET + 28 const val DHCP_OPTIONS_OFFSET = BOOTP_OFFSET + 240 /** * A [Predicate] that matches a [ByteArray] if it contains the specified [bytes] at the specified * [offset]. */ class OffsetFilter(val offset: Int, vararg val bytes: Byte) : Predicate<ByteArray> { override fun test(packet: ByteArray) = bytes.withIndex().all { it.value == packet[offset + it.index] } } /** * A [Predicate] that matches ethernet-encapped packets that contain an UDP over IPv4 datagram. */ class IPv4UdpFilter : Predicate<ByteArray> { private val impl = OffsetFilter(ETHER_TYPE_OFFSET, 0x08, 0x00 /* IPv4 */).and( OffsetFilter(IPV4_PROTOCOL_OFFSET, 17 /* UDP */)) override fun test(t: ByteArray) = impl.test(t) } /** * A [Predicate] that matches ethernet-encapped DHCP packets sent from a DHCP client. */ class DhcpClientPacketFilter : Predicate<ByteArray> { private val impl = IPv4UdpFilter() .and(OffsetFilter(IPV4_UDP_OFFSET /* source port */, 0x00, 0x44 /* 68 */)) .and(OffsetFilter(IPV4_UDP_OFFSET + 2 /* dest port */, 0x00, 0x43 /* 67 */)) override fun test(t: ByteArray) = impl.test(t) } /** * A [Predicate] that matches a [ByteArray] if it contains a ethernet-encapped DHCP packet that * contains the specified option with the specified [bytes] as value. */ class DhcpOptionFilter(val option: Byte, vararg val bytes: Byte) : Predicate<ByteArray> { override fun test(packet: ByteArray): Boolean { val option = findDhcpOption(packet, option) ?: return false return option.contentEquals(bytes) } } /** * Find a DHCP option in a packet and return its value, if found. */ fun findDhcpOption(packet: ByteArray, option: Byte): ByteArray? = findOptionOffset(packet, option, DHCP_OPTIONS_OFFSET)?.let { val optionLen = packet[it + 1] return packet.copyOfRange(it + 2 /* type, length bytes */, it + 2 + optionLen) } private tailrec fun findOptionOffset(packet: ByteArray, option: Byte, searchOffset: Int): Int? { if (packet.size <= searchOffset + 2 /* type, length bytes */) return null return if (packet[searchOffset] == option) searchOffset else { val optionLen = packet[searchOffset + 1] findOptionOffset(packet, option, searchOffset + 2 + optionLen) } }