proxy_string_util.cc 8.4 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227
  1. // Copyright 2021 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/proxy_string_util.h"
  5. #include <string>
  6. #include "base/notreached.h"
  7. #include "base/strings/strcat.h"
  8. #include "base/strings/string_piece.h"
  9. #include "base/strings/string_util.h"
  10. #include "net/base/proxy_server.h"
  11. #include "net/base/url_util.h"
  12. #include "net/http/http_util.h"
  13. #include "url/third_party/mozilla/url_parse.h"
  14. namespace net {
  15. namespace {
  16. // Parses the proxy type from a PAC string, to a ProxyServer::Scheme.
  17. // This mapping is case-insensitive. If no type could be matched
  18. // returns SCHEME_INVALID.
  19. ProxyServer::Scheme GetSchemeFromPacTypeInternal(base::StringPiece type) {
  20. if (base::EqualsCaseInsensitiveASCII(type, "proxy"))
  21. return ProxyServer::SCHEME_HTTP;
  22. if (base::EqualsCaseInsensitiveASCII(type, "socks")) {
  23. // Default to v4 for compatibility. This is because the SOCKS4 vs SOCKS5
  24. // notation didn't originally exist, so if a client returns SOCKS they
  25. // really meant SOCKS4.
  26. return ProxyServer::SCHEME_SOCKS4;
  27. }
  28. if (base::EqualsCaseInsensitiveASCII(type, "socks4"))
  29. return ProxyServer::SCHEME_SOCKS4;
  30. if (base::EqualsCaseInsensitiveASCII(type, "socks5"))
  31. return ProxyServer::SCHEME_SOCKS5;
  32. if (base::EqualsCaseInsensitiveASCII(type, "direct"))
  33. return ProxyServer::SCHEME_DIRECT;
  34. if (base::EqualsCaseInsensitiveASCII(type, "https"))
  35. return ProxyServer::SCHEME_HTTPS;
  36. if (base::EqualsCaseInsensitiveASCII(type, "quic"))
  37. return ProxyServer::SCHEME_QUIC;
  38. return ProxyServer::SCHEME_INVALID;
  39. }
  40. ProxyServer FromSchemeHostAndPort(ProxyServer::Scheme scheme,
  41. base::StringPiece host_and_port) {
  42. // Trim leading/trailing space.
  43. host_and_port = HttpUtil::TrimLWS(host_and_port);
  44. if (scheme == ProxyServer::SCHEME_INVALID)
  45. return ProxyServer();
  46. if (scheme == ProxyServer::SCHEME_DIRECT) {
  47. if (!host_and_port.empty())
  48. return ProxyServer(); // Invalid -- DIRECT cannot have a host/port.
  49. return ProxyServer::Direct();
  50. }
  51. url::Component username_component;
  52. url::Component password_component;
  53. url::Component hostname_component;
  54. url::Component port_component;
  55. url::ParseAuthority(host_and_port.data(),
  56. url::Component(0, host_and_port.size()),
  57. &username_component, &password_component,
  58. &hostname_component, &port_component);
  59. if (username_component.is_valid() || password_component.is_valid() ||
  60. !hostname_component.is_nonempty()) {
  61. return ProxyServer();
  62. }
  63. base::StringPiece hostname =
  64. host_and_port.substr(hostname_component.begin, hostname_component.len);
  65. // Reject inputs like "foo:". /url parsing and canonicalization code generally
  66. // allows it and treats it the same as a URL without a specified port, but
  67. // Chrome has traditionally disallowed it in proxy specifications.
  68. if (port_component.is_valid() && !port_component.is_nonempty())
  69. return ProxyServer();
  70. base::StringPiece port =
  71. port_component.is_nonempty()
  72. ? host_and_port.substr(port_component.begin, port_component.len)
  73. : "";
  74. return ProxyServer::FromSchemeHostAndPort(scheme, hostname, port);
  75. }
  76. std::string ConstructHostPortString(base::StringPiece hostname, uint16_t port) {
  77. DCHECK(!hostname.empty());
  78. DCHECK((hostname.front() == '[' && hostname.back() == ']') ||
  79. hostname.find(":") == base::StringPiece::npos);
  80. return base::StrCat({hostname, ":", base::NumberToString(port)});
  81. }
  82. } // namespace
  83. ProxyServer PacResultElementToProxyServer(
  84. base::StringPiece pac_result_element) {
  85. // Trim the leading/trailing whitespace.
  86. pac_result_element = HttpUtil::TrimLWS(pac_result_element);
  87. // Input should match:
  88. // "DIRECT" | ( <type> 1*(LWS) <host-and-port> )
  89. // Start by finding the first space (if any).
  90. size_t space = 0;
  91. for (; space < pac_result_element.size(); space++) {
  92. if (HttpUtil::IsLWS(pac_result_element[space])) {
  93. break;
  94. }
  95. }
  96. // Everything to the left of the space is the scheme.
  97. ProxyServer::Scheme scheme =
  98. GetSchemeFromPacTypeInternal(pac_result_element.substr(0, space));
  99. // And everything to the right of the space is the
  100. // <host>[":" <port>].
  101. return FromSchemeHostAndPort(scheme, pac_result_element.substr(space));
  102. }
  103. std::string ProxyServerToPacResultElement(const ProxyServer& proxy_server) {
  104. switch (proxy_server.scheme()) {
  105. case ProxyServer::SCHEME_DIRECT:
  106. return "DIRECT";
  107. case ProxyServer::SCHEME_HTTP:
  108. return std::string("PROXY ") +
  109. ConstructHostPortString(proxy_server.GetHost(),
  110. proxy_server.GetPort());
  111. case ProxyServer::SCHEME_SOCKS4:
  112. // For compatibility send SOCKS instead of SOCKS4.
  113. return std::string("SOCKS ") +
  114. ConstructHostPortString(proxy_server.GetHost(),
  115. proxy_server.GetPort());
  116. case ProxyServer::SCHEME_SOCKS5:
  117. return std::string("SOCKS5 ") +
  118. ConstructHostPortString(proxy_server.GetHost(),
  119. proxy_server.GetPort());
  120. case ProxyServer::SCHEME_HTTPS:
  121. return std::string("HTTPS ") +
  122. ConstructHostPortString(proxy_server.GetHost(),
  123. proxy_server.GetPort());
  124. case ProxyServer::SCHEME_QUIC:
  125. return std::string("QUIC ") +
  126. ConstructHostPortString(proxy_server.GetHost(),
  127. proxy_server.GetPort());
  128. default:
  129. // Got called with an invalid scheme.
  130. NOTREACHED();
  131. return std::string();
  132. }
  133. }
  134. ProxyServer ProxyUriToProxyServer(base::StringPiece uri,
  135. ProxyServer::Scheme default_scheme) {
  136. // We will default to |default_scheme| if no scheme specifier was given.
  137. ProxyServer::Scheme scheme = default_scheme;
  138. // Trim the leading/trailing whitespace.
  139. uri = HttpUtil::TrimLWS(uri);
  140. // Check for [<scheme> "://"]
  141. size_t colon = uri.find(':');
  142. if (colon != base::StringPiece::npos && uri.size() - colon >= 3 &&
  143. uri[colon + 1] == '/' && uri[colon + 2] == '/') {
  144. scheme = GetSchemeFromUriScheme(uri.substr(0, colon));
  145. uri = uri.substr(colon + 3); // Skip past the "://"
  146. }
  147. // Now parse the <host>[":"<port>].
  148. return FromSchemeHostAndPort(scheme, uri);
  149. }
  150. std::string ProxyServerToProxyUri(const ProxyServer& proxy_server) {
  151. switch (proxy_server.scheme()) {
  152. case ProxyServer::SCHEME_DIRECT:
  153. return "direct://";
  154. case ProxyServer::SCHEME_HTTP:
  155. // Leave off "http://" since it is our default scheme.
  156. return ConstructHostPortString(proxy_server.GetHost(),
  157. proxy_server.GetPort());
  158. case ProxyServer::SCHEME_SOCKS4:
  159. return std::string("socks4://") +
  160. ConstructHostPortString(proxy_server.GetHost(),
  161. proxy_server.GetPort());
  162. case ProxyServer::SCHEME_SOCKS5:
  163. return std::string("socks5://") +
  164. ConstructHostPortString(proxy_server.GetHost(),
  165. proxy_server.GetPort());
  166. case ProxyServer::SCHEME_HTTPS:
  167. return std::string("https://") +
  168. ConstructHostPortString(proxy_server.GetHost(),
  169. proxy_server.GetPort());
  170. case ProxyServer::SCHEME_QUIC:
  171. return std::string("quic://") +
  172. ConstructHostPortString(proxy_server.GetHost(),
  173. proxy_server.GetPort());
  174. default:
  175. // Got called with an invalid scheme.
  176. NOTREACHED();
  177. return std::string();
  178. }
  179. }
  180. ProxyServer::Scheme GetSchemeFromUriScheme(base::StringPiece scheme) {
  181. if (base::EqualsCaseInsensitiveASCII(scheme, "http"))
  182. return ProxyServer::SCHEME_HTTP;
  183. if (base::EqualsCaseInsensitiveASCII(scheme, "socks4"))
  184. return ProxyServer::SCHEME_SOCKS4;
  185. if (base::EqualsCaseInsensitiveASCII(scheme, "socks"))
  186. return ProxyServer::SCHEME_SOCKS5;
  187. if (base::EqualsCaseInsensitiveASCII(scheme, "socks5"))
  188. return ProxyServer::SCHEME_SOCKS5;
  189. if (base::EqualsCaseInsensitiveASCII(scheme, "direct"))
  190. return ProxyServer::SCHEME_DIRECT;
  191. if (base::EqualsCaseInsensitiveASCII(scheme, "https"))
  192. return ProxyServer::SCHEME_HTTPS;
  193. if (base::EqualsCaseInsensitiveASCII(scheme, "quic"))
  194. return ProxyServer::SCHEME_QUIC;
  195. return ProxyServer::SCHEME_INVALID;
  196. }
  197. } // namespace net