Add bypass private DNS test case and null network test for DnsResolver cts

1. add test case for testing bypass Private DNS
2. add null network test
3. minor change for cleanup

Bug: 130594022
Test: atest DnsResolverTest

Merged-In: I8dd48f11baf92d953ded237204a3c2cd3b58581d
(cherry picked from commit da665a0a78a2919c3e2edabafec463de3de9ddb7)

Change-Id: Iabb100c51fd80eca5ab0284bc891f4fb98492e8b
This commit is contained in:
Luke Huang
2019-05-30 04:46:32 -07:00
parent 87ac083a3b
commit 0b35214faa

View File

@@ -21,20 +21,25 @@ import static android.net.DnsResolver.FLAG_EMPTY;
import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP; import static android.net.DnsResolver.FLAG_NO_CACHE_LOOKUP;
import static android.net.DnsResolver.TYPE_A; import static android.net.DnsResolver.TYPE_A;
import static android.net.DnsResolver.TYPE_AAAA; import static android.net.DnsResolver.TYPE_AAAA;
import static android.system.OsConstants.EBADF; import static android.system.OsConstants.ETIMEDOUT;
import android.annotation.NonNull; import android.annotation.NonNull;
import android.annotation.Nullable; import android.annotation.Nullable;
import android.content.Context; import android.content.Context;
import android.content.ContentResolver;
import android.net.ConnectivityManager; import android.net.ConnectivityManager;
import android.net.ConnectivityManager.NetworkCallback;
import android.net.DnsPacket; import android.net.DnsPacket;
import android.net.DnsResolver; import android.net.DnsResolver;
import android.net.LinkProperties;
import android.net.Network; import android.net.Network;
import android.net.NetworkCapabilities; import android.net.NetworkCapabilities;
import android.net.NetworkRequest;
import android.net.ParseException; import android.net.ParseException;
import android.os.CancellationSignal; import android.os.CancellationSignal;
import android.os.Handler; import android.os.Handler;
import android.os.Looper; import android.os.Looper;
import android.provider.Settings;
import android.system.ErrnoException; import android.system.ErrnoException;
import android.test.AndroidTestCase; import android.test.AndroidTestCase;
import android.util.Log; import android.util.Log;
@@ -53,20 +58,63 @@ public class DnsResolverTest extends AndroidTestCase {
private static final char[] HEX_CHARS = { private static final char[] HEX_CHARS = {
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F' '0', '1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F'
}; };
static final String TEST_DOMAIN = "www.google.com";
static final String INVALID_PRIVATE_DNS_SERVER = "invalid.google";
static final byte[] TEST_BLOB = new byte[]{
/* Header */
0x55, 0x66, /* Transaction ID */
0x01, 0x00, /* Flags */
0x00, 0x01, /* Questions */
0x00, 0x00, /* Answer RRs */
0x00, 0x00, /* Authority RRs */
0x00, 0x00, /* Additional RRs */
/* Queries */
0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
0x00, 0x01, /* Type */
0x00, 0x01 /* Class */
};
static final int TIMEOUT_MS = 12_000; static final int TIMEOUT_MS = 12_000;
static final int CANCEL_TIMEOUT_MS = 3_000; static final int CANCEL_TIMEOUT_MS = 3_000;
static final int CANCEL_RETRY_TIMES = 5; static final int CANCEL_RETRY_TIMES = 5;
static final int NXDOMAIN = 3; static final int NXDOMAIN = 3;
static final int PRIVATE_DNS_SETTING_TIMEOUT_MS = 2_000;
private ContentResolver mCR;
private ConnectivityManager mCM; private ConnectivityManager mCM;
private Executor mExecutor; private Executor mExecutor;
private DnsResolver mDns; private DnsResolver mDns;
private String mOldMode;
private String mOldDnsSpecifier;
@Override
protected void setUp() throws Exception { protected void setUp() throws Exception {
super.setUp(); super.setUp();
mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE); mCM = (ConnectivityManager) getContext().getSystemService(Context.CONNECTIVITY_SERVICE);
mDns = DnsResolver.getInstance(); mDns = DnsResolver.getInstance();
mExecutor = new Handler(Looper.getMainLooper())::post; mExecutor = new Handler(Looper.getMainLooper())::post;
mCR = getContext().getContentResolver();
storePrivateDnsSetting();
}
@Override
protected void tearDown() throws Exception {
restorePrivateDnsSetting();
super.tearDown();
}
private void storePrivateDnsSetting() {
// Store private DNS setting
mOldMode = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_MODE);
mOldDnsSpecifier = Settings.Global.getString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER);
}
private void restorePrivateDnsSetting() {
// restore private DNS setting
Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, mOldMode);
Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_SPECIFIER, mOldDnsSpecifier);
} }
private static String byteArrayToHexString(byte[] bytes) { private static String byteArrayToHexString(byte[] bytes) {
@@ -94,6 +142,9 @@ public class DnsResolverTest extends AndroidTestCase {
"This test requires that at least one network be connected. " + "This test requires that at least one network be connected. " +
"Please ensure that the device is connected to a network.", "Please ensure that the device is connected to a network.",
testableNetworks.size() >= 1); testableNetworks.size() >= 1);
// In order to test query with null network, add null as an element.
// Test cases which query with null network will go on default network.
testableNetworks.add(null);
return testableNetworks.toArray(new Network[0]); return testableNetworks.toArray(new Network[0]);
} }
@@ -105,15 +156,12 @@ public class DnsResolverTest extends AndroidTestCase {
public DnsParseException(String msg) { public DnsParseException(String msg) {
super(msg); super(msg);
} }
public DnsParseException(String msg, Throwable cause) {
super(msg, cause);
}
} }
private static class DnsAnswer extends DnsPacket { private static class DnsAnswer extends DnsPacket {
DnsAnswer(@NonNull byte[] data) throws DnsParseException { DnsAnswer(@NonNull byte[] data) throws DnsParseException {
super(data); super(data);
// Check QR field.(query (0), or a response (1)). // Check QR field.(query (0), or a response (1)).
if ((mHeader.flags & (1 << 15)) == 0) { if ((mHeader.flags & (1 << 15)) == 0) {
throw new DnsParseException("Not an answer packet"); throw new DnsParseException("Not an answer packet");
@@ -123,10 +171,12 @@ public class DnsResolverTest extends AndroidTestCase {
int getRcode() { int getRcode() {
return mHeader.rcode; return mHeader.rcode;
} }
int getANCount(){
int getANCount() {
return mHeader.getRecordCount(ANSECTION); return mHeader.getRecordCount(ANSECTION);
} }
int getQDCount(){
int getQDCount() {
return mHeader.getRecordCount(QDSECTION); return mHeader.getRecordCount(QDSECTION);
} }
} }
@@ -173,7 +223,7 @@ public class DnsResolverTest extends AndroidTestCase {
mRcode = rcode; mRcode = rcode;
try { try {
mDnsAnswer = new DnsAnswer(answer); mDnsAnswer = new DnsAnswer(answer);
} catch (DnsParseException e) { } catch (ParseException | DnsParseException e) {
fail(mMsg + e.getMessage()); fail(mMsg + e.getMessage());
} }
Log.d(TAG, "Reported blob: " + byteArrayToHexString(answer)); Log.d(TAG, "Reported blob: " + byteArrayToHexString(answer));
@@ -222,90 +272,76 @@ public class DnsResolverTest extends AndroidTestCase {
} }
} }
public void testRawQuery() { public void testRawQuery() throws InterruptedException {
final String dname = "www.google.com"; final String msg = "RawQuery " + TEST_DOMAIN;
final String msg = "RawQuery " + dname;
for (Network network : getTestableNetworks()) { for (Network network : getTestableNetworks()) {
final VerifyCancelCallback callback = new VerifyCancelCallback(msg); final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, mDns.rawQuery(network, TEST_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
mExecutor, null, callback); mExecutor, null, callback);
try {
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer()); callback.waitForAnswer());
callback.assertHasAnswer(); callback.assertHasAnswer();
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} }
} }
public void testRawQueryBlob() { public void testRawQueryBlob() throws InterruptedException {
final byte[] blob = new byte[]{ final byte[] blob = new byte[]{
/* Header */ /* Header */
0x55, 0x66, /* Transaction ID */ 0x55, 0x66, /* Transaction ID */
0x01, 0x00, /* Flags */ 0x01, 0x00, /* Flags */
0x00, 0x01, /* Questions */ 0x00, 0x01, /* Questions */
0x00, 0x00, /* Answer RRs */ 0x00, 0x00, /* Answer RRs */
0x00, 0x00, /* Authority RRs */ 0x00, 0x00, /* Authority RRs */
0x00, 0x00, /* Additional RRs */ 0x00, 0x00, /* Additional RRs */
/* Queries */ /* Queries */
0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65, 0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */ 0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
0x00, 0x01, /* Type */ 0x00, 0x01, /* Type */
0x00, 0x01 /* Class */ 0x00, 0x01 /* Class */
}; };
final String msg = "RawQuery blob " + byteArrayToHexString(blob); final String msg = "RawQuery blob " + byteArrayToHexString(blob);
for (Network network : getTestableNetworks()) { for (Network network : getTestableNetworks()) {
final VerifyCancelCallback callback = new VerifyCancelCallback(msg); final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
mDns.rawQuery(network, blob, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callback); mDns.rawQuery(network, blob, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callback);
try {
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer()); callback.waitForAnswer());
callback.assertHasAnswer(); callback.assertHasAnswer();
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} }
} }
public void testRawQueryRoot() { public void testRawQueryRoot() throws InterruptedException {
final String dname = ""; final String dname = "";
final String msg = "RawQuery empty dname(ROOT) "; final String msg = "RawQuery empty dname(ROOT) ";
for (Network network : getTestableNetworks()) { for (Network network : getTestableNetworks()) {
final VerifyCancelCallback callback = new VerifyCancelCallback(msg); final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
mExecutor, null, callback); mExecutor, null, callback);
try {
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer()); callback.waitForAnswer());
// Except no answer record because the root does not have AAAA records. // Except no answer record because the root does not have AAAA records.
callback.assertEmptyAnswer(); callback.assertEmptyAnswer();
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} }
} }
public void testRawQueryNXDomain() { public void testRawQueryNXDomain() throws InterruptedException {
final String dname = "test1-nx.metric.gstatic.com"; final String dname = "test1-nx.metric.gstatic.com";
final String msg = "RawQuery " + dname; final String msg = "RawQuery " + dname;
for (Network network : getTestableNetworks()) { for (Network network : getTestableNetworks()) {
final VerifyCancelCallback callback = new VerifyCancelCallback(msg); final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
mExecutor, null, callback); mExecutor, null, callback);
try {
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer()); callback.waitForAnswer());
callback.assertNXDomain(); callback.assertNXDomain();
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} }
} }
public void testRawQueryCancel() throws ErrnoException { public void testRawQueryCancel() throws InterruptedException {
final String dname = "www.google.com"; final String msg = "Test cancel RawQuery " + TEST_DOMAIN;
final String msg = "Test cancel RawQuery " + dname;
// Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
// that the query is cancelled before it succeeds. If it is not cancelled before it // that the query is cancelled before it succeeds. If it is not cancelled before it
// succeeds, retry the test until it is. // succeeds, retry the test until it is.
@@ -319,39 +355,22 @@ public class DnsResolverTest extends AndroidTestCase {
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
final CancellationSignal cancelSignal = new CancellationSignal(); final CancellationSignal cancelSignal = new CancellationSignal();
final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal); final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal);
mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_EMPTY, mDns.rawQuery(network, TEST_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_EMPTY,
mExecutor, cancelSignal, callback); mExecutor, cancelSignal, callback);
mExecutor.execute(() -> { mExecutor.execute(() -> {
cancelSignal.cancel(); cancelSignal.cancel();
latch.countDown(); latch.countDown();
}); });
try {
retry = callback.needRetry(); retry = callback.needRetry();
assertTrue(msg + " query was not cancelled", assertTrue(msg + " query was not cancelled",
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} while (retry); } while (retry);
} }
} }
public void testRawQueryBlobCancel() throws ErrnoException { public void testRawQueryBlobCancel() throws InterruptedException {
final byte[] blob = new byte[]{ final String msg = "Test cancel RawQuery blob " + byteArrayToHexString(TEST_BLOB);
/* Header */
0x55, 0x66, /* Transaction ID */
0x01, 0x00, /* Flags */
0x00, 0x01, /* Questions */
0x00, 0x00, /* Answer RRs */
0x00, 0x00, /* Authority RRs */
0x00, 0x00, /* Additional RRs */
/* Queries */
0x03, 0x77, 0x77, 0x77, 0x06, 0x67, 0x6F, 0x6F, 0x67, 0x6c, 0x65,
0x03, 0x63, 0x6f, 0x6d, 0x00, /* Name */
0x00, 0x01, /* Type */
0x00, 0x01 /* Class */
};
final String msg = "Test cancel RawQuery blob " + byteArrayToHexString(blob);
// Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect // Start a DNS query and the cancel it immediately. Use VerifyCancelCallback to expect
// that the query is cancelled before it succeeds. If it is not cancelled before it // that the query is cancelled before it succeeds. If it is not cancelled before it
// succeeds, retry the test until it is. // succeeds, retry the test until it is.
@@ -365,37 +384,30 @@ public class DnsResolverTest extends AndroidTestCase {
final CountDownLatch latch = new CountDownLatch(1); final CountDownLatch latch = new CountDownLatch(1);
final CancellationSignal cancelSignal = new CancellationSignal(); final CancellationSignal cancelSignal = new CancellationSignal();
final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal); final VerifyCancelCallback callback = new VerifyCancelCallback(msg, cancelSignal);
mDns.rawQuery(network, blob, FLAG_EMPTY, mExecutor, cancelSignal, callback); mDns.rawQuery(network, TEST_BLOB, FLAG_EMPTY, mExecutor, cancelSignal, callback);
mExecutor.execute(() -> { mExecutor.execute(() -> {
cancelSignal.cancel(); cancelSignal.cancel();
latch.countDown(); latch.countDown();
}); });
try {
retry = callback.needRetry(); retry = callback.needRetry();
assertTrue(msg + " cancel is not done", assertTrue(msg + " cancel is not done",
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} while (retry); } while (retry);
} }
} }
public void testCancelBeforeQuery() throws ErrnoException { public void testCancelBeforeQuery() throws InterruptedException {
final String dname = "www.google.com"; final String msg = "Test cancelled RawQuery " + TEST_DOMAIN;
final String msg = "Test cancelled RawQuery " + dname;
for (Network network : getTestableNetworks()) { for (Network network : getTestableNetworks()) {
final VerifyCancelCallback callback = new VerifyCancelCallback(msg); final VerifyCancelCallback callback = new VerifyCancelCallback(msg);
final CancellationSignal cancelSignal = new CancellationSignal(); final CancellationSignal cancelSignal = new CancellationSignal();
cancelSignal.cancel(); cancelSignal.cancel();
mDns.rawQuery(network, dname, CLASS_IN, TYPE_AAAA, FLAG_EMPTY, mDns.rawQuery(network, TEST_DOMAIN, CLASS_IN, TYPE_AAAA, FLAG_EMPTY,
mExecutor, cancelSignal, callback); mExecutor, cancelSignal, callback);
try {
assertTrue(msg + " should not return any answers", assertTrue(msg + " should not return any answers",
!callback.waitForAnswer(CANCEL_TIMEOUT_MS)); !callback.waitForAnswer(CANCEL_TIMEOUT_MS));
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} }
} }
@@ -462,27 +474,21 @@ public class DnsResolverTest extends AndroidTestCase {
} }
} }
public void testQueryForInetAddress() { public void testQueryForInetAddress() throws InterruptedException {
final String dname = "www.google.com"; final String msg = "Test query for InetAddress " + TEST_DOMAIN;
final String msg = "Test query for InetAddress " + dname;
for (Network network : getTestableNetworks()) { for (Network network : getTestableNetworks()) {
final VerifyCancelInetAddressCallback callback = final VerifyCancelInetAddressCallback callback =
new VerifyCancelInetAddressCallback(msg, null); new VerifyCancelInetAddressCallback(msg, null);
mDns.query(network, dname, FLAG_NO_CACHE_LOOKUP, mDns.query(network, TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callback);
mExecutor, null, callback);
try { assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", callback.waitForAnswer());
callback.waitForAnswer()); assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} }
} }
public void testQueryCancelForInetAddress() throws ErrnoException { public void testQueryCancelForInetAddress() throws InterruptedException {
final String dname = "www.google.com"; final String msg = "Test cancel query for InetAddress " + TEST_DOMAIN;
final String msg = "Test cancel query for InetAddress " + dname;
// Start a DNS query and the cancel it immediately. Use VerifyCancelInetAddressCallback to // Start a DNS query and the cancel it immediately. Use VerifyCancelInetAddressCallback to
// expect that the query is cancelled before it succeeds. If it is not cancelled before it // expect that the query is cancelled before it succeeds. If it is not cancelled before it
// succeeds, retry the test until it is. // succeeds, retry the test until it is.
@@ -497,57 +503,131 @@ public class DnsResolverTest extends AndroidTestCase {
final CancellationSignal cancelSignal = new CancellationSignal(); final CancellationSignal cancelSignal = new CancellationSignal();
final VerifyCancelInetAddressCallback callback = final VerifyCancelInetAddressCallback callback =
new VerifyCancelInetAddressCallback(msg, cancelSignal); new VerifyCancelInetAddressCallback(msg, cancelSignal);
mDns.query(network, dname, FLAG_EMPTY, mExecutor, cancelSignal, callback); mDns.query(network, TEST_DOMAIN, FLAG_EMPTY, mExecutor, cancelSignal, callback);
mExecutor.execute(() -> { mExecutor.execute(() -> {
cancelSignal.cancel(); cancelSignal.cancel();
latch.countDown(); latch.countDown();
}); });
try {
retry = callback.needRetry(); retry = callback.needRetry();
assertTrue(msg + " query was not cancelled", assertTrue(msg + " query was not cancelled",
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS)); latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} while (retry); } while (retry);
} }
} }
public void testQueryForInetAddressIpv4() { public void testQueryForInetAddressIpv4() throws InterruptedException {
final String dname = "www.google.com"; final String msg = "Test query for IPv4 InetAddress " + TEST_DOMAIN;
final String msg = "Test query for IPv4 InetAddress " + dname;
for (Network network : getTestableNetworks()) { for (Network network : getTestableNetworks()) {
final VerifyCancelInetAddressCallback callback = final VerifyCancelInetAddressCallback callback =
new VerifyCancelInetAddressCallback(msg, null); new VerifyCancelInetAddressCallback(msg, null);
mDns.query(network, dname, TYPE_A, FLAG_NO_CACHE_LOOKUP, mDns.query(network, TEST_DOMAIN, TYPE_A, FLAG_NO_CACHE_LOOKUP,
mExecutor, null, callback); mExecutor, null, callback);
try {
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer()); callback.waitForAnswer());
assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty()); assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
assertTrue(msg + " returned Ipv6 results", !callback.hasIpv6Answer()); assertTrue(msg + " returned Ipv6 results", !callback.hasIpv6Answer());
} catch (InterruptedException e) {
fail(msg + " Waiting for DNS lookup was interrupted");
}
} }
} }
public void testQueryForInetAddressIpv6() { public void testQueryForInetAddressIpv6() throws InterruptedException {
final String dname = "www.google.com"; final String msg = "Test query for IPv6 InetAddress " + TEST_DOMAIN;
final String msg = "Test query for IPv6 InetAddress " + dname;
for (Network network : getTestableNetworks()) { for (Network network : getTestableNetworks()) {
final VerifyCancelInetAddressCallback callback = final VerifyCancelInetAddressCallback callback =
new VerifyCancelInetAddressCallback(msg, null); new VerifyCancelInetAddressCallback(msg, null);
mDns.query(network, dname, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP, mDns.query(network, TEST_DOMAIN, TYPE_AAAA, FLAG_NO_CACHE_LOOKUP,
mExecutor, null, callback); mExecutor, null, callback);
try {
assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.", assertTrue(msg + " but no answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer()); callback.waitForAnswer());
assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty()); assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
assertTrue(msg + " returned Ipv4 results", !callback.hasIpv4Answer()); assertTrue(msg + " returned Ipv4 results", !callback.hasIpv4Answer());
} catch (InterruptedException e) { }
fail(msg + " Waiting for DNS lookup was interrupted"); }
private void awaitPrivateDnsSetting(@NonNull String msg,
@NonNull Network network, @NonNull String server) throws InterruptedException {
CountDownLatch latch = new CountDownLatch(1);
NetworkRequest request = new NetworkRequest.Builder().clearCapabilities().build();
NetworkCallback callback = new NetworkCallback() {
@Override
public void onLinkPropertiesChanged(Network n, LinkProperties lp) {
if (network.equals(n) && server.equals(lp.getPrivateDnsServerName())) {
latch.countDown();
}
} }
};
mCM.registerNetworkCallback(request, callback);
assertTrue(msg, latch.await(PRIVATE_DNS_SETTING_TIMEOUT_MS, TimeUnit.MILLISECONDS));
mCM.unregisterNetworkCallback(callback);
}
public void testPrivateDnsBypass() throws InterruptedException {
final Network[] testNetworks = getTestableNetworks();
// Set an invalid private DNS server
Settings.Global.putString(mCR, Settings.Global.PRIVATE_DNS_MODE, "hostname");
Settings.Global.putString(mCR,
Settings.Global.PRIVATE_DNS_SPECIFIER, INVALID_PRIVATE_DNS_SERVER);
final String msg = "Test PrivateDnsBypass " + TEST_DOMAIN;
for (Network network : testNetworks) {
// This test cannot be ran with null network because we need to explicitly pass a
// private DNS bypassable network or bind one.
if (network == null) continue;
// wait for private DNS setting propagating
awaitPrivateDnsSetting(msg + " wait private DNS setting timeout",
network, INVALID_PRIVATE_DNS_SERVER);
final CountDownLatch latch = new CountDownLatch(1);
final DnsResolver.Callback<List<InetAddress>> errorCallback =
new DnsResolver.Callback<List<InetAddress>>() {
@Override
public void onAnswer(@NonNull List<InetAddress> answerList, int rcode) {
fail(msg + " should not get valid answer");
}
@Override
public void onError(@NonNull DnsResolver.DnsException error) {
assertEquals(DnsResolver.ERROR_SYSTEM, error.code);
assertEquals(ETIMEDOUT, ((ErrnoException) error.getCause()).errno);
latch.countDown();
}
};
// Private DNS strict mode with invalid DNS server is set
// Expect no valid answer returned but ErrnoException with ETIMEDOUT
mDns.query(network, TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, errorCallback);
assertTrue(msg + " invalid server round. No response after " + TIMEOUT_MS + "ms.",
latch.await(TIMEOUT_MS, TimeUnit.MILLISECONDS));
final VerifyCancelInetAddressCallback callback =
new VerifyCancelInetAddressCallback(msg, null);
// Bypass privateDns, expect query works fine
mDns.query(network.getPrivateDnsBypassingCopy(),
TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callback);
assertTrue(msg + " bypass private DNS round. No answer after " + TIMEOUT_MS + "ms.",
callback.waitForAnswer());
assertTrue(msg + " returned 0 results", !callback.isAnswerEmpty());
// To ensure private DNS bypass still work even if passing null network.
// Bind process network with a private DNS bypassable network.
mCM.bindProcessToNetwork(network.getPrivateDnsBypassingCopy());
final VerifyCancelInetAddressCallback callbackWithNullNetwork =
new VerifyCancelInetAddressCallback(msg + " with null network ", null);
mDns.query(null,
TEST_DOMAIN, FLAG_NO_CACHE_LOOKUP, mExecutor, null, callbackWithNullNetwork);
assertTrue(msg + " with null network bypass private DNS round. No answer after " +
TIMEOUT_MS + "ms.", callbackWithNullNetwork.waitForAnswer());
assertTrue(msg + " with null network returned 0 results",
!callbackWithNullNetwork.isAnswerEmpty());
// Reset process network to default.
mCM.bindProcessToNetwork(null);
} }
} }
} }