xref: /aosp_15_r20/external/cronet/net/base/scheme_host_port_matcher_rule.cc (revision 6777b5387eb2ff775bb5750e3f5d96f37fb7352b)
1 // Copyright 2020 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/scheme_host_port_matcher_rule.h"
6 
7 #include "base/strings/pattern.h"
8 #include "base/strings/strcat.h"
9 #include "base/strings/string_util.h"
10 #include "base/strings/stringprintf.h"
11 #include "base/trace_event/memory_usage_estimator.h"
12 #include "net/base/host_port_pair.h"
13 #include "net/base/parse_number.h"
14 #include "net/base/url_util.h"
15 #include "url/url_util.h"
16 
17 namespace net {
18 
19 namespace {
20 
AddBracketsIfIPv6(const IPAddress & ip_address)21 std::string AddBracketsIfIPv6(const IPAddress& ip_address) {
22   std::string ip_host = ip_address.ToString();
23   if (ip_address.IsIPv6())
24     return base::StringPrintf("[%s]", ip_host.c_str());
25   return ip_host;
26 }
27 
28 }  // namespace
29 
30 // static
31 std::unique_ptr<SchemeHostPortMatcherRule>
FromUntrimmedRawString(std::string_view raw_untrimmed)32 SchemeHostPortMatcherRule::FromUntrimmedRawString(
33     std::string_view raw_untrimmed) {
34   std::string_view raw =
35       base::TrimWhitespaceASCII(raw_untrimmed, base::TRIM_ALL);
36 
37   // Extract any scheme-restriction.
38   std::string::size_type scheme_pos = raw.find("://");
39   std::string scheme;
40   if (scheme_pos != std::string::npos) {
41     scheme = std::string(raw.substr(0, scheme_pos));
42     raw = raw.substr(scheme_pos + 3);
43     if (scheme.empty())
44       return nullptr;
45   }
46 
47   if (raw.empty())
48     return nullptr;
49 
50   // If there is a forward slash in the input, it is probably a CIDR style
51   // mask.
52   if (raw.find('/') != std::string::npos) {
53     IPAddress ip_prefix;
54     size_t prefix_length_in_bits;
55 
56     if (!ParseCIDRBlock(raw, &ip_prefix, &prefix_length_in_bits))
57       return nullptr;
58 
59     return std::make_unique<SchemeHostPortMatcherIPBlockRule>(
60         std::string(raw), scheme, ip_prefix, prefix_length_in_bits);
61   }
62 
63   // Check if we have an <ip-address>[:port] input. We need to treat this
64   // separately since the IP literal may not be in a canonical form.
65   std::string host;
66   int port;
67   if (ParseHostAndPort(raw, &host, &port)) {
68     IPAddress ip_address;
69     if (ip_address.AssignFromIPLiteral(host)) {
70       // Instead of -1, 0 is invalid for IPEndPoint.
71       int adjusted_port = port == -1 ? 0 : port;
72       return std::make_unique<SchemeHostPortMatcherIPHostRule>(
73           scheme, IPEndPoint(ip_address, adjusted_port));
74     }
75   }
76 
77   // Otherwise assume we have <hostname-pattern>[:port].
78   std::string::size_type pos_colon = raw.rfind(':');
79   port = -1;
80   if (pos_colon != std::string::npos) {
81     if (!ParseInt32(
82             base::MakeStringPiece(raw.begin() + pos_colon + 1, raw.end()),
83             ParseIntFormat::NON_NEGATIVE, &port) ||
84         port > 0xFFFF) {
85       return nullptr;  // Port was invalid.
86     }
87     raw = raw.substr(0, pos_colon);
88   }
89 
90   // Special-case hostnames that begin with a period.
91   // For example, we remap ".google.com" --> "*.google.com".
92   std::string hostname_pattern;
93   if (raw.starts_with(".")) {
94     hostname_pattern = base::StrCat({"*", raw});
95   } else {
96     hostname_pattern = std::string(raw);
97   }
98 
99   return std::make_unique<SchemeHostPortMatcherHostnamePatternRule>(
100       scheme, hostname_pattern, port);
101 }
102 
IsHostnamePatternRule() const103 bool SchemeHostPortMatcherRule::IsHostnamePatternRule() const {
104   return false;
105 }
106 
107 #if !BUILDFLAG(CRONET_BUILD)
EstimateMemoryUsage() const108 size_t SchemeHostPortMatcherRule::EstimateMemoryUsage() const {
109   return 0;
110 }
111 #endif  // !BUILDFLAG(CRONET_BUILD)
112 
113 SchemeHostPortMatcherHostnamePatternRule::
SchemeHostPortMatcherHostnamePatternRule(const std::string & optional_scheme,const std::string & hostname_pattern,int optional_port)114     SchemeHostPortMatcherHostnamePatternRule(
115         const std::string& optional_scheme,
116         const std::string& hostname_pattern,
117         int optional_port)
118     : optional_scheme_(base::ToLowerASCII(optional_scheme)),
119       hostname_pattern_(base::ToLowerASCII(hostname_pattern)),
120       optional_port_(optional_port) {
121   // |hostname_pattern| shouldn't be an IP address.
122   DCHECK(!url::HostIsIPAddress(hostname_pattern));
123 }
124 
Evaluate(const GURL & url) const125 SchemeHostPortMatcherResult SchemeHostPortMatcherHostnamePatternRule::Evaluate(
126     const GURL& url) const {
127   if (optional_port_ != -1 && url.EffectiveIntPort() != optional_port_) {
128     // Didn't match port expectation.
129     return SchemeHostPortMatcherResult::kNoMatch;
130   }
131 
132   if (!optional_scheme_.empty() && url.scheme() != optional_scheme_) {
133     // Didn't match scheme expectation.
134     return SchemeHostPortMatcherResult::kNoMatch;
135   }
136 
137   // Note it is necessary to lower-case the host, since GURL uses capital
138   // letters for percent-escaped characters.
139   return base::MatchPattern(url.host(), hostname_pattern_)
140              ? SchemeHostPortMatcherResult::kInclude
141              : SchemeHostPortMatcherResult::kNoMatch;
142 }
143 
ToString() const144 std::string SchemeHostPortMatcherHostnamePatternRule::ToString() const {
145   std::string str;
146   if (!optional_scheme_.empty())
147     base::StringAppendF(&str, "%s://", optional_scheme_.c_str());
148   str += hostname_pattern_;
149   if (optional_port_ != -1)
150     base::StringAppendF(&str, ":%d", optional_port_);
151   return str;
152 }
153 
IsHostnamePatternRule() const154 bool SchemeHostPortMatcherHostnamePatternRule::IsHostnamePatternRule() const {
155   return true;
156 }
157 
158 std::unique_ptr<SchemeHostPortMatcherHostnamePatternRule>
GenerateSuffixMatchingRule() const159 SchemeHostPortMatcherHostnamePatternRule::GenerateSuffixMatchingRule() const {
160   if (!hostname_pattern_.starts_with("*")) {
161     return std::make_unique<SchemeHostPortMatcherHostnamePatternRule>(
162         optional_scheme_, "*" + hostname_pattern_, optional_port_);
163   }
164   // return a new SchemeHostPortMatcherHostNamePatternRule with the same data.
165   return std::make_unique<SchemeHostPortMatcherHostnamePatternRule>(
166       optional_scheme_, hostname_pattern_, optional_port_);
167 }
168 
169 #if !BUILDFLAG(CRONET_BUILD)
EstimateMemoryUsage() const170 size_t SchemeHostPortMatcherHostnamePatternRule::EstimateMemoryUsage() const {
171   return base::trace_event::EstimateMemoryUsage(optional_scheme_) +
172          base::trace_event::EstimateMemoryUsage(hostname_pattern_);
173 }
174 #endif  // !BUILDFLAG(CRONET_BUILD)
175 
SchemeHostPortMatcherIPHostRule(const std::string & optional_scheme,const IPEndPoint & ip_end_point)176 SchemeHostPortMatcherIPHostRule::SchemeHostPortMatcherIPHostRule(
177     const std::string& optional_scheme,
178     const IPEndPoint& ip_end_point)
179     : optional_scheme_(base::ToLowerASCII(optional_scheme)),
180       ip_host_(AddBracketsIfIPv6(ip_end_point.address())),
181       optional_port_(ip_end_point.port()) {}
182 
Evaluate(const GURL & url) const183 SchemeHostPortMatcherResult SchemeHostPortMatcherIPHostRule::Evaluate(
184     const GURL& url) const {
185   if (optional_port_ != 0 && url.EffectiveIntPort() != optional_port_) {
186     // Didn't match port expectation.
187     return SchemeHostPortMatcherResult::kNoMatch;
188   }
189 
190   if (!optional_scheme_.empty() && url.scheme() != optional_scheme_) {
191     // Didn't match scheme expectation.
192     return SchemeHostPortMatcherResult::kNoMatch;
193   }
194 
195   // Note it is necessary to lower-case the host, since GURL uses capital
196   // letters for percent-escaped characters.
197   return base::MatchPattern(url.host(), ip_host_)
198              ? SchemeHostPortMatcherResult::kInclude
199              : SchemeHostPortMatcherResult::kNoMatch;
200 }
201 
ToString() const202 std::string SchemeHostPortMatcherIPHostRule::ToString() const {
203   std::string str;
204   if (!optional_scheme_.empty())
205     base::StringAppendF(&str, "%s://", optional_scheme_.c_str());
206   str += ip_host_;
207   if (optional_port_ != 0)
208     base::StringAppendF(&str, ":%d", optional_port_);
209   return str;
210 }
211 
212 #if !BUILDFLAG(CRONET_BUILD)
EstimateMemoryUsage() const213 size_t SchemeHostPortMatcherIPHostRule::EstimateMemoryUsage() const {
214   return base::trace_event::EstimateMemoryUsage(optional_scheme_) +
215          base::trace_event::EstimateMemoryUsage(ip_host_);
216 }
217 #endif  // !BUILDFLAG(CRONET_BUILD)
218 
SchemeHostPortMatcherIPBlockRule(const std::string & description,const std::string & optional_scheme,const IPAddress & ip_prefix,size_t prefix_length_in_bits)219 SchemeHostPortMatcherIPBlockRule::SchemeHostPortMatcherIPBlockRule(
220     const std::string& description,
221     const std::string& optional_scheme,
222     const IPAddress& ip_prefix,
223     size_t prefix_length_in_bits)
224     : description_(description),
225       optional_scheme_(optional_scheme),
226       ip_prefix_(ip_prefix),
227       prefix_length_in_bits_(prefix_length_in_bits) {}
228 
Evaluate(const GURL & url) const229 SchemeHostPortMatcherResult SchemeHostPortMatcherIPBlockRule::Evaluate(
230     const GURL& url) const {
231   if (!url.HostIsIPAddress())
232     return SchemeHostPortMatcherResult::kNoMatch;
233 
234   if (!optional_scheme_.empty() && url.scheme() != optional_scheme_) {
235     // Didn't match scheme expectation.
236     return SchemeHostPortMatcherResult::kNoMatch;
237   }
238 
239   // Parse the input IP literal to a number.
240   IPAddress ip_address;
241   if (!ip_address.AssignFromIPLiteral(url.HostNoBracketsPiece()))
242     return SchemeHostPortMatcherResult::kNoMatch;
243 
244   // Test if it has the expected prefix.
245   return IPAddressMatchesPrefix(ip_address, ip_prefix_, prefix_length_in_bits_)
246              ? SchemeHostPortMatcherResult::kInclude
247              : SchemeHostPortMatcherResult::kNoMatch;
248 }
249 
ToString() const250 std::string SchemeHostPortMatcherIPBlockRule::ToString() const {
251   return description_;
252 }
253 
254 #if !BUILDFLAG(CRONET_BUILD)
EstimateMemoryUsage() const255 size_t SchemeHostPortMatcherIPBlockRule::EstimateMemoryUsage() const {
256   return base::trace_event::EstimateMemoryUsage(description_) +
257          base::trace_event::EstimateMemoryUsage(optional_scheme_) +
258          base::trace_event::EstimateMemoryUsage(ip_prefix_);
259 }
260 #endif  // !BUILDFLAG(CRONET_BUILD)
261 
262 }  // namespace net
263