• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 // Copyright 2022 The Chromium Authors
2 // Use of this source code is governed by a BSD-style license that can be
3 // found in the LICENSE file.
4 
5 #include "net/dns/public/dns_over_https_config.h"
6 
7 #include <iterator>
8 #include <string>
9 #include <vector>
10 
11 #include "base/json/json_reader.h"
12 #include "base/json/json_writer.h"
13 #include "base/ranges/algorithm.h"
14 #include "base/strings/string_split.h"
15 #include "base/strings/string_util.h"
16 #include "base/values.h"
17 #include "net/dns/public/dns_over_https_server_config.h"
18 #include "net/dns/public/util.h"
19 #include "third_party/abseil-cpp/absl/types/optional.h"
20 
21 namespace net {
22 
23 namespace {
24 
SplitGroup(base::StringPiece group)25 std::vector<std::string> SplitGroup(base::StringPiece group) {
26   // Templates in a group are whitespace-separated.
27   return SplitString(group, base::kWhitespaceASCII, base::TRIM_WHITESPACE,
28                      base::SPLIT_WANT_NONEMPTY);
29 }
30 
ParseTemplates(std::vector<std::string> templates)31 std::vector<absl::optional<DnsOverHttpsServerConfig>> ParseTemplates(
32     std::vector<std::string> templates) {
33   std::vector<absl::optional<DnsOverHttpsServerConfig>> parsed;
34   parsed.reserve(templates.size());
35   base::ranges::transform(templates, std::back_inserter(parsed), [](auto& s) {
36     return DnsOverHttpsServerConfig::FromString(std::move(s));
37   });
38   return parsed;
39 }
40 
41 constexpr base::StringPiece kJsonKeyServers("servers");
42 
FromValue(base::Value::Dict value)43 absl::optional<DnsOverHttpsConfig> FromValue(base::Value::Dict value) {
44   base::Value::List* servers_value = value.FindList(kJsonKeyServers);
45   if (!servers_value)
46     return absl::nullopt;
47   std::vector<DnsOverHttpsServerConfig> servers;
48   servers.reserve(servers_value->size());
49   for (base::Value& elt : *servers_value) {
50     base::Value::Dict* dict = elt.GetIfDict();
51     if (!dict)
52       return absl::nullopt;
53     auto parsed = DnsOverHttpsServerConfig::FromValue(std::move(*dict));
54     if (!parsed.has_value())
55       return absl::nullopt;
56     servers.push_back(std::move(*parsed));
57   }
58   return DnsOverHttpsConfig(servers);
59 }
60 
FromJson(base::StringPiece json)61 absl::optional<DnsOverHttpsConfig> FromJson(base::StringPiece json) {
62   absl::optional<base::Value> value = base::JSONReader::Read(json);
63   if (!value || !value->is_dict())
64     return absl::nullopt;
65   return FromValue(std::move(*value).TakeDict());
66 }
67 
68 }  // namespace
69 
70 DnsOverHttpsConfig::DnsOverHttpsConfig() = default;
71 DnsOverHttpsConfig::~DnsOverHttpsConfig() = default;
72 DnsOverHttpsConfig::DnsOverHttpsConfig(const DnsOverHttpsConfig& other) =
73     default;
74 DnsOverHttpsConfig& DnsOverHttpsConfig::operator=(
75     const DnsOverHttpsConfig& other) = default;
76 DnsOverHttpsConfig::DnsOverHttpsConfig(DnsOverHttpsConfig&& other) = default;
77 DnsOverHttpsConfig& DnsOverHttpsConfig::operator=(DnsOverHttpsConfig&& other) =
78     default;
79 
DnsOverHttpsConfig(std::vector<DnsOverHttpsServerConfig> servers)80 DnsOverHttpsConfig::DnsOverHttpsConfig(
81     std::vector<DnsOverHttpsServerConfig> servers)
82     : servers_(std::move(servers)) {}
83 
84 // static
FromTemplates(std::vector<std::string> server_templates)85 absl::optional<DnsOverHttpsConfig> DnsOverHttpsConfig::FromTemplates(
86     std::vector<std::string> server_templates) {
87   // All templates must be valid for the group to be considered valid.
88   std::vector<DnsOverHttpsServerConfig> servers;
89   for (auto& server_config : ParseTemplates(server_templates)) {
90     if (!server_config)
91       return absl::nullopt;
92     servers.push_back(std::move(*server_config));
93   }
94   return DnsOverHttpsConfig(std::move(servers));
95 }
96 
97 // static
FromTemplatesForTesting(std::vector<std::string> server_templates)98 absl::optional<DnsOverHttpsConfig> DnsOverHttpsConfig::FromTemplatesForTesting(
99     std::vector<std::string> server_templates) {
100   return FromTemplates(std::move(server_templates));
101 }
102 
103 // static
FromString(base::StringPiece doh_config)104 absl::optional<DnsOverHttpsConfig> DnsOverHttpsConfig::FromString(
105     base::StringPiece doh_config) {
106   absl::optional<DnsOverHttpsConfig> parsed = FromJson(doh_config);
107   if (parsed && !parsed->servers().empty())
108     return parsed;
109   std::vector<std::string> server_templates = SplitGroup(doh_config);
110   if (server_templates.empty())
111     return absl::nullopt;  // `doh_config` must contain at least one server.
112   return FromTemplates(std::move(server_templates));
113 }
114 
115 // static
FromStringLax(base::StringPiece doh_config)116 DnsOverHttpsConfig DnsOverHttpsConfig::FromStringLax(
117     base::StringPiece doh_config) {
118   if (absl::optional<DnsOverHttpsConfig> parsed = FromJson(doh_config))
119     return *parsed;
120   auto parsed = ParseTemplates(SplitGroup(doh_config));
121   std::vector<DnsOverHttpsServerConfig> servers;
122   for (auto& server_config : parsed) {
123     if (server_config)
124       servers.push_back(std::move(*server_config));
125   }
126   return DnsOverHttpsConfig(std::move(servers));
127 }
128 
operator ==(const DnsOverHttpsConfig & other) const129 bool DnsOverHttpsConfig::operator==(const DnsOverHttpsConfig& other) const {
130   return servers() == other.servers();
131 }
132 
ToString() const133 std::string DnsOverHttpsConfig::ToString() const {
134   if (base::ranges::all_of(servers(), &DnsOverHttpsServerConfig::IsSimple)) {
135     // Return the templates on separate lines.
136     std::vector<base::StringPiece> strings;
137     strings.reserve(servers().size());
138     base::ranges::transform(servers(), std::back_inserter(strings),
139                             &DnsOverHttpsServerConfig::server_template_piece);
140     return base::JoinString(std::move(strings), "\n");
141   }
142   std::string json;
143   CHECK(base::JSONWriter::WriteWithOptions(
144       ToValue(), base::JSONWriter::OPTIONS_PRETTY_PRINT, &json));
145   // Remove the trailing newline from pretty-print output.
146   base::TrimWhitespaceASCII(json, base::TRIM_TRAILING, &json);
147   return json;
148 }
149 
ToValue() const150 base::Value::Dict DnsOverHttpsConfig::ToValue() const {
151   base::Value::List list;
152   list.reserve(servers().size());
153   for (const auto& server : servers()) {
154     list.Append(server.ToValue());
155   }
156   base::Value::Dict dict;
157   dict.Set(kJsonKeyServers, std::move(list));
158   return dict;
159 }
160 
161 }  // namespace net
162