diff options
author | Remi NGUYEN VAN <reminv@google.com> | 2020-04-09 06:41:16 +0000 |
---|---|---|
committer | Remi NGUYEN VAN <reminv@google.com> | 2020-04-10 05:54:12 +0000 |
commit | 75e9d9014f3c76135068eef2b12401774a9f98e3 (patch) | |
tree | 941cd31ced80fff03a8e294d6b36cb02015b94a4 /src | |
parent | a8782392d4950627a141da6451c25eeb161fd26c (diff) |
Do not detect portals when DNS returns private IPs
When access points return private IPs (as defined in the NetworkMonitor
constant) in response to DNS probes, do not consider the access point as
behind a portal, but instead indicate that it has no connectivity.
This solves issues with some access points that return private IP
responses to DNS queries when they do not have internet access.
This feature is turned off by default while investigating its impact.
OEMs can force-enable it through a resource overlay:
config_force_dns_probe_private_ip_not_portal. Metrics to evaluate the
feature will be added in a later change.
Bug: 136734947
Test: atest NetworkStackTests
Merged-In: I51975e18f424e3b7265011000f073777f376e597
Change-Id: I51975e18f424e3b7265011000f073777f376e597
Diffstat (limited to 'src')
-rwxr-xr-x[-rw-r--r--] | src/android/net/util/NetworkStackUtils.java | 9 | ||||
-rwxr-xr-x[-rw-r--r--] | src/com/android/server/connectivity/NetworkMonitor.java | 65 |
2 files changed, 68 insertions, 6 deletions
diff --git a/src/android/net/util/NetworkStackUtils.java b/src/android/net/util/NetworkStackUtils.java index bc41549..2de18de 100644..100755 --- a/src/android/net/util/NetworkStackUtils.java +++ b/src/android/net/util/NetworkStackUtils.java @@ -172,6 +172,15 @@ public class NetworkStackUtils { public static final String DISMISS_PORTAL_IN_VALIDATED_NETWORK = "dismiss_portal_in_validated_network"; + /** + * Experiment flag to enable considering DNS probes returning private IP addresses as failed + * when attempting to detect captive portals. + * + * This flag is enabled if !=0 and less than the module APK version. + */ + public static final String DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION = + "dns_probe_private_ip_no_internet"; + static { System.loadLibrary("networkstackutilsjni"); } diff --git a/src/com/android/server/connectivity/NetworkMonitor.java b/src/com/android/server/connectivity/NetworkMonitor.java index 7ea61fa..0540258 100644..100755 --- a/src/com/android/server/connectivity/NetworkMonitor.java +++ b/src/com/android/server/connectivity/NetworkMonitor.java @@ -70,6 +70,7 @@ import static android.net.util.NetworkStackUtils.CAPTIVE_PORTAL_USE_HTTPS; import static android.net.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_HTTPS_URLS; import static android.net.util.NetworkStackUtils.DEFAULT_CAPTIVE_PORTAL_HTTP_URLS; 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.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; @@ -423,6 +424,8 @@ public class NetworkMonitor extends StateMachine { private boolean mAcceptPartialConnectivity = false; private final EvaluationState mEvaluationState = new EvaluationState(); + private final boolean mPrivateIpNotPortalEnabled; + private int getCallbackVersion(INetworkMonitorCallbacks cb) { int version; try { @@ -484,6 +487,7 @@ public class NetworkMonitor extends StateMachine { // CHECKSTYLE:ON IndentationCheck mIsCaptivePortalCheckEnabled = getIsCaptivePortalCheckEnabled(); + mPrivateIpNotPortalEnabled = getIsPrivateIpNotPortalEnabled(); mUseHttps = getUseHttpsValidation(); mCaptivePortalUserAgent = getCaptivePortalUserAgent(); mCaptivePortalHttpsUrls = makeCaptivePortalHttpsUrls(); @@ -1434,6 +1438,12 @@ public class NetworkMonitor extends StateMachine { return mode != CAPTIVE_PORTAL_MODE_IGNORE; } + private boolean getIsPrivateIpNotPortalEnabled() { + return mDependencies.isFeatureEnabled(mContext, DNS_PROBE_PRIVATE_IP_NO_INTERNET_VERSION) + || mContext.getResources().getBoolean( + R.bool.config_force_dns_probe_private_ip_no_internet); + } + private boolean getUseHttpsValidation() { return mDependencies.getDeviceConfigPropertyInt(NAMESPACE_CONNECTIVITY, CAPTIVE_PORTAL_USE_HTTPS, 1) == 1; @@ -1877,7 +1887,13 @@ public class NetworkMonitor extends StateMachine { // state machine thread. Reporting results here would cause races and potentially send // information to callers that does not make sense because the state machine has already // changed state. - sendDnsProbe(host); + 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 + && (proxy == null) && hasPrivateIpAddress(resolvedAddr)) { + return CaptivePortalProbeResult.PRIVATE_IP; + } return sendHttpProbe(url, probeType, null); } @@ -1891,23 +1907,41 @@ public class NetworkMonitor extends StateMachine { } /** Do a DNS resolution of the given server. */ - private void sendDnsProbe(String host) { + private InetAddress[] sendDnsProbe(String host) { if (TextUtils.isEmpty(host)) { - return; + return null; } - final String name = ValidationProbeEvent.getProbeName(ValidationProbeEvent.PROBE_DNS); final Stopwatch watch = new Stopwatch().start(); int result; - String connectInfo; + InetAddress[] addresses; try { - InetAddress[] addresses = sendDnsProbeWithTimeout(host, getDnsProbeTimeout()); + addresses = sendDnsProbeWithTimeout(host, getDnsProbeTimeout()); result = ValidationProbeEvent.DNS_SUCCESS; } catch (UnknownHostException e) { + addresses = null; result = ValidationProbeEvent.DNS_FAILURE; } final long latency = watch.stop(); logValidationProbe(latency, ValidationProbeEvent.PROBE_DNS, result); + return addresses; + } + + /** + * Check if any of the provided IP addresses include a private IP, as defined by + * {@link com.android.server.util.NetworkStackConstants#PRIVATE_IPV4_RANGES}. + * @return true if an IP address is private. + */ + private static boolean hasPrivateIpAddress(@Nullable InetAddress[] addresses) { + if (addresses == null) { + return false; + } + for (InetAddress address : addresses) { + if (address.isLinkLocalAddress() || address.isSiteLocalAddress()) { + return true; + } + } + return false; } /** @@ -2230,6 +2264,15 @@ public class NetworkMonitor extends StateMachine { reportHttpProbeResult(NETWORK_VALIDATION_PROBE_HTTPS, httpsResult); return httpsResult; } + // 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. + // 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())) { + validationLog("DNS response to the URL is private IP"); + return CaptivePortalProbeResult.FAILED; + } // If a fallback method exists, use it to retry portal detection. // If we have new-style probe specs, use those. Otherwise, use the fallback URLs. final CaptivePortalProbeSpec probeSpec = nextFallbackSpec(); @@ -2449,6 +2492,16 @@ public class NetworkMonitor extends StateMachine { } /** + * Check whether or not one experimental feature in the connectivity namespace is + * enabled. + * @param name Flag name of the experiment in the connectivity namespace. + * @see NetworkStackUtils#isFeatureEnabled(Context, String, String) + */ + public boolean isFeatureEnabled(@NonNull Context context, @NonNull String name) { + return NetworkStackUtils.isFeatureEnabled(context, NAMESPACE_CONNECTIVITY, name); + } + + /** * Send a broadcast indicating network conditions. */ public void sendNetworkConditionsBroadcast(@NonNull Context context, |