xref: /aosp_15_r20/external/cronet/net/base/host_mapping_rules.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
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