1 /* 2 * Copyright (C) 2019 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 17 package android.net.cts; 18 19 import static android.net.DnsResolver.CLASS_IN; 20 import static android.net.DnsResolver.FLAG_EMPTY; 21 import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP; 22 import static android.net.DnsResolver.TYPE_A; 23 import static android.net.DnsResolver.TYPE_AAAA; 24 import static android.net.NetworkCapabilities.TRANSPORT_CELLULAR; 25 import static android.net.cts.util.CtsNetUtils.TestNetworkCallback; 26 import static android.provider.DeviceConfig.NAMESPACE_CONNECTIVITY; 27 import static android.system.OsConstants.ETIMEDOUT; 28 29 import static com.android.testutils.DevSdkIgnoreRuleKt.SC_V2; 30 31 import static org.junit.Assert.assertEquals; 32 import static org.junit.Assert.assertNotNull; 33 import static org.junit.Assert.assertNull; 34 import static org.junit.Assert.assertTrue; 35 import static org.junit.Assert.fail; 36 37 import android.annotation.NonNull; 38 import android.annotation.Nullable; 39 import android.content.ContentResolver; 40 import android.content.Context; 41 import android.content.pm.PackageManager; 42 import android.net.ConnectivityManager; 43 import android.net.DnsResolver; 44 import android.net.Network; 45 import android.net.NetworkCapabilities; 46 import android.net.NetworkRequest; 47 import android.net.ParseException; 48 import android.net.cts.util.CtsNetUtils; 49 import android.os.CancellationSignal; 50 import android.os.Handler; 51 import android.os.Looper; 52 import android.platform.test.annotations.AppModeFull; 53 import android.provider.Settings; 54 import android.system.ErrnoException; 55 import android.util.Log; 56 57 import androidx.test.InstrumentationRegistry; 58 import androidx.test.runner.AndroidJUnit4; 59 60 import com.android.net.module.util.DnsPacket; 61 import com.android.testutils.ConnectivityDiagnosticsCollector; 62 import com.android.testutils.DevSdkIgnoreRule; 63 import com.android.testutils.DevSdkIgnoreRule.IgnoreUpTo; 64 import com.android.testutils.DeviceConfigRule; 65 import com.android.testutils.DnsResolverModuleTest; 66 import com.android.testutils.SkipPresubmit; 67 68 import org.junit.After; 69 import org.junit.Before; 70 import org.junit.BeforeClass; 71 import org.junit.ClassRule; 72 import org.junit.Rule; 73 import org.junit.Test; 74 import org.junit.runner.RunWith; 75 76 import java.net.Inet4Address; 77 import java.net.Inet6Address; 78 import java.net.InetAddress; 79 import java.util.ArrayList; 80 import java.util.List; 81 import java.util.concurrent.CountDownLatch; 82 import java.util.concurrent.Executor; 83 import java.util.concurrent.TimeUnit; 84 85 @AppModeFull(reason = "WRITE_SECURE_SETTINGS permission can't be granted to instant apps") 86 @RunWith(AndroidJUnit4.class) 87 public class DnsResolverTest { 88 @ClassRule 89 public static final DeviceConfigRule DEVICE_CONFIG_CLASS_RULE = new DeviceConfigRule(); 90 @Rule 91 public final DevSdkIgnoreRule ignoreRule = new DevSdkIgnoreRule(); 92 93 private static final String TAG = "DnsResolverTest"; 94 private static final char[] HEX_CHARS = { 95 '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' 96 }; 97 98 static final String TEST_DOMAIN = "www.google.com"; 99 static final String TEST_NX_DOMAIN = "test1-nx.metric.gstatic.com"; 100 static final String INVALID_PRIVATE_DNS_SERVER = "invalid.google"; 101 static final String GOOGLE_PRIVATE_DNS_SERVER = "dns.google"; 102 static final byte[] TEST_BLOB = new byte[]{ 103 /* Header */ 104 0x55, 0x66, /* Transaction ID */ 105 0x01, 0x00, /* Flags */ 106 0x00, 0x01, /* Questions */ 107 0x00, 0x00, /* Answer RRs */ 108 0x00, 0x00, /* Authority RRs */ 109 0x00, 0x00, /* Additional RRs */ 110 /* Queries */ 111 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65, 112 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */ 113 0x00, 0x01, /* Type */ 114 0x00, 0x01 /* Class */ 115 }; 116 static final int TIMEOUT_MS = 12_000; 117 static final int CANCEL_TIMEOUT_MS = 3_000; 118 static final int CANCEL_RETRY_TIMES = 5; 119 static final int QUERY_TIMES = 10; 120 static final int NXDOMAIN = 3; 121 122 private Context mContext; 123 private ContentResolver mCR; 124 private ConnectivityManager mCM; 125 private PackageManager mPackageManager; 126 private CtsNetUtils mCtsNetUtils; 127 private Executor mExecutor; 128 private Executor mExecutorInline; 129 private DnsResolver mDns; 130 131 private TestNetworkCallback mWifiRequestCallback = null; 132 133 /** 134 * @see BeforeClass 135 */ 136 @BeforeClass beforeClass()137 public static void beforeClass() throws Exception { 138 // Use async private DNS resolution to avoid flakes due to races applying the setting 139 DEVICE_CONFIG_CLASS_RULE.setConfig(NAMESPACE_CONNECTIVITY, 140 "networkmonitor_async_privdns_resolution", "1"); 141 // Make sure NetworkMonitor is restarted before and after the test so the flag is applied 142 // and cleaned up. 143 maybeToggleWifiAndCell(); 144 DEVICE_CONFIG_CLASS_RULE.runAfterNextCleanup(DnsResolverTest::maybeToggleWifiAndCell); 145 } 146 147 @Before setUp()148 public void setUp() throws Exception { 149 mContext = InstrumentationRegistry.getContext(); 150 mCM = mContext.getSystemService(ConnectivityManager.class); 151 mDns = DnsResolver.getInstance(); 152 mExecutor = new Handler(Looper.getMainLooper())::post; 153 mExecutorInline = (Runnable r) -> r.run(); 154 mCR = mContext.getContentResolver(); 155 mCtsNetUtils = new CtsNetUtils(mContext); 156 mCtsNetUtils.storePrivateDnsSetting(); 157 mPackageManager = mContext.getPackageManager(); 158 } 159 160 @After tearDown()161 public void tearDown() throws Exception { 162 mCtsNetUtils.restorePrivateDnsSetting(); 163 if (mWifiRequestCallback != null) { 164 mCM.unregisterNetworkCallback(mWifiRequestCallback); 165 } 166 } 167 maybeToggleWifiAndCell()168 private static void maybeToggleWifiAndCell() throws Exception { 169 final CtsNetUtils utils = new CtsNetUtils(InstrumentationRegistry.getContext()); 170 utils.reconnectWifiIfSupported(); 171 utils.reconnectCellIfSupported(); 172 } 173 byteArrayToHexString(byte[] bytes)174 private static String byteArrayToHexString(byte[] bytes) { 175 char[] hexChars = new char[bytes.length * 2]; 176 for (int i = 0; i < bytes.length; ++i) { 177 int b = bytes[i] & 0xFF; 178 hexChars[i * 2] = HEX_CHARS[b >>> 4]; 179 hexChars[i * 2 + 1] = HEX_CHARS[b & 0x0F]; 180 } 181 return new String(hexChars); 182 } 183 getTestableNetworks()184 private Network[] getTestableNetworks() { 185 if (mPackageManager.hasSystemFeature(PackageManager.FEATURE_WIFI)) { 186 // File a NetworkRequest for Wi-Fi, so it connects even if a higher-scoring 187 // network, such as Ethernet, is already connected. 188 final NetworkRequest request = new NetworkRequest.Builder() 189 .addTransportType(NetworkCapabilities.TRANSPORT_WIFI) 190 .addCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) 191 .build(); 192 mWifiRequestCallback = new TestNetworkCallback(); 193 mCM.requestNetwork(request, mWifiRequestCallback); 194 mCtsNetUtils.ensureWifiConnected(); 195 } 196 final ArrayList<Network> testableNetworks = new ArrayList<Network>(); 197 for (Network network : mCM.getAllNetworks()) { 198 final NetworkCapabilities nc = mCM.getNetworkCapabilities(network); 199 if (nc != null 200 && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_NOT_RESTRICTED) 201 && nc.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET)) { 202 testableNetworks.add(network); 203 } 204 } 205 206 assertTrue( 207 "This test requires that at least one network be connected. " + 208 "Please ensure that the device is connected to a network.", 209 testableNetworks.size() >= 1); 210 // In order to test query with null network, add null as an element. 211 // Test cases which query with null network will go on default network. 212 testableNetworks.add(null); 213 return testableNetworks.toArray(new Network[0]); 214 } 215 assertGreaterThan(String msg, int first, int second)216 static private void assertGreaterThan(String msg, int first, int second) { 217 assertTrue(msg + " Excepted " + first + " to be greater than " + second, first > second); 218 } 219 220 private static class DnsParseException extends Exception { DnsParseException(String msg)221 public DnsParseException(String msg) { 222 super(msg); 223 } 224 } 225 226 private static class DnsAnswer extends DnsPacket { DnsAnswer(@onNull byte[] data)227 DnsAnswer(@NonNull byte[] data) throws DnsParseException { 228 super(data); 229 230 // Check QR field.(query (0), or a response (1)). 231 if ((mHeader.getFlags() & (1 << 15)) == 0) { 232 throw new DnsParseException("Not an answer packet"); 233 } 234 } 235 getRcode()236 int getRcode() { 237 return mHeader.getFlags() & 0x0F; 238 } 239 getANCount()240 int getANCount() { 241 return mHeader.getRecordCount(ANSECTION); 242 } 243 getQDCount()244 int getQDCount() { 245 return mHeader.getRecordCount(QDSECTION); 246 } 247 } 248 249 /** 250 * A query callback that ensures that the query is cancelled and that onAnswer is never 251 * called. If the query succeeds before it is cancelled, needRetry will return true so the 252 * test can retry. 253 */ 254 class VerifyCancelCallback implements DnsResolver.Callback<byte[]> { 255 private final CountDownLatch mLatch = new CountDownLatch(1); 256 private final String mMsg; 257 private final CancellationSignal mCancelSignal; 258 private int mRcode; 259 private DnsAnswer mDnsAnswer; 260 private String mErrorMsg = null; 261 VerifyCancelCallback(@onNull String msg, @Nullable CancellationSignal cancel)262 VerifyCancelCallback(@NonNull String msg, @Nullable CancellationSignal cancel) { 263 mMsg = msg; 264 mCancelSignal = cancel; 265 } 266 VerifyCancelCallback(@onNull String msg)267 VerifyCancelCallback(@NonNull String msg) { 268 this(msg, null); 269 } 270 waitForAnswer(int timeout)271 public boolean waitForAnswer(int timeout) throws InterruptedException { 272 return mLatch.await(timeout, TimeUnit.MILLISECONDS); 273 } 274 waitForAnswer()275 public boolean waitForAnswer() throws InterruptedException { 276 return waitForAnswer(TIMEOUT_MS); 277 } 278 needRetry()279 public boolean needRetry() throws InterruptedException { 280 return mLatch.await(CANCEL_TIMEOUT_MS, TimeUnit.MILLISECONDS); 281 } 282 283 @Override onAnswer(@onNull byte[] answer, int rcode)284 public void onAnswer(@NonNull byte[] answer, int rcode) { 285 if (mCancelSignal != null && mCancelSignal.isCanceled()) { 286 mErrorMsg = mMsg + " should not have returned any answers"; 287 mLatch.countDown(); 288 return; 289 } 290 291 mRcode = rcode; 292 try { 293 mDnsAnswer = new DnsAnswer(answer); 294 } catch (ParseException | DnsParseException e) { 295 mErrorMsg = mMsg + e.getMessage(); 296 mLatch.countDown(); 297 return; 298 } 299 Log.d(TAG, "Reported blob: " + byteArrayToHexString(answer)); 300 mLatch.countDown(); 301 } 302 303 @Override onError(@onNull DnsResolver.DnsException error)304 public void onError(@NonNull DnsResolver.DnsException error) { 305 mErrorMsg = mMsg + error.getMessage(); 306 mLatch.countDown(); 307 } 308 assertValidAnswer()309 private void assertValidAnswer() { 310 assertNull(mErrorMsg); 311 assertNotNull(mMsg + " No valid answer", mDnsAnswer); 312 assertEquals(mMsg + " Unexpected error: reported rcode" + mRcode + 313 " blob's rcode " + mDnsAnswer.getRcode(), mRcode, mDnsAnswer.getRcode()); 314 } 315 assertHasAnswer()316 public void assertHasAnswer() { 317 assertValidAnswer(); 318 // Check rcode field.(0, No error condition). 319 assertEquals(mMsg + " Response error, rcode: " + mRcode, mRcode, 0); 320 // Check answer counts. 321 assertGreaterThan(mMsg + " No answer found", mDnsAnswer.getANCount(), 0); 322 // Check question counts. 323 assertGreaterThan(mMsg + " No question found", mDnsAnswer.getQDCount(), 0); 324 } 325 assertNXDomain()326 public void assertNXDomain() { 327 assertValidAnswer(); 328 // Check rcode field.(3, NXDomain). 329 assertEquals(mMsg + " Unexpected rcode: " + mRcode, mRcode, NXDOMAIN); 330 // Check answer counts. Expect 0 answer. 331 assertEquals(mMsg + " Not an empty answer", mDnsAnswer.getANCount(), 0); 332 // Check question counts. 333 assertGreaterThan(mMsg + " No question found", mDnsAnswer.getQDCount(), 0); 334 } 335 assertEmptyAnswer()336 public void assertEmptyAnswer() { 337 assertValidAnswer(); 338 // Check rcode field.(0, No error condition). 339 assertEquals(mMsg + " Response error, rcode: " + mRcode, mRcode, 0); 340 // Check answer counts. Expect 0 answer. 341 assertEquals(mMsg + " Not an empty answer", mDnsAnswer.getANCount(), 0); 342 // Check question counts. 343 assertGreaterThan(mMsg + " No question found", mDnsAnswer.getQDCount(), 0); 344 } 345 } 346 347 @Test 348 @DnsResolverModuleTest testRawQuery()349 public void testRawQuery() throws Exception { 350 doTestRawQuery(mExecutor); 351 } 352 353 @Test 354 @DnsResolverModuleTest testRawQueryInline()355 public void testRawQueryInline() throws Exception { 356 doTestRawQuery(mExecutorInline); 357 } 358 359 @Test 360 @DnsResolverModuleTest testRawQueryBlob()361 public void testRawQueryBlob() throws Exception { 362 doTestRawQueryBlob(mExecutor); 363 } 364 365 @Test 366 @DnsResolverModuleTest testRawQueryBlobInline()367 public void testRawQueryBlobInline() throws Exception { 368 doTestRawQueryBlob(mExecutorInline); 369 } 370 371 @Test 372 @DnsResolverModuleTest testRawQueryRoot()373 public void testRawQueryRoot() throws Exception { 374 doTestRawQueryRoot(mExecutor); 375 } 376 377 @Test 378 @DnsResolverModuleTest testRawQueryRootInline()379 public void testRawQueryRootInline() throws Exception { 380 doTestRawQueryRoot(mExecutorInline); 381 } 382 383 @Test 384 @DnsResolverModuleTest testRawQueryNXDomain()385 public void testRawQueryNXDomain() throws Exception { 386 doTestRawQueryNXDomain(mExecutor); 387 } 388 389 @Test 390 @DnsResolverModuleTest testRawQueryNXDomainInline()391 public void testRawQueryNXDomainInline() throws Exception { 392 doTestRawQueryNXDomain(mExecutorInline); 393 } 394 395 @Test 396 @DnsResolverModuleTest testRawQueryNXDomainWithPrivateDns()397 public void testRawQueryNXDomainWithPrivateDns() throws Exception { 398 try { 399 doTestRawQueryNXDomainWithPrivateDns(mExecutor); 400 } catch (Throwable e) { 401 final ConnectivityDiagnosticsCollector collector = 402 ConnectivityDiagnosticsCollector.getInstance(); 403 if (collector != null) { 404 // IWLAN on U QPR3 release may cause failures in this test, see 405 // CarrierConfigSetupTest which is supposed to avoid the issue. Collect IWLAN 406 // related dumpsys if the test still fails. 407 collector.collectDumpsys("carrier_config", e); 408 collector.collectDumpsys("telecom", e); 409 collector.collectDumpsys("telephony_ims", e); 410 collector.collectDumpsys("telephony.registry", e); 411 } 412 throw e; 413 } 414 } 415 416 @Test 417 @DnsResolverModuleTest testRawQueryNXDomainInlineWithPrivateDns()418 public void testRawQueryNXDomainInlineWithPrivateDns() throws Exception { 419 doTestRawQueryNXDomainWithPrivateDns(mExecutorInline); 420 } 421 doTestRawQuery(Executor executor)422 public void doTestRawQuery(Executor executor) throws InterruptedException { 423 final String msg = "RawQuery " + TEST_DOMAIN; 424 for (Network network : getTestableNetworks()) { 425 final VerifyCancelCallback callback = new VerifyCancelCallback(msg); 426 mDns.rawQuery(network, TEST_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, 427 executor, null, callback); 428 429 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", 430 callback.waitForAnswer()); 431 callback.assertHasAnswer(); 432 } 433 } 434 doTestRawQueryBlob(Executor executor)435 public void doTestRawQueryBlob(Executor executor) throws InterruptedException { 436 final byte[] blob = new byte[]{ 437 /* Header */ 438 0x55, 0x66, /* Transaction ID */ 439 0x01, 0x00, /* Flags */ 440 0x00, 0x01, /* Questions */ 441 0x00, 0x00, /* Answer RRs */ 442 0x00, 0x00, /* Authority RRs */ 443 0x00, 0x00, /* Additional RRs */ 444 /* Queries */ 445 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65, 446 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */ 447 0x00, 0x01, /* Type */ 448 0x00, 0x01 /* Class */ 449 }; 450 final String msg = "RawQuery blob " + byteArrayToHexString(blob); 451 for (Network network : getTestableNetworks()) { 452 final VerifyCancelCallback callback = new VerifyCancelCallback(msg); 453 mDns.rawQuery(network, blob, FLAG_NO_CACHE_LOOKUP, executor, null, callback); 454 455 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", 456 callback.waitForAnswer()); 457 callback.assertHasAnswer(); 458 } 459 } 460 doTestRawQueryRoot(Executor executor)461 public void doTestRawQueryRoot(Executor executor) throws InterruptedException { 462 final String dname = ""; 463 final String msg = "RawQuery empty dname(ROOT) "; 464 for (Network network : getTestableNetworks()) { 465 final VerifyCancelCallback callback = new VerifyCancelCallback(msg); 466 mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, 467 executor, null, callback); 468 469 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", 470 callback.waitForAnswer()); 471 // Except no answer record because the root does not have AAAA records. 472 callback.assertEmptyAnswer(); 473 } 474 } 475 doTestRawQueryNXDomain(Executor executor)476 public void doTestRawQueryNXDomain(Executor executor) throws InterruptedException { 477 final String msg = "RawQuery " + TEST_NX_DOMAIN; 478 479 for (Network network : getTestableNetworks()) { 480 final NetworkCapabilities nc = (network != null) 481 ? mCM.getNetworkCapabilities(network) 482 : mCM.getNetworkCapabilities(mCM.getActiveNetwork()); 483 assertNotNull("Couldn't determine NetworkCapabilities for " + network, nc); 484 // Some cellular networks configure their DNS servers never to return NXDOMAIN, so don't 485 // test NXDOMAIN on these DNS servers. 486 // b/144521720 487 if (nc.hasTransport(TRANSPORT_CELLULAR)) continue; 488 final VerifyCancelCallback callback = new VerifyCancelCallback(msg); 489 mDns.rawQuery(network, TEST_NX_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, 490 executor, null, callback); 491 492 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", 493 callback.waitForAnswer()); 494 callback.assertNXDomain(); 495 } 496 } 497 doTestRawQueryNXDomainWithPrivateDns(Executor executor)498 public void doTestRawQueryNXDomainWithPrivateDns(Executor executor) 499 throws InterruptedException { 500 final String msg = "RawQuery " + TEST_NX_DOMAIN + " with private DNS"; 501 // Enable private DNS strict mode and set server to dns.google before doing NxDomain test. 502 // b/144521720 503 mCtsNetUtils.setPrivateDnsStrictMode(GOOGLE_PRIVATE_DNS_SERVER); 504 for (Network network : getTestableNetworks()) { 505 final Network networkForPrivateDns = 506 (network != null) ? network : mCM.getActiveNetwork(); 507 assertNotNull("Can't find network to await private DNS on", networkForPrivateDns); 508 mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout", 509 networkForPrivateDns, GOOGLE_PRIVATE_DNS_SERVER, true); 510 final VerifyCancelCallback callback = new VerifyCancelCallback(msg); 511 mDns.rawQuery(network, TEST_NX_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, 512 executor, null, callback); 513 514 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", 515 callback.waitForAnswer()); 516 callback.assertNXDomain(); 517 } 518 } 519 520 @Test testRawQueryCancel()521 public void testRawQueryCancel() throws InterruptedException { 522 final String msg = "Test cancel RawQuery " + TEST_DOMAIN; 523 // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect 524 // that the query is cancelled before it succeeds. If it is not cancelled before it 525 // succeeds, retry the test until it is. 526 for (Network network : getTestableNetworks()) { 527 boolean retry = false; 528 int round = 0; 529 do { 530 if (++round > CANCEL_RETRY_TIMES) { 531 fail(msg + " cancel failed " + CANCEL_RETRY_TIMES + " times"); 532 } 533 final CountDownLatch latch = new CountDownLatch(1); 534 final CancellationSignal cancelSignal = new CancellationSignal(); 535 final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal); 536 mDns.rawQuery(network, TEST_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_EMPTY, 537 mExecutor, cancelSignal, callback); 538 mExecutor.execute(() -> { 539 cancelSignal.cancel(); 540 latch.countDown(); 541 }); 542 543 retry = callback.needRetry(); 544 assertTrue(msg + " query was not cancelled", 545 latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 546 } while (retry); 547 } 548 } 549 550 @Test testRawQueryBlobCancel()551 public void testRawQueryBlobCancel() throws InterruptedException { 552 final String msg = "Test cancel RawQuery blob " + byteArrayToHexString(TEST_BLOB); 553 // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect 554 // that the query is cancelled before it succeeds. If it is not cancelled before it 555 // succeeds, retry the test until it is. 556 for (Network network : getTestableNetworks()) { 557 boolean retry = false; 558 int round = 0; 559 do { 560 if (++round > CANCEL_RETRY_TIMES) { 561 fail(msg + " cancel failed " + CANCEL_RETRY_TIMES + " times"); 562 } 563 final CountDownLatch latch = new CountDownLatch(1); 564 final CancellationSignal cancelSignal = new CancellationSignal(); 565 final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal); 566 mDns.rawQuery(network, TEST_BLOB, FLAG_EMPTY, mExecutor, cancelSignal, callback); 567 mExecutor.execute(() -> { 568 cancelSignal.cancel(); 569 latch.countDown(); 570 }); 571 572 retry = callback.needRetry(); 573 assertTrue(msg + " cancel is not done", 574 latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 575 } while (retry); 576 } 577 } 578 579 @Test testCancelBeforeQuery()580 public void testCancelBeforeQuery() throws InterruptedException { 581 final String msg = "Test cancelled RawQuery " + TEST_DOMAIN; 582 for (Network network : getTestableNetworks()) { 583 final VerifyCancelCallback callback = new VerifyCancelCallback(msg); 584 final CancellationSignal cancelSignal = new CancellationSignal(); 585 cancelSignal.cancel(); 586 mDns.rawQuery(network, TEST_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_EMPTY, 587 mExecutor, cancelSignal, callback); 588 589 assertTrue(msg + " should not return any answers", 590 !callback.waitForAnswer(CANCEL_TIMEOUT_MS)); 591 } 592 } 593 594 /** 595 * A query callback for InetAddress that ensures that the query is 596 * cancelled and that onAnswer is never called. If the query succeeds 597 * before it is cancelled, needRetry will return true so the 598 * test can retry. 599 */ 600 class VerifyCancelInetAddressCallback implements DnsResolver.Callback<List<InetAddress>> { 601 private final CountDownLatch mLatch = new CountDownLatch(1); 602 private final String mMsg; 603 private final List<InetAddress> mAnswers; 604 private final CancellationSignal mCancelSignal; 605 private String mErrorMsg = null; 606 VerifyCancelInetAddressCallback(@onNull String msg, @Nullable CancellationSignal cancel)607 VerifyCancelInetAddressCallback(@NonNull String msg, @Nullable CancellationSignal cancel) { 608 this.mMsg = msg; 609 this.mCancelSignal = cancel; 610 mAnswers = new ArrayList<>(); 611 } 612 waitForAnswer()613 public boolean waitForAnswer() throws InterruptedException { 614 return mLatch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS); 615 } 616 needRetry()617 public boolean needRetry() throws InterruptedException { 618 return mLatch.await(CANCEL_TIMEOUT_MS, TimeUnit.MILLISECONDS); 619 } 620 isAnswerEmpty()621 public boolean isAnswerEmpty() { 622 return mAnswers.isEmpty(); 623 } 624 hasIpv6Answer()625 public boolean hasIpv6Answer() { 626 for (InetAddress answer : mAnswers) { 627 if (answer instanceof Inet6Address) return true; 628 } 629 return false; 630 } 631 hasIpv4Answer()632 public boolean hasIpv4Answer() { 633 for (InetAddress answer : mAnswers) { 634 if (answer instanceof Inet4Address) return true; 635 } 636 return false; 637 } 638 assertNoError()639 public void assertNoError() { 640 assertNull(mErrorMsg); 641 } 642 643 @Override onAnswer(@onNull List<InetAddress> answerList, int rcode)644 public void onAnswer(@NonNull List<InetAddress> answerList, int rcode) { 645 if (mCancelSignal != null && mCancelSignal.isCanceled()) { 646 mErrorMsg = mMsg + " should not have returned any answers"; 647 mLatch.countDown(); 648 return; 649 } 650 for (InetAddress addr : answerList) { 651 Log.d(TAG, "Reported addr: " + addr.toString()); 652 } 653 mAnswers.clear(); 654 mAnswers.addAll(answerList); 655 mLatch.countDown(); 656 } 657 658 @Override onError(@onNull DnsResolver.DnsException error)659 public void onError(@NonNull DnsResolver.DnsException error) { 660 mErrorMsg = mMsg + error.getMessage(); 661 mLatch.countDown(); 662 } 663 } 664 665 @Test 666 @DnsResolverModuleTest testQueryForInetAddress()667 public void testQueryForInetAddress() throws Exception { 668 doTestQueryForInetAddress(mExecutor); 669 } 670 671 @Test 672 @DnsResolverModuleTest testQueryForInetAddressInline()673 public void testQueryForInetAddressInline() throws Exception { 674 doTestQueryForInetAddress(mExecutorInline); 675 } 676 677 @Test 678 @DnsResolverModuleTest testQueryForInetAddressIpv4()679 public void testQueryForInetAddressIpv4() throws Exception { 680 doTestQueryForInetAddressIpv4(mExecutor); 681 } 682 683 @Test 684 @DnsResolverModuleTest testQueryForInetAddressIpv4Inline()685 public void testQueryForInetAddressIpv4Inline() throws Exception { 686 doTestQueryForInetAddressIpv4(mExecutorInline); 687 } 688 689 @Test 690 @DnsResolverModuleTest testQueryForInetAddressIpv6()691 public void testQueryForInetAddressIpv6() throws Exception { 692 doTestQueryForInetAddressIpv6(mExecutor); 693 } 694 695 @Test 696 @DnsResolverModuleTest testQueryForInetAddressIpv6Inline()697 public void testQueryForInetAddressIpv6Inline() throws Exception { 698 doTestQueryForInetAddressIpv6(mExecutorInline); 699 } 700 701 @Test 702 @DnsResolverModuleTest testContinuousQueries()703 public void testContinuousQueries() throws Exception { 704 doTestContinuousQueries(mExecutor); 705 } 706 707 @Test 708 @DnsResolverModuleTest 709 @SkipPresubmit(reason = "Flaky: b/159762682; add to presubmit after fixing") testContinuousQueriesInline()710 public void testContinuousQueriesInline() throws Exception { 711 doTestContinuousQueries(mExecutorInline); 712 } 713 doTestQueryForInetAddress(Executor executor)714 public void doTestQueryForInetAddress(Executor executor) throws InterruptedException { 715 final String msg = "Test query for InetAddress " + TEST_DOMAIN; 716 for (Network network : getTestableNetworks()) { 717 final VerifyCancelInetAddressCallback callback = 718 new VerifyCancelInetAddressCallback(msg, null); 719 mDns.query(network, TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, executor, null, callback); 720 721 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", 722 callback.waitForAnswer()); 723 callback.assertNoError(); 724 assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty()); 725 } 726 } 727 728 @Test testQueryCancelForInetAddress()729 public void testQueryCancelForInetAddress() throws InterruptedException { 730 final String msg = "Test cancel query for InetAddress " + TEST_DOMAIN; 731 // Start a DNS query and the cancel it immediately. Use VerifyCancelInetAddressCallback to 732 // expect that the query is cancelled before it succeeds. If it is not cancelled before it 733 // succeeds, retry the test until it is. 734 for (Network network : getTestableNetworks()) { 735 boolean retry = false; 736 int round = 0; 737 do { 738 if (++round > CANCEL_RETRY_TIMES) { 739 fail(msg + " cancel failed " + CANCEL_RETRY_TIMES + " times"); 740 } 741 final CountDownLatch latch = new CountDownLatch(1); 742 final CancellationSignal cancelSignal = new CancellationSignal(); 743 final VerifyCancelInetAddressCallback callback = 744 new VerifyCancelInetAddressCallback(msg, cancelSignal); 745 mDns.query(network, TEST_DOMAIN, FLAG_EMPTY, mExecutor, cancelSignal, callback); 746 mExecutor.execute(() -> { 747 cancelSignal.cancel(); 748 latch.countDown(); 749 }); 750 751 retry = callback.needRetry(); 752 assertTrue(msg + " query was not cancelled", 753 latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 754 } while (retry); 755 } 756 } 757 doTestQueryForInetAddressIpv4(Executor executor)758 public void doTestQueryForInetAddressIpv4(Executor executor) throws InterruptedException { 759 final String msg = "Test query for IPv4 InetAddress " + TEST_DOMAIN; 760 for (Network network : getTestableNetworks()) { 761 final VerifyCancelInetAddressCallback callback = 762 new VerifyCancelInetAddressCallback(msg, null); 763 mDns.query(network, TEST_DOMAIN, TYPE_A, FLAG_NO_CACHE_LOOKUP, 764 executor, null, callback); 765 766 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", 767 callback.waitForAnswer()); 768 callback.assertNoError(); 769 assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty()); 770 assertTrue(msg + " returned Ipv6 results", !callback.hasIpv6Answer()); 771 } 772 } 773 doTestQueryForInetAddressIpv6(Executor executor)774 public void doTestQueryForInetAddressIpv6(Executor executor) throws InterruptedException { 775 final String msg = "Test query for IPv6 InetAddress " + TEST_DOMAIN; 776 for (Network network : getTestableNetworks()) { 777 final VerifyCancelInetAddressCallback callback = 778 new VerifyCancelInetAddressCallback(msg, null); 779 mDns.query(network, TEST_DOMAIN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, 780 executor, null, callback); 781 782 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", 783 callback.waitForAnswer()); 784 callback.assertNoError(); 785 assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty()); 786 assertTrue(msg + " returned Ipv4 results", !callback.hasIpv4Answer()); 787 } 788 } 789 790 @Test testPrivateDnsBypass()791 public void testPrivateDnsBypass() throws InterruptedException { 792 final String dataStallSetting = Settings.Global.getString(mCR, 793 Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK); 794 Settings.Global.putInt(mCR, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 0); 795 try { 796 doTestPrivateDnsBypass(); 797 } finally { 798 Settings.Global.putString(mCR, Settings.Global.DATA_STALL_RECOVERY_ON_BAD_NETWORK, 799 dataStallSetting); 800 } 801 } 802 doTestPrivateDnsBypass()803 private void doTestPrivateDnsBypass() throws InterruptedException { 804 final Network[] testNetworks = getTestableNetworks(); 805 806 // Set an invalid private DNS server 807 mCtsNetUtils.setPrivateDnsStrictMode(INVALID_PRIVATE_DNS_SERVER); 808 final String msg = "Test PrivateDnsBypass " + TEST_DOMAIN; 809 for (Network network : testNetworks) { 810 // This test cannot be ran with null network because we need to explicitly pass a 811 // private DNS bypassable network or bind one. 812 if (network == null) continue; 813 814 // wait for private DNS setting propagating 815 mCtsNetUtils.awaitPrivateDnsSetting(msg + " wait private DNS setting timeout", 816 network, INVALID_PRIVATE_DNS_SERVER, false); 817 818 final CountDownLatch latch = new CountDownLatch(1); 819 final DnsResolver.Callback<List<InetAddress>> errorCallback = 820 new DnsResolver.Callback<List<InetAddress>>() { 821 @Override 822 public void onAnswer(@NonNull List<InetAddress> answerList, int rcode) { 823 fail(msg + " should not get valid answer"); 824 } 825 826 @Override 827 public void onError(@NonNull DnsResolver.DnsException error) { 828 assertEquals(DnsResolver.ERROR_SYSTEM, error.code); 829 assertEquals(ETIMEDOUT, ((ErrnoException) error.getCause()).errno); 830 latch.countDown(); 831 } 832 }; 833 // Private DNS strict mode with invalid DNS server is set 834 // Expect no valid answer returned but ErrnoException with ETIMEDOUT 835 mDns.query(network, TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, errorCallback); 836 837 assertTrue(msg + " invalid server round. No response after " + TIMEOUT_MS + "ms.", 838 latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); 839 840 final VerifyCancelInetAddressCallback callback = 841 new VerifyCancelInetAddressCallback(msg, null); 842 // Bypass privateDns, expect query works fine 843 mDns.query(network.getPrivateDnsBypassingCopy(), 844 TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callback); 845 846 assertTrue(msg + " bypass private DNS round. No answer after " + TIMEOUT_MS + "ms.", 847 callback.waitForAnswer()); 848 callback.assertNoError(); 849 assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty()); 850 851 // To ensure private DNS bypass still work even if passing null network. 852 // Bind process network with a private DNS bypassable network. 853 mCM.bindProcessToNetwork(network.getPrivateDnsBypassingCopy()); 854 final VerifyCancelInetAddressCallback callbackWithNullNetwork = 855 new VerifyCancelInetAddressCallback(msg + " with null network ", null); 856 mDns.query(null, 857 TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callbackWithNullNetwork); 858 859 assertTrue(msg + " with null network bypass private DNS round. No answer after " + 860 TIMEOUT_MS + "ms.", callbackWithNullNetwork.waitForAnswer()); 861 callbackWithNullNetwork.assertNoError(); 862 assertTrue(msg + " with null network returned 0 results", 863 !callbackWithNullNetwork.isAnswerEmpty()); 864 865 // Reset process network to default. 866 mCM.bindProcessToNetwork(null); 867 } 868 } 869 doTestContinuousQueries(Executor executor)870 public void doTestContinuousQueries(Executor executor) throws InterruptedException { 871 for (Network network : getTestableNetworks()) { 872 for (int i = 0; i < QUERY_TIMES ; ++i) { 873 // query v6/v4 in turn 874 boolean queryV6 = (i % 2 == 0); 875 final String msg = "Test continuous " + QUERY_TIMES + " queries " + TEST_DOMAIN 876 + " on " + network + ", queryV6=" + queryV6; 877 final VerifyCancelInetAddressCallback callback = 878 new VerifyCancelInetAddressCallback(msg, null); 879 mDns.query(network, TEST_DOMAIN, queryV6 ? TYPE_AAAA : TYPE_A, 880 FLAG_NO_CACHE_LOOKUP, executor, null, callback); 881 882 assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", 883 callback.waitForAnswer()); 884 callback.assertNoError(); 885 assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty()); 886 assertTrue(msg + " returned " + (queryV6 ? "Ipv4" : "Ipv6") + " results", 887 queryV6 ? !callback.hasIpv4Answer() : !callback.hasIpv6Answer()); 888 } 889 } 890 } 891 892 /** Verifies that DnsResolver.DnsException can be subclassed and its constructor re-used. */ 893 @Test @IgnoreUpTo(SC_V2) // TODO: Use to Build.VERSION_CODES.SC_V2 when available testDnsExceptionConstructor()894 public void testDnsExceptionConstructor() throws InterruptedException { 895 class TestDnsException extends DnsResolver.DnsException { 896 TestDnsException(int code, @Nullable Throwable cause) { 897 super(code, cause); 898 } 899 } 900 try { 901 throw new TestDnsException(DnsResolver.ERROR_SYSTEM, null); 902 } catch (DnsResolver.DnsException e) { 903 assertEquals(DnsResolver.ERROR_SYSTEM, e.code); 904 } 905 } 906 907 @Test testNoRawBinderAccess()908 public void testNoRawBinderAccess() { 909 assertNull(mContext.getSystemService("dnsresolver")); 910 } 911 } 912