Program local and TLS servers, and allow TLS-bypass

This change comprises several parts:

[1] Define a wasExplicitlyConfigured() notion on a DnsTlsServer to
    indicate whether the hostname or any fingerprints have been
    explicitly set. A DnsTlsServer not wasExplicitlyConfigured()
    implies opportunistic mode.

[2] The locally-assigned DNS servers get set in bionic, and the TLS
    servers get set in ResolverController.

[3] ResolverController::getPrivateDnsMode returns the Private DNS mode
    configured for a given netid.

[4] ResolverController::getValidatedTlsServers() returns a list of
    validated DnsTlsServers for a given netid.

[5] The mode and a non-empty list together instruct the qhook in
    DnsProxyListener to hand a query off to the DnsTlsDispatcher.

[6] The DnsTlsDispatcher iterates over the list of DnsTlsServers,
    preferring servers for which connections already exist.

[7] Enable EDNS0 for DNS-over-TLS queries (set the appropriate flag
    in the android_net_context.flags field).

[8] Introduce NETID_USE_LOCAL_NAMESERVERS flag for setting the high
    bit of netids in order to pass this informatin across the
    app<->netd boundary.

[9] Update setNetworkForResolv and getNetworkForResolv to handle the
    NETID_USE_LOCAL_NAMESERVERS flag accordingly.

[10] DnsProxyListener translates the NETID_USE_LOCAL_NAMESERVERS bit
     into the NET_CONTEXT_FLAG_USE_LOCAL_NAMESERVERS flag.

Test: as follows
    - built
    - flashed
    - booted
    - ./system/netd/tests/runtests.sh passes
Bug: 34953048
Bug: 64133961
Bug: 72345192
Bug: 76103007
Change-Id: Ib564c6a23c44b36755418fd1557cd86ea54dae44
diff --git a/tests/netd_test.cpp b/tests/netd_test.cpp
index 3b77833..8d1f6a8 100644
--- a/tests/netd_test.cpp
+++ b/tests/netd_test.cpp
@@ -39,6 +39,7 @@
 // TODO: make this dynamic and stop depending on implementation details.
 #define TEST_NETID 30
 
+#include "resolv_netid.h"
 #include "NetdClient.h"
 
 #include <gtest/gtest.h>
@@ -858,16 +859,21 @@
     // Wait for query to get counted.
     EXPECT_TRUE(tls.waitForQueries(2, 5000));
 
-    // Stop the TLS server.  Since it's already been validated, queries will
-    // continue to be routed to it.
+    // Stop the TLS server.  Since we're in opportunistic mode, queries will
+    // fall back to the locally-assigned (clear text) nameservers.
     tls.stopServer();
 
+    dns.clearQueries();
     result = gethostbyname("tls2");
-    EXPECT_TRUE(result == nullptr);
-    EXPECT_EQ(HOST_NOT_FOUND, h_errno);
+    EXPECT_FALSE(result == nullptr);
+    EXPECT_EQ("1.2.3.2", ToString(result));
+    const auto queries = dns.queries();
+    EXPECT_EQ(1U, queries.size());
+    EXPECT_EQ("tls2.example.com.", queries[0].first);
+    EXPECT_EQ(ns_t_a, queries[0].second);
 
-    // Reset the resolvers without enabling TLS.  Queries should now be routed to the
-    // UDP endpoint.
+    // Reset the resolvers without enabling TLS.  Queries should still be routed
+    // to the UDP endpoint.
     ASSERT_TRUE(SetResolversForNetwork(servers, mDefaultSearchDomains, mDefaultParams_Binder));
 
     result = gethostbyname("tls3");
@@ -1154,3 +1160,167 @@
     tls.stopServer();
     dns.stopServer();
 }
