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