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