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));
+}