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