1 // Copyright 2010 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/base/host_mapping_rules.h"
6
7 #include <string>
8
9 #include "base/logging.h"
10 #include "base/strings/pattern.h"
11 #include "base/strings/string_number_conversions.h"
12 #include "base/strings/string_split.h"
13 #include "base/strings/string_tokenizer.h"
14 #include "base/strings/string_util.h"
15 #include "net/base/host_port_pair.h"
16 #include "net/base/url_util.h"
17 #include "url/gurl.h"
18 #include "url/third_party/mozilla/url_parse.h"
19 #include "url/url_canon.h"
20
21 namespace net {
22
23 struct HostMappingRules::MapRule {
24 MapRule() = default;
25
26 std::string hostname_pattern;
27 std::string replacement_hostname;
28 int replacement_port = -1;
29 };
30
31 struct HostMappingRules::ExclusionRule {
32 std::string hostname_pattern;
33 };
34
35 HostMappingRules::HostMappingRules() = default;
36
37 HostMappingRules::HostMappingRules(const HostMappingRules& host_mapping_rules) =
38 default;
39
40 HostMappingRules::~HostMappingRules() = default;
41
42 HostMappingRules& HostMappingRules::operator=(
43 const HostMappingRules& host_mapping_rules) = default;
44
RewriteHost(HostPortPair * host_port) const45 bool HostMappingRules::RewriteHost(HostPortPair* host_port) const {
46 // Check if the hostname was remapped.
47 for (const auto& map_rule : map_rules_) {
48 // The rule's hostname_pattern will be something like:
49 // www.foo.com
50 // *.foo.com
51 // www.foo.com:1234
52 // *.foo.com:1234
53 // First, we'll check for a match just on hostname.
54 // If that fails, we'll check for a match with both hostname and port.
55 if (!base::MatchPattern(host_port->host(), map_rule.hostname_pattern)) {
56 std::string host_port_string = host_port->ToString();
57 if (!base::MatchPattern(host_port_string, map_rule.hostname_pattern))
58 continue; // This rule doesn't apply.
59 }
60
61 // Check if the hostname was excluded.
62 for (const auto& exclusion_rule : exclusion_rules_) {
63 if (base::MatchPattern(host_port->host(),
64 exclusion_rule.hostname_pattern))
65 return false;
66 }
67
68 host_port->set_host(map_rule.replacement_hostname);
69 if (map_rule.replacement_port != -1)
70 host_port->set_port(static_cast<uint16_t>(map_rule.replacement_port));
71 return true;
72 }
73
74 return false;
75 }
76
RewriteUrl(GURL & url) const77 HostMappingRules::RewriteResult HostMappingRules::RewriteUrl(GURL& url) const {
78 // Must be a valid and standard URL. Otherwise, Chrome might not know how to
79 // find/replace the contained host or port.
80 DCHECK(url.is_valid());
81 DCHECK(url.IsStandard());
82 DCHECK(url.has_host());
83
84 HostPortPair host_port_pair = HostPortPair::FromURL(url);
85 if (!RewriteHost(&host_port_pair))
86 return RewriteResult::kNoMatchingRule;
87
88 GURL::Replacements replacements;
89 std::string port_str = base::NumberToString(host_port_pair.port());
90 replacements.SetPortStr(port_str);
91 std::string host_str = host_port_pair.HostForURL();
92 replacements.SetHostStr(host_str);
93 GURL new_url = url.ReplaceComponents(replacements);
94
95 if (!new_url.is_valid())
96 return RewriteResult::kInvalidRewrite;
97
98 DCHECK(new_url.IsStandard());
99 DCHECK(new_url.has_host());
100 DCHECK_EQ(url.EffectiveIntPort() == url::PORT_UNSPECIFIED,
101 new_url.EffectiveIntPort() == url::PORT_UNSPECIFIED);
102
103 url = std::move(new_url);
104 return RewriteResult::kRewritten;
105 }
106
AddRuleFromString(std::string_view rule_string)107 bool HostMappingRules::AddRuleFromString(std::string_view rule_string) {
108 std::vector<std::string_view> parts = base::SplitStringPiece(
109 base::TrimWhitespaceASCII(rule_string, base::TRIM_ALL), " ",
110 base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
111
112 // Test for EXCLUSION rule.
113 if (parts.size() == 2 &&
114 base::EqualsCaseInsensitiveASCII(parts[0], "exclude")) {
115 ExclusionRule rule;
116 rule.hostname_pattern = base::ToLowerASCII(parts[1]);
117 exclusion_rules_.push_back(rule);
118 return true;
119 }
120
121 // Test for MAP rule.
122 if (parts.size() == 3 && base::EqualsCaseInsensitiveASCII(parts[0], "map")) {
123 MapRule rule;
124 rule.hostname_pattern = base::ToLowerASCII(parts[1]);
125
126 if (!ParseHostAndPort(parts[2], &rule.replacement_hostname,
127 &rule.replacement_port)) {
128 return false; // Failed parsing the hostname/port.
129 }
130
131 map_rules_.push_back(rule);
132 return true;
133 }
134
135 return false;
136 }
137
SetRulesFromString(std::string_view rules_string)138 void HostMappingRules::SetRulesFromString(std::string_view rules_string) {
139 exclusion_rules_.clear();
140 map_rules_.clear();
141
142 std::vector<std::string_view> rules = base::SplitStringPiece(
143 rules_string, ",", base::TRIM_WHITESPACE, base::SPLIT_WANT_ALL);
144 for (std::string_view rule : rules) {
145 bool ok = AddRuleFromString(rule);
146 LOG_IF(ERROR, !ok) << "Failed parsing rule: " << rule;
147 }
148 }
149
150 } // namespace net
151