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/proxy_server.h"
6
7 #include <stdint.h>
8
9 #include <optional>
10 #include <ostream>
11 #include <string>
12 #include <string_view>
13
14 #include "base/check_op.h"
15 #include "base/numerics/safe_conversions.h"
16 #include "base/pickle.h"
17 #include "base/strings/strcat.h"
18 #include "base/strings/string_number_conversions.h"
19 #include "base/strings/string_util.h"
20 #include "net/base/proxy_string_util.h"
21 #include "url/third_party/mozilla/url_parse.h"
22 #include "url/url_canon.h"
23 #include "url/url_canon_stdstring.h"
24
25 namespace net {
26
27 namespace {
28
IsValidSchemeInt(int scheme_int)29 bool IsValidSchemeInt(int scheme_int) {
30 switch (scheme_int) {
31 case ProxyServer::SCHEME_INVALID:
32 case ProxyServer::SCHEME_HTTP:
33 case ProxyServer::SCHEME_SOCKS4:
34 case ProxyServer::SCHEME_SOCKS5:
35 case ProxyServer::SCHEME_HTTPS:
36 case ProxyServer::SCHEME_QUIC:
37 return true;
38 default:
39 return false;
40 }
41 }
42
43 } // namespace
44
ProxyServer(Scheme scheme,const HostPortPair & host_port_pair)45 ProxyServer::ProxyServer(Scheme scheme, const HostPortPair& host_port_pair)
46 : scheme_(scheme), host_port_pair_(host_port_pair) {
47 if (scheme_ == SCHEME_INVALID) {
48 // |host_port_pair| isn't relevant for these special schemes, so none should
49 // have been specified. It is important for this to be consistent since we
50 // do raw field comparisons in the equality and comparison functions.
51 DCHECK(host_port_pair.Equals(HostPortPair()));
52 host_port_pair_ = HostPortPair();
53 }
54 }
55
56 // static
FromSchemeHostAndPort(Scheme scheme,std::string_view host,std::string_view port_str)57 ProxyServer ProxyServer::FromSchemeHostAndPort(Scheme scheme,
58 std::string_view host,
59 std::string_view port_str) {
60 // Create INVALID proxies directly using `ProxyServer()`.
61 DCHECK_NE(scheme, SCHEME_INVALID);
62
63 int port_number =
64 url::ParsePort(port_str.data(), url::Component(0, port_str.size()));
65 if (port_number == url::PORT_UNSPECIFIED)
66 return FromSchemeHostAndPort(scheme, host, std::nullopt);
67 if (port_number == url::PORT_INVALID)
68 return ProxyServer();
69
70 DCHECK(base::IsValueInRangeForNumericType<uint16_t>(port_number));
71
72 return FromSchemeHostAndPort(scheme, host,
73 static_cast<uint16_t>(port_number));
74 }
75
76 // static
FromSchemeHostAndPort(Scheme scheme,std::string_view host,std::optional<uint16_t> port)77 ProxyServer ProxyServer::FromSchemeHostAndPort(Scheme scheme,
78 std::string_view host,
79 std::optional<uint16_t> port) {
80 // Create INVALID proxies directly using `ProxyServer()`.
81 DCHECK_NE(scheme, SCHEME_INVALID);
82
83 // Trim host which may have been pasted with excess whitespace.
84 if (!host.empty()) {
85 host = base::TrimWhitespaceASCII(host, base::TRIM_ALL);
86 }
87
88 // Add brackets to IPv6 literals if missing, as required by url
89 // canonicalization.
90 std::string bracketed_host;
91 if (!host.empty() && host.front() != '[' &&
92 host.find(":") != std::string_view::npos) {
93 bracketed_host = base::StrCat({"[", host, "]"});
94 host = bracketed_host;
95 }
96
97 std::string canonicalized_host;
98 url::StdStringCanonOutput canonicalized_output(&canonicalized_host);
99 url::Component component_output;
100
101 if (!url::CanonicalizeHost(host.data(), url::Component(0, host.size()),
102 &canonicalized_output, &component_output)) {
103 return ProxyServer();
104 }
105 if (component_output.is_empty())
106 return ProxyServer();
107
108 canonicalized_output.Complete();
109
110 // Remove IPv6 literal bracketing, as required by HostPortPair.
111 std::string_view unbracketed_host = canonicalized_host;
112 if (canonicalized_host.front() == '[' && canonicalized_host.back() == ']')
113 unbracketed_host = unbracketed_host.substr(1, unbracketed_host.size() - 2);
114
115 // A uint16_t port is always valid and canonicalized.
116 uint16_t fixed_port = port.value_or(GetDefaultPortForScheme(scheme));
117
118 return ProxyServer(scheme, HostPortPair(unbracketed_host, fixed_port));
119 }
120
121 // static
CreateFromPickle(base::PickleIterator * pickle_iter)122 ProxyServer ProxyServer::CreateFromPickle(base::PickleIterator* pickle_iter) {
123 Scheme scheme = SCHEME_INVALID;
124 int scheme_int;
125 if (pickle_iter->ReadInt(&scheme_int) && IsValidSchemeInt(scheme_int)) {
126 scheme = static_cast<Scheme>(scheme_int);
127 }
128
129 HostPortPair host_port_pair;
130 std::string host_port_pair_string;
131 if (pickle_iter->ReadString(&host_port_pair_string)) {
132 host_port_pair = HostPortPair::FromString(host_port_pair_string);
133 }
134
135 return ProxyServer(scheme, host_port_pair);
136 }
137
Persist(base::Pickle * pickle) const138 void ProxyServer::Persist(base::Pickle* pickle) const {
139 pickle->WriteInt(static_cast<int>(scheme_));
140 pickle->WriteString(host_port_pair_.ToString());
141 }
142
GetHost() const143 std::string ProxyServer::GetHost() const {
144 return host_port_pair().HostForURL();
145 }
146
GetPort() const147 uint16_t ProxyServer::GetPort() const {
148 return host_port_pair().port();
149 }
150
host_port_pair() const151 const HostPortPair& ProxyServer::host_port_pair() const {
152 // Doesn't make sense to call this if the URI scheme doesn't
153 // have concept of a host.
154 DCHECK(is_valid());
155 return host_port_pair_;
156 }
157
158 // static
GetDefaultPortForScheme(Scheme scheme)159 int ProxyServer::GetDefaultPortForScheme(Scheme scheme) {
160 switch (scheme) {
161 case SCHEME_HTTP:
162 return 80;
163 case SCHEME_SOCKS4:
164 case SCHEME_SOCKS5:
165 return 1080;
166 case SCHEME_HTTPS:
167 case SCHEME_QUIC:
168 return 443;
169 case SCHEME_INVALID:
170 break;
171 }
172 return -1;
173 }
174
operator <<(std::ostream & os,const ProxyServer & proxy_server)175 std::ostream& operator<<(std::ostream& os, const ProxyServer& proxy_server) {
176 return os << ProxyServerToPacResultElement(proxy_server);
177 }
178
179 } // namespace net
180