test_doh_server.cc 6.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215
  1. // Copyright 2021 The Chromium Authors. All rights reserved.
  2. // Use of this source code is governed by a BSD-style license that can be
  3. // found in the LICENSE file.
  4. #include "net/test/test_doh_server.h"
  5. #include <string.h>
  6. #include <memory>
  7. #include "base/base64url.h"
  8. #include "base/bind.h"
  9. #include "base/check.h"
  10. #include "base/logging.h"
  11. #include "base/memory/scoped_refptr.h"
  12. #include "base/strings/string_number_conversions.h"
  13. #include "base/strings/string_piece.h"
  14. #include "base/synchronization/lock.h"
  15. #include "net/base/io_buffer.h"
  16. #include "net/base/url_util.h"
  17. #include "net/dns/dns_query.h"
  18. #include "net/dns/dns_response.h"
  19. #include "net/dns/dns_test_util.h"
  20. #include "net/dns/dns_util.h"
  21. #include "net/dns/public/dns_protocol.h"
  22. #include "net/http/http_status_code.h"
  23. #include "net/test/embedded_test_server/embedded_test_server.h"
  24. #include "net/test/embedded_test_server/http_request.h"
  25. #include "net/test/embedded_test_server/http_response.h"
  26. #include "url/gurl.h"
  27. namespace net {
  28. namespace {
  29. const char kPath[] = "/dns-query";
  30. std::unique_ptr<test_server::HttpResponse> MakeHttpErrorResponse(
  31. HttpStatusCode status,
  32. base::StringPiece error) {
  33. auto response = std::make_unique<test_server::BasicHttpResponse>();
  34. response->set_code(status);
  35. response->set_content(std::string(error));
  36. response->set_content_type("text/plain;charset=utf-8");
  37. return response;
  38. }
  39. std::unique_ptr<test_server::HttpResponse> MakeHttpResponseFromDns(
  40. const DnsResponse& dns_response) {
  41. if (!dns_response.IsValid()) {
  42. return MakeHttpErrorResponse(HTTP_INTERNAL_SERVER_ERROR,
  43. "error making DNS response");
  44. }
  45. auto response = std::make_unique<test_server::BasicHttpResponse>();
  46. response->set_code(HTTP_OK);
  47. response->set_content(std::string(dns_response.io_buffer()->data(),
  48. dns_response.io_buffer_size()));
  49. response->set_content_type("application/dns-message");
  50. return response;
  51. }
  52. } // namespace
  53. TestDohServer::TestDohServer() {
  54. server_.RegisterRequestHandler(base::BindRepeating(
  55. &TestDohServer::HandleRequest, base::Unretained(this)));
  56. }
  57. TestDohServer::~TestDohServer() = default;
  58. void TestDohServer::SetHostname(base::StringPiece name) {
  59. DCHECK(!server_.Started());
  60. hostname_ = std::string(name);
  61. }
  62. void TestDohServer::SetFailRequests(bool fail_requests) {
  63. base::AutoLock lock(lock_);
  64. fail_requests_ = fail_requests;
  65. }
  66. void TestDohServer::AddAddressRecord(base::StringPiece name,
  67. const IPAddress& address,
  68. base::TimeDelta ttl) {
  69. AddRecord(BuildTestAddressRecord(std::string(name), address, ttl));
  70. }
  71. void TestDohServer::AddRecord(const DnsResourceRecord& record) {
  72. base::AutoLock lock(lock_);
  73. records_.insert(
  74. std::make_pair(std::make_pair(record.name, record.type), record));
  75. }
  76. bool TestDohServer::Start() {
  77. if (!InitializeAndListen()) {
  78. return false;
  79. }
  80. StartAcceptingConnections();
  81. return true;
  82. }
  83. bool TestDohServer::InitializeAndListen() {
  84. if (hostname_) {
  85. EmbeddedTestServer::ServerCertificateConfig cert_config;
  86. cert_config.dns_names = {*hostname_};
  87. server_.SetSSLConfig(cert_config);
  88. } else {
  89. // `CERT_OK` is valid for 127.0.0.1.
  90. server_.SetSSLConfig(EmbeddedTestServer::CERT_OK);
  91. }
  92. return server_.InitializeAndListen();
  93. }
  94. void TestDohServer::StartAcceptingConnections() {
  95. server_.StartAcceptingConnections();
  96. }
  97. bool TestDohServer::ShutdownAndWaitUntilComplete() {
  98. return server_.ShutdownAndWaitUntilComplete();
  99. }
  100. std::string TestDohServer::GetTemplate() {
  101. GURL url =
  102. hostname_ ? server_.GetURL(*hostname_, kPath) : server_.GetURL(kPath);
  103. return url.spec() + "{?dns}";
  104. }
  105. std::string TestDohServer::GetPostOnlyTemplate() {
  106. GURL url =
  107. hostname_ ? server_.GetURL(*hostname_, kPath) : server_.GetURL(kPath);
  108. return url.spec();
  109. }
  110. int TestDohServer::QueriesServed() {
  111. base::AutoLock lock(lock_);
  112. return queries_served_;
  113. }
  114. std::unique_ptr<test_server::HttpResponse> TestDohServer::HandleRequest(
  115. const test_server::HttpRequest& request) {
  116. GURL request_url = request.GetURL();
  117. if (request_url.path_piece() != kPath) {
  118. return nullptr;
  119. }
  120. base::AutoLock lock(lock_);
  121. queries_served_++;
  122. if (fail_requests_) {
  123. return MakeHttpErrorResponse(HTTP_NOT_FOUND, "failed request");
  124. }
  125. // See RFC 8484, Section 4.1.
  126. std::string query;
  127. if (request.method == test_server::METHOD_GET) {
  128. std::string query_b64;
  129. if (!GetValueForKeyInQuery(request_url, "dns", &query_b64) ||
  130. !base::Base64UrlDecode(
  131. query_b64, base::Base64UrlDecodePolicy::IGNORE_PADDING, &query)) {
  132. return MakeHttpErrorResponse(HTTP_BAD_REQUEST,
  133. "could not decode query string");
  134. }
  135. } else if (request.method == test_server::METHOD_POST) {
  136. auto content_type = request.headers.find("content-type");
  137. if (content_type == request.headers.end() ||
  138. content_type->second != "application/dns-message") {
  139. return MakeHttpErrorResponse(HTTP_BAD_REQUEST,
  140. "unsupported content type");
  141. }
  142. query = request.content;
  143. } else {
  144. return MakeHttpErrorResponse(HTTP_BAD_REQUEST, "invalid method");
  145. }
  146. // Parse the DNS query.
  147. auto query_buf = base::MakeRefCounted<IOBufferWithSize>(query.size());
  148. memcpy(query_buf->data(), query.data(), query.size());
  149. DnsQuery dns_query(std::move(query_buf));
  150. if (!dns_query.Parse(query.size())) {
  151. return MakeHttpErrorResponse(HTTP_BAD_REQUEST, "invalid DNS query");
  152. }
  153. absl::optional<std::string> name =
  154. DnsDomainToString(dns_query.qname(), /*require_complete=*/true);
  155. if (!name) {
  156. DnsResponse response(dns_query.id(), /*is_authoritative=*/false,
  157. /*answers=*/{}, /*authority_records=*/{},
  158. /*additional_records=*/{}, dns_query,
  159. dns_protocol::kRcodeFORMERR);
  160. return MakeHttpResponseFromDns(response);
  161. }
  162. auto range = records_.equal_range(std::make_pair(*name, dns_query.qtype()));
  163. std::vector<DnsResourceRecord> answers;
  164. for (auto i = range.first; i != range.second; ++i) {
  165. answers.push_back(i->second);
  166. }
  167. VLOG(1) << "Serving " << answers.size() << " records for " << *name
  168. << ", qtype " << dns_query.qtype();
  169. // Note `answers` may be empty. NOERROR with no answers is how to express
  170. // NODATA, so there is no need handle it specially.
  171. //
  172. // For now, this server does not support configuring additional records. When
  173. // testing more complex HTTPS record cases, this will need to be extended.
  174. //
  175. // TODO(crbug.com/1251204): Add SOA records to test the default TTL.
  176. DnsResponse response(dns_query.id(), /*is_authoritative=*/true,
  177. /*answers=*/answers, /*authority_records=*/{},
  178. /*additional_records=*/{}, dns_query);
  179. return MakeHttpResponseFromDns(response);
  180. }
  181. } // namespace net