diff options
4 files changed, 41 insertions, 16 deletions
diff --git a/common/captiveportal/src/android/net/captiveportal/CaptivePortalProbeResult.java b/common/captiveportal/src/android/net/captiveportal/CaptivePortalProbeResult.java index deee91a..ff743c8 100755 --- a/common/captiveportal/src/android/net/captiveportal/CaptivePortalProbeResult.java +++ b/common/captiveportal/src/android/net/captiveportal/CaptivePortalProbeResult.java @@ -36,10 +36,11 @@ public final class CaptivePortalProbeResult { */ public static final int PARTIAL_CODE = -1; - // DNS response with private IP on the probe URL means the network, especially Wi-Fi network is - // not connected to the Internet. This code represents the result of a single probe, for correct - // logging of the probe results. The result of the whole evaluation would typically be FAILED if - // one of the probes returns this status. + // DNS response with private IP on the probe URL suggests that the network, especially Wi-Fi + // network is not connected to the Internet. This code represents the result of a single probe, + // for correct logging of the probe results. The result of the whole evaluation would typically + // be FAILED if one of the probes returns this status. + // This logic is only used if the config_force_dns_probe_private_ip_no_internet flag is set. public static final int DNS_PRIVATE_IP_RESPONSE_CODE = -2; @NonNull diff --git a/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java index 2de18de..6fd6043 100755 --- a/src/android/net/util/NetworkStackUtils.java +++ b/src/android/net/util/NetworkStackUtils.java @@ -385,4 +385,12 @@ public class NetworkStackUtils { (address instanceof Inet6Address) ? "[%s]:%d" : "%s:%d", address.getHostAddress(), port); } + + /** + * Return true if the provided address is non-null and an IPv6 Unique Local Address (RFC4193). + */ + public static boolean isIPv6ULA(@Nullable InetAddress addr) { + return addr instanceof Inet6Address + && ((addr.getAddress()[0] & 0xfe) == 0xfc); + } } diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java index 4aba4f9..eed63e6 100755 --- a/src/com/android/server/connectivity/NetworkMonitor.java +++ b/src/com/android/server/connectivity/NetworkMonitor.java @@ -72,6 +72,7 @@ import static android.net.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_HTTP_URL import static android.net.util.NetworkStackUtils.DISMISS_PORTAL_IN_VALIDATED_NETWORK; import static android.net.util.NetworkStackUtils.DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION; import static android.net.util.NetworkStackUtils.isEmpty; +import static android.net.util.NetworkStackUtils.isIPv6ULA; import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; import static com.android.networkstack.apishim.ConstantsShim.DETECTION_METHOD_DNS_EVENTS; @@ -427,7 +428,7 @@ public class NetworkMonitor extends StateMachine { private boolean mAcceptPartialConnectivity = false; private final EvaluationState mEvaluationState = new EvaluationState(); - private final boolean mPrivateIpNotPortalEnabled; + private final boolean mPrivateIpNoInternetEnabled; private int getCallbackVersion(INetworkMonitorCallbacks cb) { int version; @@ -490,7 +491,7 @@ public class NetworkMonitor extends StateMachine { // CHECKSTYLE:ON IndentationCheck mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled(); - mPrivateIpNotPortalEnabled = getIsPrivateIpNotPortalEnabled(); + mPrivateIpNoInternetEnabled = getIsPrivateIpNoInternetEnabled(); mUseHttps = getUseHttpsValidation(); mCaptivePortalUserAgent = getCaptivePortalUserAgent(); mCaptivePortalHttpsUrls = makeCaptivePortalHttpsUrls(); @@ -1441,7 +1442,7 @@ public class NetworkMonitor extends StateMachine { return mode != CAPTIVE_PORTAL_MODE_IGNORE; } - private boolean getIsPrivateIpNotPortalEnabled() { + private boolean getIsPrivateIpNoInternetEnabled() { return mDependencies.isFeatureEnabled(mContext, DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION) || mContext.getResources().getBoolean( R.bool.config_force_dns_probe_private_ip_no_internet); @@ -1919,9 +1920,9 @@ public class NetworkMonitor extends StateMachine { // information to callers that does not make sense because the state machine has already // changed state. final InetAddress[] resolvedAddr = sendDnsProbe(host); - // The private IP logic only applies to the HTTP probe, not the HTTPS probe (which would - // fail anyway) or the PAC probe. - if (mPrivateIpNotPortalEnabled && probeType == ValidationProbeEvent.PROBE_HTTP + // The private IP logic only applies to captive portal detection (the HTTP probe), not + // network validation (the HTTPS probe, which would likely fail anyway) or the PAC probe. + if (mPrivateIpNoInternetEnabled && probeType == ValidationProbeEvent.PROBE_HTTP && (proxy == null) && hasPrivateIpAddress(resolvedAddr)) { return CaptivePortalProbeResult.PRIVATE_IP; } @@ -1959,8 +1960,7 @@ public class NetworkMonitor extends StateMachine { } /** - * Check if any of the provided IP addresses include a private IP, as defined by - * {@link com.android.server.util.NetworkStackConstants#PRIVATE_IPV4_RANGES}. + * Check if any of the provided IP addresses include a private IP. * @return true if an IP address is private. */ private static boolean hasPrivateIpAddress(@Nullable InetAddress[] addresses) { @@ -1968,7 +1968,8 @@ public class NetworkMonitor extends StateMachine { return false; } for (InetAddress address : addresses) { - if (address.isLinkLocalAddress() || address.isSiteLocalAddress()) { + if (address.isLinkLocalAddress() || address.isSiteLocalAddress() + || isIPv6ULA(address)) { return true; } } @@ -2343,10 +2344,11 @@ public class NetworkMonitor extends StateMachine { } // Consider a DNS response with a private IP address on the HTTP probe as an indication that // the network is not connected to the Internet, and have the whole evaluation fail in that - // case. + // case, instead of potentially detecting a captive portal. This logic only affects portal + // detection, not network validation. // This only applies if the DNS probe completed within PROBE_TIMEOUT_MS, as the fallback // probe should not be delayed by this check. - if (mPrivateIpNotPortalEnabled && (httpResult.isDnsPrivateIpResponse())) { + if (mPrivateIpNoInternetEnabled && (httpResult.isDnsPrivateIpResponse())) { validationLog("DNS response to the URL is private IP"); return CaptivePortalProbeResult.FAILED; } diff --git a/tests/unit/src/android/net/util/NetworkStackUtilsTest.java b/tests/unit/src/android/net/util/NetworkStackUtilsTest.java index 9dd91ab..80b4272 100644 --- a/tests/unit/src/android/net/util/NetworkStackUtilsTest.java +++ b/tests/unit/src/android/net/util/NetworkStackUtilsTest.java @@ -16,6 +16,9 @@ package android.net.util; +import static android.net.InetAddresses.parseNumericAddress; +import static android.net.util.NetworkStackUtils.isIPv6ULA; + import static com.android.dx.mockito.inline.extended.ExtendedMockito.doReturn; import static com.android.dx.mockito.inline.extended.ExtendedMockito.mockitoSession; @@ -185,4 +188,15 @@ public class NetworkStackUtilsTest { assertFalse(NetworkStackUtils.isFeatureEnabled(mContext, TEST_NAME_SPACE, TEST_EXPERIMENT_FLAG)); } -} + + @Test + public void testIsIPv6ULA() { + assertTrue(isIPv6ULA(parseNumericAddress("fc00::"))); + assertTrue(isIPv6ULA(parseNumericAddress("fc00::1"))); + assertTrue(isIPv6ULA(parseNumericAddress("fc00:1234::5678"))); + assertTrue(isIPv6ULA(parseNumericAddress("fdff:ffff:ffff:ffff:ffff:ffff:ffff:ffff"))); + + assertFalse(isIPv6ULA(parseNumericAddress("fe00::"))); + assertFalse(isIPv6ULA(parseNumericAddress("2480:1248::123:456"))); + } +}
\ No newline at end of file |