scheme_host_port_matcher_rule.cc 7.8 KB

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