+
+TEST_F(ResolverTest, TlsBypass) {
+    const char OFF[] = "off";
+    const char OPPORTUNISTIC[] = "opportunistic";
+    const char STRICT[] = "strict";
+
+    const char GETHOSTBYNAME[] = "gethostbyname";
+    const char GETADDRINFO[] = "getaddrinfo";
+    const char GETADDRINFOFORNET[] = "getaddrinfofornet";
+
+    const unsigned BYPASS_NETID = NETID_USE_LOCAL_NAMESERVERS | TEST_NETID;
+
+    const std::vector<uint8_t> NOOP_FINGERPRINT(test::SHA256_SIZE, 0U);
+
+    const char ADDR4[] = "192.0.2.1";
+    const char ADDR6[] = "2001:db8::1";
+
+    const char cleartext_addr[] = "127.0.0.53";
+    const char cleartext_port[] = "53";
+    const char tls_port[] = "853";
+    const std::vector<std::string> servers = { cleartext_addr };
+
+    test::DNSResponder dns(cleartext_addr, cleartext_port, 250, ns_rcode::ns_r_servfail, 1.0);
+    ASSERT_TRUE(dns.startServer());
+
+    test::DnsTlsFrontend tls(cleartext_addr, tls_port, cleartext_addr, cleartext_port);
+
+    struct TestConfig {
+        const std::string mode;
+        const bool withWorkingTLS;
+        const std::string method;
+
+        std::string asHostName() const {
+            return StringPrintf("%s.%s.%s.",
+                                mode.c_str(),
+                                withWorkingTLS ? "tlsOn" : "tlsOff",
+                                method.c_str());
+        }
+    } testConfigs[]{
+        {OFF,           false, GETHOSTBYNAME},
+        {OPPORTUNISTIC, false, GETHOSTBYNAME},
+        {STRICT,        false, GETHOSTBYNAME},
+        {OFF,           true,  GETHOSTBYNAME},
+        {OPPORTUNISTIC, true,  GETHOSTBYNAME},
+        {STRICT,        true,  GETHOSTBYNAME},
+        {OFF,           false, GETADDRINFO},
+        {OPPORTUNISTIC, false, GETADDRINFO},
+        {STRICT,        false, GETADDRINFO},
+        {OFF,           true,  GETADDRINFO},
+        {OPPORTUNISTIC, true,  GETADDRINFO},
+        {STRICT,        true,  GETADDRINFO},
+        {OFF,           false, GETADDRINFOFORNET},
+        {OPPORTUNISTIC, false, GETADDRINFOFORNET},
+        {STRICT,        false, GETADDRINFOFORNET},
+        {OFF,           true,  GETADDRINFOFORNET},
+        {OPPORTUNISTIC, true,  GETADDRINFOFORNET},
+        {STRICT,        true,  GETADDRINFOFORNET},
+    };
+
+    for (const auto& config : testConfigs) {
+        const std::string testHostName = config.asHostName();
+        SCOPED_TRACE(testHostName);
+
+        // Don't tempt test bugs due to caching.
+        const char* host_name = testHostName.c_str();
+        dns.addMapping(host_name, ns_type::ns_t_a, ADDR4);
+        dns.addMapping(host_name, ns_type::ns_t_aaaa, ADDR6);
+
+        if (config.withWorkingTLS) ASSERT_TRUE(tls.startServer());
+
+        if (config.mode == OFF) {
+            ASSERT_TRUE(SetResolversForNetwork(
+                    servers, mDefaultSearchDomains,  mDefaultParams_Binder));
+        } else if (config.mode == OPPORTUNISTIC) {
+            ASSERT_TRUE(SetResolversWithTls(
+                    servers, mDefaultSearchDomains, mDefaultParams_Binder, "", {}));
+            // Wait for validation to complete.
+            if (config.withWorkingTLS) EXPECT_TRUE(tls.waitForQueries(1, 5000));
+        } else if (config.mode == STRICT) {
+            // We use the existence of fingerprints to trigger strict mode,
+            // rather than hostname validation.
+            const auto& fingerprint =
+                    (config.withWorkingTLS) ? tls.fingerprint() : NOOP_FINGERPRINT;
+            ASSERT_TRUE(SetResolversWithTls(
+                    servers, mDefaultSearchDomains, mDefaultParams_Binder, "",
+                    { base64Encode(fingerprint) }));
+            // Wait for validation to complete.
+            if (config.withWorkingTLS) EXPECT_TRUE(tls.waitForQueries(1, 5000));
+        } else {
+            FAIL() << "Unsupported Private DNS mode: " << config.mode;
+        }
+
+        const int tlsQueriesBefore = tls.queries();
+
+        const hostent* h_result = nullptr;
+        addrinfo* ai_result = nullptr;
+
+        if (config.method == GETHOSTBYNAME) {
+            ASSERT_EQ(0, setNetworkForResolv(BYPASS_NETID));
+            h_result = gethostbyname(host_name);
+
+            EXPECT_EQ(1U, GetNumQueriesForType(dns, ns_type::ns_t_a, host_name));
+            ASSERT_FALSE(h_result == nullptr);
+            ASSERT_EQ(4, h_result->h_length);
+            ASSERT_FALSE(h_result->h_addr_list[0] == nullptr);
+            EXPECT_EQ(ADDR4, ToString(h_result));
+            EXPECT_TRUE(h_result->h_addr_list[1] == nullptr);
+        } else if (config.method == GETADDRINFO) {
+            ASSERT_EQ(0, setNetworkForResolv(BYPASS_NETID));
+            EXPECT_EQ(0, getaddrinfo(host_name, nullptr, nullptr, &ai_result));
+
+            EXPECT_LE(1U, GetNumQueries(dns, host_name));
+            // Could be A or AAAA
+            const std::string result_str = ToString(ai_result);
+            EXPECT_TRUE(result_str == ADDR4 || result_str == ADDR6)
+                << ", result_str='" << result_str << "'";
+        } else if (config.method == GETADDRINFOFORNET) {
+            EXPECT_EQ(0, android_getaddrinfofornet(
+                    host_name, nullptr, nullptr, BYPASS_NETID, MARK_UNSET, &ai_result));
+
+            EXPECT_LE(1U, GetNumQueries(dns, host_name));
+            // Could be A or AAAA
+            const std::string result_str = ToString(ai_result);
+            EXPECT_TRUE(result_str == ADDR4 || result_str == ADDR6)
+                << ", result_str='" << result_str << "'";
+        } else {
+            FAIL() << "Unsupported query method: " << config.method;
+        }
+
+        const int tlsQueriesAfter = tls.queries();
+        EXPECT_EQ(0, tlsQueriesAfter - tlsQueriesBefore);
+
+        // TODO: Use ScopedAddrinfo or similar once it is available in a common header file.
+        if (ai_result != nullptr) freeaddrinfo(ai_result);
+
+        // Clear per-process resolv netid.
+        ASSERT_EQ(0, setNetworkForResolv(NETID_UNSET));
+        tls.stopServer();
+        dns.clearQueries();
+    }
+
+    dns.stopServer();
+}
+
+TEST_F(ResolverTest, StrictMode_NoTlsServers) {
+    const std::vector<uint8_t> NOOP_FINGERPRINT(test::SHA256_SIZE, 0U);
+    const char cleartext_addr[] = "127.0.0.53";
+    const char cleartext_port[] = "53";
+    const std::vector<std::string> servers = { cleartext_addr };
+
+    test::DNSResponder dns(cleartext_addr, cleartext_port, 250, ns_rcode::ns_r_servfail, 1.0);
+    const char* host_name = "strictmode.notlsips.example.com.";
+    dns.addMapping(host_name, ns_type::ns_t_a, "1.2.3.4");
+    dns.addMapping(host_name, ns_type::ns_t_aaaa, "::1.2.3.4");
+    ASSERT_TRUE(dns.startServer());
+
+    ASSERT_TRUE(SetResolversWithTls(
+            servers, mDefaultSearchDomains, mDefaultParams_Binder,
+            {}, "", { base64Encode(NOOP_FINGERPRINT) }));
+
+    addrinfo* ai_result = nullptr;
+    EXPECT_NE(0, getaddrinfo(host_name, nullptr, nullptr, &ai_result));
+    EXPECT_EQ(0U, GetNumQueries(dns, host_name));
+}